Java线程协作的两种方式小结

 更新时间:2023年05月05日 10:27:32   作者:思维的深度  
Java中线程协作的最常见的两种方式是利用Object.wait()、Object.notify()和使用Condition,本文主要介绍了Java线程协作的两种方式小结,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧

为什么线程之间需要协作

线程之间相互配合,完成某项工作,比如:一个线程修改了一个对象的值,而另一个线程感知到了变化,然后进行相应的操作,整个过程开始于一个线程,而最终执行又是另一个线程。前者是生产者,后者就是消费者,这种模式隔离了“做什么”(What)和“怎么做”(How)。简单的办法是让消费者线程不断地循环检查变量是否符合预期,在while循环中设置不满足的条件,如果条件满足则退出while循环,从而完成消费者的工作。这样进行线程之间的协作却存在如下2个问题:

(1)难以确保及时性。

(2)难以降低开销。如果降低睡眠的时间,比如休眠1毫秒,这样消费者能更加迅速地发现条件变化,但是却可能消耗更多的处理器资源,造成了无端的浪费。

那么有没有什么办法可以解决以上2个问题呢?此时等待/通知机制毫不客气的站出来说,都让开,交给我,我能行! 

介绍

Java中线程协作的最常见的两种方式:利用Object.wait()、Object.notify()和使用Condition

方法一

Object中的wait、notify、notifyAll方法定义如下

  • public final native void notify(); public final native void notifyAll(); public final native void wait(long timeout) throws InterruptedException;
  • wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写
  • 调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)
  • 调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程
  • 调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程
  • 之所以这三个方法声明在Object类中是因为每个对象都拥有monitor(即锁)
  • 调用某个对象的wait()方法,当前线程必须拥有这个对象的monitor(即锁),因此调用wait()方法必须在同步块或者同步方法中进行

示例

public class Test {
    public static Object object = new Object();
    public static void main(String[] args) {
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
        thread1.start();
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread2.start();
    }
    static class Thread1 extends Thread{
        @Override
        public void run() {
            synchronized (object) {
                try {
                    object.wait();
                } catch (InterruptedException e) {
                }
                System.out.println("线程"+Thread.currentThread().getName()+"获取到了锁");
            }
        }
    }
    static class Thread2 extends Thread{
        @Override
        public void run() {
            synchronized (object) {
                object.notify();
                System.out.println("线程"+Thread.currentThread().getName()+"调用了object.notify()");
            }
            System.out.println("线程"+Thread.currentThread().getName()+"释放了锁");
        }
    }
}

运行结果

线程Thread-1调用了object.notify()
线程Thread-1释放了锁
线程Thread-0获取到了锁       

方法二

  • Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition1的await()、signal()这种方式实现线程间协作更加安全和高效
  • Condition是个接口,基本的方法就是await()和signal()方法
  • Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition()
  • 调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用

示例

public class Test {
    private int queueSize = 10;
    private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);
    private Lock lock = new ReentrantLock();
    private Condition notFull = lock.newCondition();
    private Condition notEmpty = lock.newCondition();
    public static void main(String[] args)  {
        Test test = new Test();
        Producer producer = test.new Producer();
        Consumer consumer = test.new Consumer();
        producer.start();
        consumer.start();
    }
    class Consumer extends Thread{
        @Override
        public void run() {
            consume();
        }
        private void consume() {
            while(true){
                lock.lock();
                try {
                    while(queue.size() == 0){
                        try {
                            System.out.println("队列空,等待数据");
                            notEmpty.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    queue.poll();                //每次移走队首元素
                    notFull.signal();
                    System.out.println("从队列取走一个元素,队列剩余"+queue.size()+"个元素");
                } finally{
                    lock.unlock();
                }
            }
        }
    }
    class Producer extends Thread{
        @Override
        public void run() {
            produce();
        }
        private void produce() {
            while(true){
                lock.lock();
                try {
                    while(queue.size() == queueSize){
                        try {
                            System.out.println("队列满,等待有空余空间");
                            notFull.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    queue.offer(1);        //每次插入一个元素
                    notEmpty.signal();
                    System.out.println("向队列取中插入一个元素,队列剩余空间:"+(queueSize-queue.size()));
                } finally{
                    lock.unlock();
                }
            }
        }
    }
}

到此这篇关于Java线程协作的两种方式小结的文章就介绍到这了,更多相关Java线程协作内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java设计模式笔记之适配器模式

    java设计模式笔记之适配器模式

    这篇文章主要为大家详细介绍了java设计模式之适配器模式笔记,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-10-10
  • Java常用内置注解用法分析

    Java常用内置注解用法分析

    这篇文章主要介绍了Java常用内置注解用法,结合实例形式分析了java使用@SuppressWarnings关闭警告信息以及@Depreca标注的元素不使用两种注解使用方法,需要的朋友可以参考下
    2019-08-08
  • 解析Java实现随机验证码功能的方法详解

    解析Java实现随机验证码功能的方法详解

    本篇文章是对Java实现随机验证码功能的方法进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • Java设计模式之23种设计模式详解

    Java设计模式之23种设计模式详解

    这篇文章主要介绍了Java设计模式之23种设计模式详解,设计模式使代码编制真正工程化,设计模式是软件工程的基石,项目中合理的运用设计模式可以完美的解决很多问题,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • 用Java产生100个1-150间不重复数字

    用Java产生100个1-150间不重复数字

    这篇文章主要介绍了用Java产生100个1-150间不重复数字,需要的朋友可以参考下
    2017-02-02
  • MongoDB整合Spring实例详细讲解(含代码)

    MongoDB整合Spring实例详细讲解(含代码)

    这篇文章主要介绍了MongoDB整合Spring实例详细讲解(含代码),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • Java Spring的refresh方法你知道吗

    Java Spring的refresh方法你知道吗

    这篇文章主要为大家详细介绍了Java Spring的refresh方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03
  • 基于springboot 配置文件context-path的坑

    基于springboot 配置文件context-path的坑

    这篇文章主要介绍了基于springboot 配置文件context-path的坑,基于很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • 基于Spring的RPC通讯模型的使用与比较

    基于Spring的RPC通讯模型的使用与比较

    这篇文章主要介绍了基于Spring的RPC通讯模型的使用与比较,详细的介绍了RMI、Caucho的Hessian和Burlap以及Spring自带的HTTP invoker,感兴趣的可以了解一下
    2018-09-09
  • 基于Idea+Jconsole实现线程监控步骤

    基于Idea+Jconsole实现线程监控步骤

    这篇文章主要介绍了基于Idea+Jconsole实现线程监控功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04

最新评论