方案一:双重检查锁(有界的生产消费)
public class Hello {
int exist = 100;
int empty = 0;
/**
* sheng chan zhe
*/
public void produce() {
while (true) {
if (empty > 0) {
synchronized (this) {
if (empty > 0) {
exist++;
empty--;
break;
}
}
}
}
}
/**
* xiao fei zhe
*/
public void consume() {
while (true) {
if (exist > 0) {
synchronized (this) {
if (exist > 0) {
empty++;
exist--;
break;
}
}
}
}
}
public static void main(String[] args) {
Hello hello = new Hello();
CountDownLatch countDownLatch = new CountDownLatch(2);
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 9050; i++) {
hello.produce();
}
countDownLatch.countDown();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 9100; i++) {
hello.consume();
}
countDownLatch.countDown();
}
}).start();
try {
countDownLatch.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(hello.exist);
System.out.println(hello.empty);
}
}
这种方案我们会发现,出现了死循环。
原因:在外层 if 语句永远拿到自己缓冲行中的数据,当出现 0 之后死循环。
解决:1.用 volatile 修饰,这样数据对其他线程可见。2.将 int 类型改用 AtomicInteger , 因为它内部的 value 是用了 volatile 修饰。3.在while下增加一句 print ,因为 print 语句有个上锁操作,可以更新变量的副本。
(这个问题一群人讨论了半天,猜测是原子操作问题还是重排序问题,结果是可见性问题,哈哈哈)
方案二:AtomicInteger(只适用于无界的生产消费)
public class Hello {
AtomicInteger exist = new AtomicInteger(100);
AtomicInteger empty = new AtomicInteger(0);
/**
* sheng chan zhe
*/
public void produce() {
exist.addAndGet(1);
empty.addAndGet(-1);
}
/**
* xiao fei zhe
*/
public void consume() {
empty.addAndGet(1);
exist.addAndGet(-1);
}
public static void main(String[] args) {
Hello hello = new Hello();
CountDownLatch countDownLatch = new CountDownLatch(2);
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 9050; i++) {
hello.produce();
}
countDownLatch.countDown();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 9100; i++) {
hello.consume();
}
countDownLatch.countDown();
}
}).start();
try {
countDownLatch.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(hello.exist);
System.out.println(hello.empty);
}
}
这个代码就简单多了,不直接用 int++ 是为了避免非原子操作带来的线程不安全现象。
但是这个只能用于无界的生产与消费,多数情况下对于消费者不适用,所以在消费者处可以增加判断。