【Java并发编程】synchronized(二):通信 --一个容器问题

要求:

  1. 实现一个容器,提供两个方法,add,size
  2. 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束

1.方案一:volatile

public class MyContainer<T> {
    
    
	// 复用 ArrayList
	// 注:这里使用 volatile 保证两个线程都能感知到修改
    volatile List<T> list = new ArrayList<>();
	
	// 使用 List#add
    void add(T t){
    
    
       list.add(t);
    }

	// 使用 List#size
    int size(){
    
    
        return list.size();
    }

    public static void main(String[] args) {
    
    
        MyContainer<Integer> myContainer = new MyContainer<>();
        
        // thead1 负责向容器add
        new Thread(()->{
    
    
            for(int i=1;i<=10;i++){
    
    
                System.out.println(i);
                myContainer.add(i);
                try {
    
    
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }).start();
        
        // thread2 监听容器变化
        new Thread(()->{
    
    
            while (true){
    
    
                if(myContainer.size() == 5){
    
    
                    System.out.println("!!!");
                    break;
                }
            }

        }).start();

    }
}

给 lists 添加 volatile之后,t2 能够接到通知,但是,t2 线程的死循环很浪费cpu,如果不用死循环,该怎么做呢?

2.方案二:wait/notify

这里使用 wait 和 notify 做到,wait 会释放锁,而 notify 不会释放锁。

注意:

  1. 运用这种方法,必须要保证 t2 先执行,也就是首先让 t2 监听才可以
  2. notify 之后,t1 必须释放锁(wait),t2 退出后,也必须 notify,通知 t1 继续执行。整个通信过程比较繁琐
public class MyContainer02<T> {
    
    

   volatile List<T> list = new ArrayList<>();
   void add(T t){
    
    
       this.list.add(t);
   }
   int size(){
    
    
       return list.size();
   }

    public static void main(String[] args) {
    
    
        MyContainer02<Integer> container02 = new MyContainer02<>();
        // thread2(监听线程)先启动
        new Thread(()->{
    
    
            try {
    
    
                synchronized (container02){
    
    
                    if (container02.size() != 5){
    
    
                    	// 释放锁,休眠
                        container02.wait();
                    }

                    System.out.println("!!!!!!");
                    container02.notify();
                }
            }catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }).start();
		
		// thread1(add线程)再启动
        new Thread(()->{
    
    
            synchronized (container02){
    
    
            	// 向容器 add
                for(int i=0;i<10;i++){
    
    
                    container02.list.add(i);
                    System.out.println(i);
                    try {
    
    
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
					// 待放了 5 个时,
                    if(i == 4){
    
    
                    	// notify 监听线程
                        container02.notify();
                        try {
    
    
                        	// 自己进入休眠
                            container02.wait();
                        } catch (InterruptedException e) {
    
    
                            e.printStackTrace();
                        }
                    }
                }
            }
        }).start();

    }

}

3.方案三:countDownLatch

使用 Latch(门闩)替代 wait notify 来进行通知,好处是通信方式简单,同时也可以指定等待时间。使用 await 和 countdown 方法替代 wait 和 notify,CountDownLatch 不涉及锁定,当 count 的值为零时当前线程继续运行。

PS:当不涉及同步,只是涉及线程通信的时候,用 synchronized + wait/notify 就显得太重了;这时应该考虑countdownlatch/cyclicbarrier/semaphore

public class MyContainer03<T> {
    
    

    volatile List<T> list = new ArrayList<>();
    void add(T t){
    
    
        this.list.add(t);
    }
    int size(){
    
    
        return list.size();
    }

    public static void main(String[] args) {
    
    

        MyContainer03<Integer> container03 = new MyContainer03<>();
        // 初始化 CountDownLatch 为 1
        CountDownLatch countDownLatch = new CountDownLatch(1);

		// thread1(监听线程)
        new Thread(()->{
    
    
            try {
    
    
            	// await() 阻塞等待 countdownLatch 减为 0
                countDownLatch.await();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("!!!");
        }).start();
		
		// thread2(add线程)
        new Thread(()->{
    
    
            for(int i=0;i<10;i++){
    
    
                container03.add(i);
                System.out.println(i);
                try {
    
    
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                // 当放了 5 个时
                if(container03.size() == 5){
    
    
                	// 将 countDownLatch--(为0,此时t1被唤醒)
                    countDownLatch.countDown();
                    try {
    
    
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_43935927/article/details/114076365