线程通讯~

线程间通讯概述

线程间的通讯技术就是通过等待和唤醒机制,来实现多个线程协同操作完成某一项任务,例如经典的生产者和消费者案例。

什么是线程通讯?

在多线程程序中,某个线程进入到"等待状态"时,必须有其他线程来唤醒处于"等待状态"的线程

线程通讯需要使用的API方法:

等待方法

特殊之处:会释放对象锁

java 复制代码
wait() //无限等待 (只能其他线程唤醒)
wait(long 毫秒) //计时等待 (时间到了自动唤醒)  

唤醒方法

特殊之处:不会释放对象锁

java 复制代码
notify()  //唤醒处于"等待状态"的任意一个线程
nitityAll() //唤醒处于"等待状态"的所有线程   

使用细节:

wait()方法和notify()方法,都必须绑定在对象锁上

java 复制代码
Object lock = new Object(); //对象锁

lock.wait();

lock.notify();

注意:
    1.等待和唤醒的方法,都要使用锁对象调用(需要在同步代码块中使用)。
    2.等待和唤醒方法应该使用相同的锁对象调用。

等待和唤醒的方法调用有什么注意事项?

  • 等待的方法会释放锁,唤醒的方法不会释放锁
  • 等待和唤醒的方法,都要使用锁对象调用(需要在同步代码块中使用)。
  • 等待和唤醒方法应该使用相同的锁对象调用。

等待唤醒代码实现

1 线程进入无限等待

java 复制代码
//线程进入无限等待
//注意:进入无限等待需要使用锁在同步代码中调用wait方法。
public class Test1 {
    public static void main(String[] args) {
//        new Thread(new Runnable() {
//            @Override
//            public void run() {}
//        });
        //使用Lambda表达式
        new Thread(()->{
            synchronized ("对象锁"){
                try {
                    System.out.println("将进入无线等待状态~");//将进入无线等待状态~
                    "对象锁".wait();//无线等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("继续执行...");//只有没有唤醒就不会输出
            }
        }).start();
    }
}

运行结果:
将进入无线等待状态~

2 线程进入无限等待后被唤醒

java 复制代码
//注意:等待和唤醒是两个或多个线程之间实现的。进入无限等待的线程是不会自动唤醒,只能通过其他线程来唤醒。
public class Test2 {
    public static void main(String[] args) {
        //创建线程任务对象
        Runnable task = new Runnable() {//匿名内部类
            Object lock = new Object();//对象锁

            boolean flag = true;//开关键
            @Override
            public void run() {
                synchronized (lock) {
                    if (flag) {
                        try {
                            System.out.println(Thread.currentThread().getName()+"即将进入无线等待状态");//Thread-0即将进入无线等待状态
                            flag = false;
                            lock.wait();//等待,同时释放掉对象锁,Thread-0线程在这里等待

                            System.out.println(Thread.currentThread().getName()+"已经唤醒了");//Thread-0已经唤醒了
                            System.out.println("程序继续执行。。。");//程序继续执行。。。
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    } else {
                        System.out.println("即将唤醒处于等待状态的线程。。。");//即将唤醒处于等待状态的线程。。。
                        try {
                            Thread.sleep(1000);//休眠1秒,1秒后唤醒
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        lock.notify();//唤醒处于等待状态的线程
                        System.out.println(Thread.currentThread().getName()+"在执行");//Thread-1在执行
                    }
                }
            }
        };
//创建两个线程
        new Thread(task).start();
        new Thread(task).start();

    }
}

运行结果:
Thread-0即将进入无线等待状态
即将唤醒处于等待状态的线程。。。
Thread-1在执行
Thread-0已经唤醒了
程序继续执行。。。

3 线程进入计时等待并唤醒

java 复制代码
//注意:进入计时等待的线程,时间结束前可以被其他线程唤醒。时间结束后会自动唤醒
public class Test3 {
    public static void main(String[] args) {
        new Thread(()->{
            synchronized("对象锁"){
                System.out.println("即将进入倒计时等待状态,休息3秒后,自动唤醒~~~");
                try {
                    "对象锁".wait(3000);//待定3秒后,自动唤醒,继续执行后面代码
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("程序继续执行~");
            }
        }).start();
    }
}

运行结果:
即将进入倒计时等待状态,休息3秒后,自动唤醒~~~
程序继续执行~

注:

java 复制代码
代码中的
lock.notify();
表示唤醒处于和notify使用同一个对象锁上的,且处于"等待状态"的任意一个线程

生产者消费者案例【经典】

java 复制代码
//共享资源
public class Resource {
    public static int num = 0;
    //共享的对象锁
    public static final String LOCK = "对象锁";
}


//生产者线程
public class ProdecerTask implements Runnable{
    @Override
    public void run() {

            //必须保证生产者和消费者是同一个对象锁
            synchronized (Resource.LOCK) {
                //判断桌子上有没有食物
                if (Resource.num == 0) {
                    //没有:生产食物
                    System.out.println(Thread.currentThread().getName() + "发现没有食物,开始生产~~");
                    Resource.num = 1;
                    //唤醒:消费者来吃
                    Resource.LOCK.notify();
                } else {
                    //有:进入等待
                    System.out.println(Thread.currentThread().getName() + "发现有食物,等待中~~");
                    try {
                        Resource.LOCK.wait();//释放掉锁
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }

    }

}



//消费者
public class ConsumerTask implements Runnable{
    //必须保证生产者和消费者是同一个对象锁
    @Override
    public void run() {

            //同步代码块
            synchronized (Resource.LOCK) {
                //判断有没有吃的
                if (Resource.num == 0) {
                    //没有
                    try {
                        System.out.println(Thread.currentThread().getName() + "没有发现食物,等待中~~~");
                        Resource.LOCK.wait();//进入无限等待中直到被唤醒(会释放对象锁)
                        System.out.println(Thread.currentThread().getName()+"等待完成,开吃");
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                } else {
                    //有食物
                    System.out.println(Thread.currentThread().getName() + "消费者发现食物,开始消费"+Resource.num);
                    Resource.num--;
                    //消费完了要唤醒生产者可以继续生产
                    Resource.LOCK.notify();
                }
            }

    }
}



public class Test1 {
    public static void main(String[] args) {
        new Thread(new ConsumerTask(), "消费者").start();
        new Thread(new ProdecerTask(), "生产者").start();
    }
}


运行结果:
消费者没有发现食物,等待中~~~
生产者发现没有食物,开始生产~~
消费者等待完成,开吃
相关推荐
MSTcheng.7 小时前
【C++】C++异常
java·数据库·c++·异常
大模型玩家七七8 小时前
基于语义切分 vs 基于结构切分的实际差异
java·开发语言·数据库·安全·batch
寻星探路13 小时前
【深度长文】万字攻克网络原理:从 HTTP 报文解构到 HTTPS 终极加密逻辑
java·开发语言·网络·python·http·ai·https
曹牧16 小时前
Spring Boot:如何测试Java Controller中的POST请求?
java·开发语言
爬山算法16 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
kfyty72517 小时前
集成 spring-ai 2.x 实践中遇到的一些问题及解决方案
java·人工智能·spring-ai
猫头虎17 小时前
如何排查并解决项目启动时报错Error encountered while processing: java.io.IOException: closed 的问题
java·开发语言·jvm·spring boot·python·开源·maven
李少兄17 小时前
在 IntelliJ IDEA 中修改 Git 远程仓库地址
java·git·intellij-idea