线程基础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添加了一个元素..

就达到了我们的效果

原文地址:https://www.cnblogs.com/kebibuluan/p/7611904.html