Java高并发二
/**
* 面试题(淘宝?)
* 实现一个容器,提供两个方法,add,size
* 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到达5时,线程2给出提示并结束
*/
public class MyContainer2 {
private volatile List<Object> list = new ArrayList<>();
public void add(Object ele) {
list.add(ele);
}
public int size() {
return list.size();
}
public static void main(String[] args) {
MyContainer2 container = new MyContainer2();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
container.add(new Object());
System.out.println("add " + i);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1").start();
new Thread(() -> {
while (true) {
if (container.size() == 5) {
break;
}
}
System.out.println("监测到容器长度为5,线程2立即退出");
}, "t2").start();
}
}
/*
添加 volatile ,使list发生变化时,主动通知其他线程,更新工作空间
上述代码,共有以下几个问题:
1. 不够精确,当container.size == 5 还未执行break时,有可能被其他线程抢占;或者 container.add() 之后,还未打印,就被 t2 判断size为5 直接推出了
2. 损耗性能,t2 线程,一直在走while循环,很浪费性能
*/
/**
* 面试题(淘宝?)
* 实现一个容器,提供两个方法,add,size
* 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到达5时,线程2给出提示并结束
*/
public class MyContainer3 {
private List<Object> list = new ArrayList<>();
public void add(Object ele) {
list.add(ele);
}
public int size() {
return list.size();
}
public static void main(String[] args) {
MyContainer3 container = new MyContainer3();
final Object lock = new Object();
new Thread(() -> {
synchronized (lock) {
System.out.println("t2 启动");
if (container.size() != 5) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("监测到容器长度为5,线程2立即退出");
lock.notify();
}
}, "t2").start();
// 先启动t2线程,让t2线程进入等待状态
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
synchronized (lock) {
for (int i = 0; i < 10; i++) {
container.add(new Object());
System.out.println("add " + i);
// 当长度为5时,通知 t2 进行退出
if (container.size() == 5) {
lock.notify(); // notify 不会释放锁,即便通知t2,t2也获取不到锁
// 可以在wait一下,将锁释放,再让t2通知t1继续执行
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "t1").start();
}
}
/*
使用wait和notify
wait()与notify() 方法的调用必须在同步代码块中
wait会释放锁,notify不会释放锁
锁定对象a,调用a.wait() 方法,当前线程就会进入等待状态,然后释放锁。
当某线程调用 a.notify() / a.notifyAll(), 叫醒在a对象等待的所有线程
*/
/**
* 面试题(淘宝?)
* 实现一个容器,提供两个方法,add,size
* 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到达5时,线程2给出提示并结束
*/
public class MyContainer5 {
private volatile List<Object> list = new ArrayList<>();
public void add(Object ele) {
list.add(ele);
}
public int size() {
return list.size();
}
public static void main(String[] args) {
MyContainer5 container = new MyContainer5();
// Count down 往下数 Latch 门闩
// 门闩不能保证可见性,不是一种同步方式,只是一种线程通信方式,保证不了可见性
// 门闩的等待,不会持有任何锁
CountDownLatch latch = new CountDownLatch(1);
new Thread(() -> {
System.out.println("t2 启动");
if (container.size() != 5) {
try {
latch.await();
// 指定等待时间
//latch.await(5000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("监测到容器长度为5,线程2立即退出");
}, "t2").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
System.out.println("t1 启动");
for (int i = 0; i < 10; i++) {
container.add(new Object());
System.out.println("add " + i);
// 当长度为5时,撤掉一个门闩,此时门闩为0,门会打开,即t2会执行
if (container.size() == 5) {
latch.countDown();
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1").start();
}
}
/*
使用CountDownLatch实现(最简单的方式)
Latch:门闩
使用Latch替代 wait notify来进行通信
好处是,通信简单,同时也可以指定等待时间
使用await和countDown 方法替代 wait 和 notify
CountDownLatch不涉及锁定,当count值为0时,当前线程继续运行
当不涉及同步,只涉及线程通信的时候,用synchronized + wait + notify 就显得太重了
*/