线程间通信(一)

一:线程间的通信

1.1:wait/notify机制的原理
1.wait方法的作用是使当前执行的wait方法的线程等待,并释放锁,直到接到通知或被中断为止
2.拥有相同锁的线程才可以实现wait/notify机制
3.wait和notify方法必须在同步中使用,否则会发生IllegalMonotorStateException异常
4.notify方法是通知等待该锁的其他线程!,所以前提是wait方法和notify方法使用的是同一把锁
5.notify方法执行后,并不会立即释放锁,需要执行完notify所在的同步区域后,当前线程才会释放锁
6.如果发出notify操作时,没有在锁中等待的线程,则该命令被忽略
7.一般都是使用锁对象来调用wait/notify方法
1.2:实现wait/notify机制
  • 例子1
/**
 * 完整实现wait/notify机制
 */
public class Demo {

    public static void main(String[] args) {

        Object lock = new Object();

        new Thread(()->{
           try {
               synchronized (lock){
                   System.out.println(Thread.currentThread().getName()+" :wait start");
                   lock.wait();
                   System.out.println(Thread.currentThread().getName()+" :wait end");
               }
           }catch (InterruptedException e){
               e.printStackTrace();
           }
        },"兔子").start();

        try {
            //让主线程休眠2s, 让兔子线程充分的先执行
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() ->{
            synchronized (lock){
                System.out.println(Thread.currentThread().getName()+" :notify start");
                lock.notify();
                System.out.println(Thread.currentThread().getName()+" :notify end");
            }
        },"乌龟").start();
    }
}
  • 控制台
兔子 :wait start
乌龟 :notify start
乌龟 :notify end
兔子 :wait end
分析:
1.为什么还加了个Thread.sleep(2000);
目的就是让兔子线程先执行,为什么要让兔子线程先执行呢?,你想想如果不加Thread.sleep(2000),
兔子线程和乌龟线程几乎同时启动线程,有几率cpu的执行权被乌龟线程抢走了,那么这时执行如下:
乌龟 :notify start
乌龟 :notify end
兔子 :wait start
注:兔子线程还在等待!

2.注意notify执行后,并没有立即释放锁,需要执行完notify所在的同步区域后,当前线程才会释放锁
所以等到打印出: 乌龟 :notify end,才会去执行 兔子 :wait start

注意,wait/notify方法必须使用同一把锁!
  • 例子2
public class Demo {

    private static List<String> list = new ArrayList<>();

    private static Object lock = new Object();

    public static void main(String[] args) {

        new Thread(()->{
           try {
               synchronized (lock){
                 if(list.size()!=5){
                     System.out.println("wait start...");
                     lock.wait();
                     System.out.println("wait end...");
                 }
               }
           }catch (InterruptedException e){
               e.printStackTrace();
           }
        }).start();

        try {
            //让主线程休眠2s, 让上面的线程先处于阻塞状态!
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() ->{
           try {
               synchronized (lock){
                   for (int i = 1; i <= 10; i++) {
                       list.add("hello world");
                       if(list.size() == 5){
                           lock.notify();
                           System.out.println("已经发出通知");
                       }
                       System.out.println("添加了"+i+"个元素");
                       Thread.sleep(1000);
                   }
               }
           }catch (Exception e){
               e.printStackTrace();
           }
        }).start();
    }
}
  • 控制台
wait start...
添加了1个元素
添加了2个元素
添加了3个元素
添加了4个元素
已经发出通知
添加了5个元素
添加了6个元素
添加了7个元素
添加了8个元素
添加了9个元素
添加了10个元素
wait end...

注:通过控制台可以看出,notify方法执行完后并没有马上释放锁,而是执行完所在的同步区域的代码后,才释放的锁。

  • 例子3:业务代码要尽量放在Service业务类中进行处理,这样更加标准

1、业务类

//业务类
public class Service {

    private Object lock = new Object();

    private List<String> list = new ArrayList<>();

    public void waitMethod(){
       try {
           synchronized (lock){
               if(list.size() != 5){
                   System.out.println("waitMethod start...");
                   lock.wait(); //释放锁
                   System.out.println("waitMethod end...");
               }
           }
       }catch (InterruptedException e){
           e.printStackTrace();
       }
    }

    public void notifyMethod(){
       try {
           synchronized (lock){
               for (int i = 1; i <= 10; i++) {
                   list.add("hello world");
                   if(list.size() == 5){
                       lock.notify();
                       System.out.println("仅仅是发出通知,并没有立即释放锁,需要待notify所在的同步区域执行完后,才释放锁.");
                   }
                   System.out.println("添加了"+i+"个元素");
                   Thread.sleep(1000);//不释放锁
               }
           }
       }catch (InterruptedException e){
           e.printStackTrace();
       }
    }
    
}

2、线程类

public class ThreadA extends Thread{

    private Service service;

    public ThreadA(Service service){
        this.service = service;
    }

    @Override
    public void run() {
        service.waitMethod();
    }
}
public class ThreadB extends Thread{

    private Service service;

    public ThreadB(Service service){
        this.service = service;
    }

    @Override
    public void run() {
        service.notifyMethod();
    }
}

3、测试类

/**
 * 完整实现wait/notify机制
 */
public class Demo {

    public static void main(String[] args) {

        Service service = new Service();
        ThreadA threadA = new ThreadA(service);
        threadA.start();

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        ThreadB threadB = new ThreadB(service);
        threadB.start();
    }
}
  • 控制台
waitMethod start...
添加了1个元素
添加了2个元素
添加了3个元素
添加了4个元素
仅仅是发出通知,并没有立即释放锁,需要待notify所在的同步区域执行完后,才释放锁.
添加了5个元素
添加了6个元素
添加了7个元素
添加了8个元素
添加了9个元素
添加了10个元素
waitMethod end...
  • 总结
创建线程的时候,将业务对象作为构造参数传递进来,执行业务类中的方法即可。

注:来自虽然,但是菜的cxy

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值