多线程编程:一个指令重排序引发的chaos

先贴出正确的代码:

 package com.xiaobai.thread.main;

 import lombok.extern.slf4j.Slf4j;

 @Slf4j
public class ThreadMain { private volatile static int ticket; private static class RunningTask implements Runnable{ public RunningTask(boolean proOrSell) {
this.proOrSell = proOrSell;
} private boolean proOrSell = true;//如果为静态,会被实例共享 public void run() {
while (true) {//只同步需要同步的代码,while(true)不能放在同步里面!!否则除非线程自己wait(),永远不会轮到其他线程!!
synchronized (RunningTask.class) {
proOrSell();
}
}
} private void proOrSell() {
if(proOrSell) {
ticket++;
log.info("Tickets/Pro:" + ticket);
RunningTask.class.notifyAll();
if(ticket >= 1000) {
try {
System.out.println("--------------------------------------------------------------------------------------------------------------------------------------->Pro Wait!!!!!");
RunningTask.class.wait();//持有锁的本线程等待
} catch (InterruptedException e) { }
}
}else {
System.out.println(Thread.currentThread().getName() + " take control!");
if(ticket <= 100) {
try {
System.out.println(Thread.currentThread().getName() + " <=100!");
System.out.println("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!");
RunningTask.class.wait();
} catch (InterruptedException e) { }
}else {
ticket--;
log.info("Tickets------->Sell:" + ticket);
System.out.println(Thread.currentThread().getName() + " decrease tickets!");
if(ticket <= 500) {
RunningTask.class.notifyAll();
}
}
}
}
} private static void proOrSell() {
Runnable task1 = new RunningTask(true);
Runnable task2 = new RunningTask(false);
Thread thread1 = new Thread(task1);
Thread thread2 = new Thread(task2);
Thread thread3 = new Thread(task2);
Thread thread4 = new Thread(task2);
thread1.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) { }
thread2.start();
thread3.start();
thread4.start();
} public static void main(String[] args) {
proOrSell();
while (ticket <= 0) {
System.out.println("------------------------------------------------------->Ticket System Broken!!!!!!");
System.exit(0);
}
} }

代码简单说明:

1.多线程生产者-消费者程序,一个生产者,3个消费者

2.同步共享变量操作代码,同一把锁(内部嵌套类的字节码对象)

3.注意while(true)要在同步代码外面,否则没有意义!

4.改造的等待-唤醒机制,控制票数在一个下限(100)和一个上限(1000)

正确代码运行结果摘录:

1)

Thread-1 take control!
2018-12-09 21:14:11.357 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:105
Thread-1 decrease tickets!
Thread-1 take control!
2018-12-09 21:14:11.357 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:104
Thread-1 decrease tickets!
Thread-1 take control!
2018-12-09 21:14:11.357 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:103
Thread-1 decrease tickets!
Thread-1 take control!
2018-12-09 21:14:11.357 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:102
Thread-1 decrease tickets!
Thread-1 take control!
2018-12-09 21:14:11.357 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:101
Thread-1 decrease tickets!
Thread-1 take control!
2018-12-09 21:14:11.357 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:100
Thread-1 decrease tickets!
Thread-1 take control!
Thread-1 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
Thread-3 take control!
Thread-3 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
2018-12-09 21:14:11.357 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:101
2018-12-09 21:14:11.357 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:102
2018-12-09 21:14:11.357 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:103
2018-12-09 21:14:11.357 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:104

2)
Thread-2 take control!
2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:107
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:106
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:105
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:104
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:103
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:102
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:101
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:100
Thread-2 decrease tickets!
Thread-2 take control!
Thread-2 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
Thread-3 take control!
Thread-3 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
Thread-1 take control!
Thread-1 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
2018-12-09 21:14:11.376 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:101
2018-12-09 21:14:11.376 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:102
2018-12-09 21:14:11.376 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:103
2018-12-09 21:14:11.376 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:104
2018-12-09 21:14:11.376 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:105

3)
Thread-2 take control!
2018-12-09 21:14:11.395 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:104
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.395 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:103
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.395 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:102
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.395 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:101
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.395 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:100
Thread-2 decrease tickets!
Thread-2 take control!
Thread-2 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
Thread-3 take control!
Thread-3 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
Thread-1 take control!
Thread-1 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
2018-12-09 21:14:11.395 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:101
2018-12-09 21:14:11.395 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:102
2018-12-09 21:14:11.395 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:103
2018-12-09 21:14:11.395 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:104

说明:当票数达到下限100时,三个消费者先后侦测到并在同一个监视器上阻塞等待,由生产者继续屯票,并唤醒消费者继续消费。生产者侦测票数达到1000时暂停生产,阻塞等待。

错误的程序:将else中嵌套的else中的内容拿到外面,去掉嵌套else.也就是说,无论上面的if条件是否满足,都执行现在在嵌套else中的全部内容。

错误程序出现的运行现象:

Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:113
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:112
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:111
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:110
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:109
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:108
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:107
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:106
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:105
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:104
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:103
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:102
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:101
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:100
Thread-3 decrease tickets!
Thread-3 take control!
Thread-3 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
2018-12-09 20:53:18.891 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:99
Thread-2 decrease tickets!
Thread-2 take control!
Thread-2 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
2018-12-09 20:53:18.891 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:98
Thread-1 decrease tickets!
Thread-1 take control!
Thread-1 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
2018-12-09 20:53:18.891 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:97
Thread-2 decrease tickets!
Thread-2 take control!
Thread-2 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -

Tickets------->Sell:96
Thread-3 decrease tickets!
Thread-3 take control!
Thread-3 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:97
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:98
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:99
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:100
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:101
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:102
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:103
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:104
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:105
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:106
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:107
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:108
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:109
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:110
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:111
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:112
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:113
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:114
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:115
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -

Tickets/Pro:116

分析:通过不同程序段打印和线程名打印可以清晰看到,消费者程序段中发生了指令重排序。if段后面的程序被挪到最前面执行,造成<=100也消费的情况。程序段中一致的线程名和上下文没有不同线程名交叉出现的情况说明同步锁正确且生效,是线程内顺序程序段的指令重排序造成了混乱。将if下面的程序段放在嵌套的else中后,程序执行正常,不再出现<=100消费的情况。生产者这边也没有溢出1000的情况发生。

总结:多线程程序,在注意共享变量操作需要同步需要同步的代码的同时,还需要注意线程内指令重排序造成多线程环境出现混乱的情况,这种情况同样会造成类似非线程安全的情况,但并非线程安全问题,而是同一个线程中的顺序混乱被带到多线程操作中,造成了混乱和不安全。

上一篇:loadrunner请求json数据参数化问题


下一篇:五子棋游戏SRS文档