实现一个容器,提供两个方法 add, size
写俩个线程,线程1添加10个元素道容器中,线程2实现监控元素得个数,当个数到5的时候,线程2给出提示并结束。
- 因为涉及到两个线程访问同一个变量,所以应该是变量共享的,会使用到volatile或者synchronized,(只涉及到一个线程去修改资源,另外一个线程去访问,所以这里同步方法可能不大适合)
- 需要另外一个线程去轮循容器是否已经有5个元素来,这样会很大的浪费资源
- wait和notify功能,两个线程共同锁定一个对象,通过这个对象,使用wait和notify 使俩个线程来启东和释放锁,这里需要抢到的一点 wait 会释放锁。
public class Container {
volatile List lists = new ArrayList();
public void add(Object o){
lists.add(o);
}
public int size(){
return lists.size();
}
public static void main(String[] args){
Container container = new Container();
final Object o = new Object();
new Thread(() ->{
synchronized (o){
System.out.println("t2 启动");// t2 监控元素
if (container.size()!=5){
try {
o.wait();// 释放
System.out.println("5个添加够了 t1");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t2 结束");
}
}, "t2").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()-> {
System.out.println("t1 启动");// t1 添加元素
synchronized (o){
for (int i = 0; i < 10; i++) {
container.add(new Object());
System.out.println("add: " + i);
if (container.size() == 5) {
o.notify();
}
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1 结束");
}
}, "t1").start();
}
}
执行上面的代码会有个问题:t2 线程并没有在t1线程增加到第5个元素得时候启动,虽然在上面的代码中t2线程有去notify t1线程,但是线程并没有打印出来我们想要的结果,而是在t1结束之后才打印出来。
之所以出现这个问题是因为 notify 并不会去释放锁,所以即使在t1线程中notify t2线程,但是t2线程并没有重新获得锁(在程序刚启动的时候t2 进入 wait 状态,释放了锁)。
解决的方法是,在t1程序 notify t2 得时候需要释放锁,使用的方法就是 让t1也进入wait状态,然后t2程序获得锁,执行结束之后,执行结束之后会释放锁,再notify t1线程运行。
notify之后,t1必须释放锁,t2退出后,也必须notify,通知t1继续执行 * 整个通信过程比较繁琐 * * 使用Latch(门闩)替代wait notify来进行通知 * 好处是通信方式简单,同时也可以指定等待时间 * 使用await和countdown方法替代wait和notify * CountDownLatch不涉及锁定,当count的值为零时当前线程继续运行 * 当不涉及同步,只是涉及线程通信的时候,用synchronized + wait/notify就显得太重了 * 这时应该考虑countdownlatch/cyclicbarrier/semaphore
涉及到的方法:CountDownLatch(1),CountDown往下数,当1变为0的时候门闩就开了,latch.countDown()调用一次数就往下-1;latch.await(),门闩的等待是不需要锁定任何对象的;
public class Container {
volatile List lists = new ArrayList();
public void add(Object o){ lists.add(o); }
public int size(){ return lists.size(); }
public static void main(String[] args){
Container container = new Container();
final Object o = new Object();
CountDownLatch latch = new CountDownLatch(1);
new Thread(() ->{
System.out.println("t2 启动");
if (container.size()!=5){
try {
latch.await();
// 可以指定等待时间
//latch.await(5000, TimeUnit.MILLISECONDS);
System.out.println("5个添加够了 t1");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t2 结束");
}, "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);
if (container.size() == 5) {
// 执行 -1 后count 为0,打开门栓,t2 执行。
latch.countDown();
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t1 结束");
}, "t1").start();
}
}
输出:
t2 启动
t1 启动
add: 0
add: 1
add: 2
add: 3
add: 4
5个添加够了 t1
t2 结束
add: 5
add: 6
add: 7
add: 8
add: 9
t1 结束