zoukankan      html  css  js  c++  java
  • Java 多线程编程核心技术之0

     回首翻了翻之前的文章,当时的思路和场景其实还是历历在目(os其实个人觉得自己记忆力还是不错的。。),废话不多说了,没有后续 也没有争取,有的就是坚持和执行力,过去了快6年 我发现自己还是能折腾的。

    线程间的通信之 等待/通知机制

    前提是多 synchronized 同步锁有一定的了解。

    未借助同步锁

    在多线程中,如若未借助同步锁的话。我们可以通过多个线程对同一个对象的状态进行处理,

    代码示例:

    /**
    * Service 为监控示例对象
    */
    public class Service {
        
        private List<Integer> list = new ArrayList<>();
        
        public void add(){
            list.add(1);
        }
        
        public int getSize(){
            return list.size();
        }
    }
    package com.kirago.cp03.demo01;
    
    public class ThreadA extends Thread{
        
        private Service service;
        
        public ThreadA(Service service){
            super();
            this.service = service;
        }
        
        @Override
        public void run(){
            try {
                
                for(int i= 0;i<10;i++){
                    service.add();
                    System.out.println("list 添加了 " + (i+1) + " 个 元素");
                    Thread.sleep(1000);
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
    package com.kirago.cp03.demo01;
    
    public class ThreadB extends Thread{
        private Service service;
        
        public ThreadB(Service service){
            super();
            this.service = service;
        }
        
        @Override
        public void run(){
            try {
                while (true){
                    if(service.getSize() >= 5){
                        System.out.println(">= 5 了,线程 " + Thread.currentThread().getName() + " 要退出了" );
                        throw new InterruptedException();
                    }
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
                
        }
    }

    测试代码如下:

    public class ServiceTest {
        
        @Test
        public void run(){
            try {
                Service service = new Service();
    
                ThreadA threadA = new ThreadA(service);
                threadA.setName("A");
                threadA.start();
    
                ThreadB threadB = new ThreadB(service);
                threadB.setName("B");
                threadB.start();
                
                Thread.sleep(10000);
            }catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    }

    线程B通过while 循环一直轮训“监控”对象的属性状态。

    通过如此的处理有个很严重的问题:

    我们会发现线程B的while循环一直去取 service 实例的属性来做业务逻辑的转移判断,如果 jvm 是 client 模式,那么会频繁的会从公共堆栈区域进行读取,其实这是严重的性能消耗。其实在 java 的体系中优秀的大佬们通过 wait/notify 机制来完美解决此问题。

    wait/notify 机制

    需要记住的一点事,wait/notify 的调用一定是在同步代码块执行。

    • 方法wait()的作用是使当前执行代码的线程进行等待,wait()方法是Object类的方法,该方法用来将当前线程置入“预执行队列”中,并且在wait()所在的代码行处停止执行,直到接到通知或被中断为止。在调用wait()之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法。在执行wait()方法后,当前线程释放锁。在从wait()返回前,线程与其他线程竞争重新获得锁。如果调用wait()时没有持有适当的锁,则抛出IllegalMonitorStateException,它是RuntimeException的一个子类,因此,不需要try-catch语句进行捕捉异常。
    • 方法notify()也要在同步方法或同步块中调用,即在调用前,线程也必须获得该对象的对象级别锁。如果调用notify()时没有持有适当的锁,也会抛出IllegalMonitorStateException。该方法用来通知那些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则由线程规划器随机挑选出其中一个呈wait状态的线程,对其发出通知notify,并使它等待获取该对象的对象锁。需要说明的是,在执行notify()方法后,当前线程不会马上释放该对象锁,呈wait状态的线程也并不能马上获取该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出synchronized代码块后,当前线程才会释放锁,而呈wait状态所在的线程才可以获取该对象锁。当第一个获得了该对象锁的wait线程运行完毕以后,它会释放掉该对象锁,此时如果该对象没有再次使用notify语句,则即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,还会继续阻塞在wait状态,直到这个对象发出一个notify或notifyAll。

    用一句话来总结一下wait和notify:wait使线程停止运行,而notify使停止的线程继续运行。

    代码示例

    package com.kirago.cp03.demo04;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class MyList {
        private List list = new ArrayList();
        
        public void add(){
            list.add("item");
        }
        
        public int size(){
            return list.size();
        }
    }
    package com.kirago.cp03.demo04;
    
    public class ThreadA extends Thread{
        
        private MyList myList;
        
        public ThreadA(MyList myList){
            super();
            this.myList = myList;
        }
        
        @Override
        public void run(){
            try {
                synchronized (myList){
                    if(myList.size() != 5){
                        System.out.println(" wait begin " + System.currentTimeMillis());
                        myList.wait();
                        System.out.println(" wait end " + System.currentTimeMillis());
                    }
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
    
        }
    }
    package com.kirago.cp03.demo04;
    
    public class ThreadB extends Thread{
        private MyList myList;
        
        public ThreadB(MyList myList){
            super();
            this.myList = myList;
        }
        
        @Override
        public void run(){
            try {
                synchronized (myList){
                    for(int i=0;i<10;i++){
                        myList.add();
                        if(myList.size() == 5){
                            myList.notify();
                            System.out.println(" 已经发出通知!");
                        }
                        System.out.println("添加了 " + myList.size() + " 个元素");
                        Thread.sleep(1000);
                    }
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }

    测试代码:

    package com.kirago.cp03.demo04;
    
    
    import org.junit.Test;
    
    public class MyListTest {
        
        @Test
        public void run(){
            try {
                MyList myList = new MyList();
                ThreadA threadA = new ThreadA(myList);
                threadA.start();
                
                ThreadB threadB = new ThreadB(myList);
                threadB.start();
                
                Thread.sleep(14000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    
    }

    关键字synchronized可以将任何一个Object对象作为同步对象来看待,而Java为每个Object都实现了wait()和notify()方法,它们必须用在被synchronized同步的Object的临界区内。通过调用wait()方法可以使处于临界区内的线程进入等待状态,同时释放被同步对象的锁。而notify操作可以唤醒一个因调用了wait操作而处于阻塞状态中的线程,使其进入就绪状态。被重新换醒的线程会试图重新获得临界区的控制权,也就是锁,并继续执行临界区内wait之后的代码。如果发出notify操作时没有处于阻塞状态中的线程,那么该命令会被忽略。wait()方法可以使调用该方法的线程释放共享资源的锁,然后从运行状态退出,进入等待队列,直到被再次唤醒。

  • 相关阅读:
    glog Windows Visual Studio 2013 编译项目
    Git Tag管理发行版本
    Ubuntu 16.04环境中读取XBOX 360手柄信息
    GCC 中 的pie和fpie选项
    CMakeLists.txt 常用指令说用
    chrome无法访问github.com
    删除前n天的数据
    shell(9)秒转换为时分秒
    Drools规则引擎实践直白总结
    空闲时间研究一个小功能:winform桌面程序如何实现动态更换桌面图标
  • 原文地址:https://www.cnblogs.com/kirago/p/13438748.html
Copyright © 2011-2022 走看看