• 线程基础2


    1、wait和notify

    注意下面的几点:

    wait和notify是Object类的常用的方法

    wait要释放锁对象

    notify不释放锁对象

     

    下面实现这样一个功能:

    开启两个线程一个线程向一个list集合中添加元素

    另外一个线程如果监听到第一个线程添加元素的编号是5,就退出该线程

    如何实现了,我们来看下面的代码

    package com.bjsxt.base.conn008;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class ListAdd1 {
    
        private volatile static List list = new ArrayList();    
        
        public void add(){
            list.add("bjsxt");
        }
        public int size(){
            return list.size();
        }
        
        public static void main(String[] args) {
            
            final ListAdd1 list1 = new ListAdd1();
            
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        for(int i = 0; i <10; i++){
                            list1.add();
                            System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素..");
                            Thread.sleep(500);
                        }    
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "t1");
            
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    while(true){
                        if(list1.size() == 5){
                            System.out.println("当前线程收到通知:" + Thread.currentThread().getName() + " list size = 5 线程停止..");
                            throw new RuntimeException();
                        }
                    }
                }
            }, "t2");        
            
            t1.start();
            t2.start();
        }
        
        
    }

    程序的运行效果是:

    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程收到通知:t2 list size = 5 线程停止..
    Exception in thread "t2" java.lang.RuntimeException
    at com.bjsxt.base.conn008.ListAdd1$2.run(ListAdd1.java:42)
    at java.lang.Thread.run(Thread.java:744)
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..

    有两个点需要注意的地方:

    1、第一两个线程都操作了list这个集合,为了保证集合在线程的可见性,所以用volatile申明

    2、第二个线程一直开启一个while循环一直在执行判断,当等于5的时候抛出一个运行时的异常退出当前线程

    这个办法效率不高一直开启一个线程一直在while循环

    应该使用线程之间的通信使用wait和notify

    我们来看下面的代码:

    package com.bjsxt.base.conn008;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.CountDownLatch;
    /**
     * @author alienware
     *
     */
    public class ListAdd2 {
        private volatile static List list = new ArrayList();    
        
        public void add(){
            list.add("bjsxt");
        }
        public int size(){
            return list.size();
        }
        
        public static void main(String[] args) {
            
            final ListAdd2 list2 = new ListAdd2();
            final Object lock = new Object();
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        synchronized (lock) {
                            System.out.println("t1启动..");
                            for(int i = 0; i <10; i++){
                                list2.add();
                                System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素..");
                                Thread.sleep(500);
                                if(list2.size() == 5){
                                    System.out.println("已经发出通知..");
                                    lock.notify();
                                }
                            }                        
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                }
            }, "t1");
            
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock) {
                        System.out.println("t2启动..");
                        if(list2.size() != 5){
                            try {
                                lock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("当前线程:" + Thread.currentThread().getName() + "收到通知线程停止..");
                        throw new RuntimeException();
                    }
                }
            }, "t2");    
            t2.start();
            t1.start();
            
        }
        
    }

    程序的运行结果是:

    t2启动..
    t1启动..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    已经发出通知..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t2收到通知线程停止..
    Exception in thread "t2" java.lang.RuntimeException
    at com.bjsxt.base.conn008.ListAdd2$2.run(ListAdd2.java:60)
    at java.lang.Thread.run(Thread.java:744)

    没有达到我们的效果我们来分析下

    t2线程先启动,首先获得锁,然后判断当前的编号不是5,执行wait操作,wait操作会将当前的线程阻塞,但是会释放锁,此时t2线程就被阻塞了,但是锁被释放了

    t1线程此时就可以获得锁对象,执行代码一直执行,执行到编号是5的时候调用notify函数,notify会发出通知,不会阻塞当前的线程,但是不会释放当前的锁,这样t1线程就无法获得锁对象执行wait阻塞线程后面的代码

    执行t1线程添加完成所有的代码之后,线程结束才释放锁,t2线程才能继续执行阻塞后面的代码

    如何修改了,当编号是5的时候 t1线程执行notify之后应该释放锁 阻塞t1线程,然后t2线程获得锁对象,执行完成之后释放锁,让t1线程继续执行后续的代码,修改如下

    package com.bjsxt.base.conn008;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.CountDownLatch;
    /**
     * @author alienware
     *
     */
    public class ListAdd2 {
        private volatile static List list = new ArrayList();    
        
        public void add(){
            list.add("bjsxt");
        }
        public int size(){
            return list.size();
        }
        
        public static void main(String[] args) {
            
            final ListAdd2 list2 = new ListAdd2();
            final Object lock = new Object();
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        synchronized (lock) {
                            System.out.println("t1启动..");
                            for(int i = 0; i <10; i++){
                                list2.add();
                                System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素..");
                                Thread.sleep(500);
                                if(list2.size() == 5){
                                    System.out.println("已经发出通知..");
                                    lock.notify();
                                    
                                    lock.wait();
                                }
                                
                            }                        
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                }
            }, "t1");
            
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock) {
                        System.out.println("t2启动..");
                        if(list2.size() != 5){
                            try {
                                lock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("当前线程:" + Thread.currentThread().getName() + "收到通知线程停止..");
                        lock.notify();
                        throw new RuntimeException();
                    }
                }
            }, "t2");    
            t2.start();
            t1.start();
            
        }
        
    }

    我们来看看程序运行的效果:

    t2启动..
    t1启动..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    已经发出通知..
    Exception in thread "t2" java.lang.RuntimeException
    at com.bjsxt.base.conn008.ListAdd2$2.run(ListAdd2.java:64)
    at java.lang.Thread.run(Thread.java:744)
    当前线程:t2收到通知线程停止..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..

    就达到了我们的效果

    上面的这种方式比较复杂,我们可以使用countDownlatch来解决该问题

    package com.bjsxt.base.conn008;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.CountDownLatch;
    /**
     * @author alienware
     *
     */
    public class ListAdd2 {
        private volatile static List list = new ArrayList();    
        
        public void add(){
            list.add("bjsxt");
        }
        public int size(){
            return list.size();
        }
        
        public static void main(String[] args) {
            
            final ListAdd2 list2 = new ListAdd2();
            /**
             * CountDownLatch不需要和锁对象一起使用
             *  CountDownLatch(1)的初始化值1,当值变成0的时候就会释放锁对象
             * */
            final CountDownLatch cLatch = new CountDownLatch(1);
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                            System.out.println("t1启动..");
                            for(int i = 0; i <10; i++){
                                list2.add();
                                System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素..");
                                Thread.sleep(500);
                                if(list2.size() == 5){
                                    System.out.println("已经发出通知..");
                                    cLatch.countDown();
                                }
                                
                            }                        
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                }
            }, "t1");
            
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                        System.out.println("t2启动..");
                        if(list2.size() != 5){
                            try {
                                cLatch.await();
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                        }
                        System.out.println("当前线程:" + Thread.currentThread().getName() + "收到通知线程停止..");
                        
                        throw new RuntimeException();
                }
            }, "t2");    
            t2.start();
            t1.start();
            
        }
        
    }

    t2启动..
    t1启动..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    已经发出通知..Exception in thread "t2" java.lang.RuntimeException
    at com.bjsxt.base.conn008.ListAdd2$2.run(ListAdd2.java:64)
    at java.lang.Thread.run(Thread.java:744)

    当前线程:t1添加了一个元素..
    当前线程:t2收到通知线程停止..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..

    就达到了我们的效果

  • 相关阅读:
    dede自定义表单增加添加时间怎么弄
    今天微信群需要人家通过吗?是微信bug吗
    6.3.28微信需群主确认才可进群&发GIF动图功能内测开始了
    聚类分析初探
    一小时了解数据挖掘⑤数据挖掘步骤&常用的聚类、决策树和CRISP-DM概念
    Bayesian optimisation for smart hyperparameter search
    【模式识别】Learning To Rank之RankBoost
    UVA 816
    设计一个算法,输出从u到v的全部最短路径(採用邻接表存储)
    禅道——測试流程
  • 原文地址:https://www.cnblogs.com/kebibuluan/p/7611904.html
Copyright © 2020-2023  润新知