请听题
共11颗糖,有两个小孩,一个小孩一次只能抓三颗糖,另一个小孩一次只能抓四颗糖
如果剩余糖果不够当前小孩拿的数量,当前小孩不在抓糖,请用多线程模拟上面的描述
废话不多说先上码
public class TestFunction {
public static volatile AtomicInteger num = new AtomicInteger(11);
//孩子线程
static class Child extends Thread {
private int getR = 0;
private int r;
CountDownLatch countDownLatch;
public Child(int r, CountDownLatch countDownLatch) {
this.r = r;
this.countDownLatch = countDownLatch;
}
@Override
public String toString() {
return "孩子," + "一次拿" + r +
"颗糖,共拿了" + getR +
"颗糖";
}
@Override
public void run() {
try {
//等待
countDownLatch.await();
for (; ; ) {
//同步代码快保证原子性,保证判断和计算一致
synchronized (num) {
if (num.get() >= r) {
num.addAndGet(-r);
getR += r;
} else {
return;
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(2);
Thread thread1 = new Child(3, countDownLatch);
thread1.start();
countDownLatch.countDown();
Thread thread2 = new Child(4, countDownLatch);
thread2.start();
countDownLatch.countDown();
//等待两个线程执行完,回到当前线程
thread1.join();
thread2.join();
System.out.println("剩余糖:" + num);
System.out.println(thread1);
System.out.println(thread2);
}
}
问题解析
-
可见性
问题可知糖一共有11颗,对于两个孩子都是可见,一个孩子拿完,另外一个孩子是能立马知道的,这就叫可见性。
在代码中普通的参数对于不同线程,在某一个时刻看到值是不一样的。当线程A看到对象是1,其实只是在哪一个时刻看到的缓存区是1,主内存可能已经修改成2。为了避免这种情况。
Java中的volatile 关键字就是为了解决这个问题,当前线程的缓存区的值发生变动时,会第一时间刷入主内存,同时会通知其他使用线程。
-
锁机制
java中的锁机制JVM来保证数据同步的,而Lock则是在硬件层面,依赖特殊的CPU指令实现数据同步的。
按照题意,在某一个时刻只能有一个线程在进行拿取糖的操作。所需要的对拿取糖的操作进行加锁,保证在任何时刻都只有一个孩子可以拿取糖。这里竞争不不激烈,所以直接使用
synchronized对代码块进行加锁,同时保证数量判断和糖的拿取的原子性。 -
线程等待
因为要模拟糖的拿取,所以我们要保证两个线程同时执行,同时开始拿取糖。所以这个时候CountDownLatch 出现了,在线程中await(),当CountDownLatch 的计数减到0时,所有线程会在继续从await()的下行代码开始执行。所以可以完美的模拟同时拿取的操作。 -
线程同步
当执行所有线程,我为了看到结果使用Thread的join()方法,保证两个孩子线程执行完后,还回到当前主线程。
join()表示将当前线程挂起,等待join的线程执行完,才会回到主线程。