Java线程通信的多种实现形式(代码为主)

来源:互联网 发布:js设置按钮隐藏 编辑:程序博客网 时间:2024/06/16 16:25

1.1  概念

使用wait和notify方法实现线程间的通信(这两个方法都是Object的类的方法)

wait和notify必须配合synchronized关键字使用

wait方法释放锁,而notify方法不释放锁.

 

1.2  线程通信实例1—线程通信基本版

比较简单的一个线程间通信的例子:

线程1负责向list中添加元素,线程2判断list中的元素的个数,当等于5的时候,线程2终止;

1.2.1  代码

 

/*

 * Description:线程通信01

 * fileName:ListAdd1.java

 * author:@xflig

 * Date:  20171126下午3:32:32

 *

 * Version: @version1.0

 *       

 */

public class ListAdd1

{

    private volatile static List list = new ArrayList<>();

   

    public void add()

    {

        list.add("apple");

    }

   

    public int size()

    {

        returnlist.size();

    }

 

    public static void main(String[] args)

    {

        //1.创建对象

        final ListAdd1listAdd1 = new ListAdd1();

       

        //2 创建2个线程

        Thread t1 = new Thread(new Runnable()

        {

            @Override

            public void run()

            {

                try

                {

                    for(inti = 0; i < 10;i++)

                    {

                        listAdd1.add();

                        System.out.println("当前线程" + Thread.currentThread().getName()+"新添加了一个元素..." );

                        Thread.sleep(2000);

                    }

                }

                catch (Exceptione)

                {

                }

               

            }

        },"t1");

       

        Thread t2 = new Thread(new Runnable()

        {

            @Override

            public void run()

            {

                while (true)

                {

                    if(listAdd1.size() == 5)

                    {

                        System.out.println("当前线程收到通知:" + Thread.currentThread().getName() + "list 长度为5,线程停止....");

                        thrownew RuntimeException();

                    }

                }

               

            }

        },"t2");

       

        t1.start();

        t2.start();

       

 

    }

 

}

 

 

1.2.2  运行结果



 

弊端:线程2一致在判断当前list的size是否为5,消耗内存.

1.3  线程通信实例2—使用wait/notify线程通信

1.3.1  代码

/*

 * Description:线程通信--wait/notify

 * fileName:ListAdd2.java

 * author:@xflig

 * Date:  20171126下午3:57:44

 *

 * Version: @version1.0

 *       

 */

public class ListAdd2

{

    private volatile static List list = new ArrayList<>();

   

    public void add()

    {

        list.add("orange");

    }

   

    public int size()

    {

        returnlist.size();

    }

   

    public static void main(String[] args)

    {

        //创建对象

        final ListAdd2listAdd2 = new ListAdd2();

       

        //实例出来一个lock

        final Objectlock = new Object();

       

        //当时用wait/notify的时候,一定要配合synchronized使用

        //创建2个线程

        Thread t1 = new Thread(new Runnable()

        {

            @Override

            public void run()

            {

                try

                {

                    synchronized (lock)

                    {

                        for(inti=0; i<10; i++)

                        {

                            listAdd2.add();

                            System.out.println("当前线程" + Thread.currentThread().getName()+"新添加了一个元素..." );

                            Thread.sleep(1000);

                            if(listAdd2.size() == 5)

                            {

                                System.out.println(Thread.currentThread().getName() +"发出通知....");

                                lock.notify();

                            }

                        }

                       

                    }

                }

                catch (Exceptione)

                {

                    e.printStackTrace();

                }

            }

        },"t1");

       

        Thread t2 = new Thread(new Runnable()

        {

            @Override

            public void run()

            {

                synchronized (lock)

                {

                    if(listAdd2.size() != 5)

                    {

                        try

                        {

                            lock.wait();

                        }

                        catch (InterruptedExceptione)

                        {

                            e.printStackTrace();

                        }

                    }

                    System.out.println("当前线程收到通知:" + Thread.currentThread().getName() + "list 长度为5,线程停止....");

                    thrownew RuntimeException();

                }

               

            }

        },"t2");

       

        //启动线程

        t2.start();

        t1.start();

     

       

 

    }

 

}

 

 

注意:

启动线程的时候,我们一定要先启动线程2,保证线程2先拿到锁,然后判断list的长度是否为5,如果不是5的话,则进入wait,wait是释放锁的,线程1拿到刚刚释放的锁,进行操作,当list的长度为5 的时候,发出notify,但是notify是不释放锁的,只有等待线程1执行结束后,才释放锁.

对比先启动线程1,查看运行结果

 

1.3.2  运行结果


我们看到,这样的设计,好处是:解决了基础版的线程2一直在循环的问题,但是又引入了新的问题,通知没有实时性.

1.4  线程通信实例3—使用countDownLatch 实现实时通信

1.4.1  代码

在实例2的基础上简单做修改

/*

 * 使用countDownLatch实现实时通信

 * Description: fileName:ListAdd3.java

 * author:@xflig

 * Date: 20171126下午4:32:55

 * Version: @version1.0

 */

public class ListAdd3

{

    private volatile static List list = new ArrayList<>();

 

    public void add()

    {

        list.add("orange");

    }

 

    public int size()

    {

        returnlist.size();

    }

 

    public static void main(String[] args)

    {

        // 创建对象

        final ListAdd2listAdd2 = new ListAdd2();

 

        // 实例出来一个lock

        // final Object lock = new Object();

        final CountDownLatchcountDownLatch = new CountDownLatch(1);

 

        // 当时用wait/notify的时候,一定要配合synchronized使用

        // 创建2个线程

        Thread t1 = new Thread(new Runnable()

        {

            @Override

            public void run()

            {

                try

                {

                    // synchronized (lock)

                    // {

                    for (inti = 0; i < 10;i++ )

                    {

                        listAdd2.add();

                        System.out.println(

                            "当前线程" + Thread.currentThread().getName() +"新添加了一个元素...");

                        Thread.sleep(1000);

                        if (listAdd2.size() == 5)

                        {

                            System.out.println(Thread.currentThread().getName() +"发出通知....");

                            // lock.notify();

                            countDownLatch.countDown();

                        }

                    }

 

                    // }

                }

                catch (Exceptione)

                {

                    e.printStackTrace();

                }

            }

        }, "t1");

 

        Thread t2 = new Thread(new Runnable()

        {

            @Override

            public void run()

            {

                // synchronized (lock)

                // {

                if (listAdd2.size() != 5)

                {

                    try

                    {

                        System.out.println("t2进入...");

                        countDownLatch.await();

                        // lock.wait();

                    }

                    catch (InterruptedExceptione)

                    {

                        e.printStackTrace();

                    }

                }

                System.out.println(

                    "当前线程收到通知:" + Thread.currentThread().getName() +"list 长度为5,线程停止....");

                throw new RuntimeException();

                // }

 

            }

        }, "t2");

 

        // 启动线程

        t2.start();

        t1.start();

 

    }

 

}

 

 

 

1.4.2  运行结果

 

 

 

 

1.5  实例3使用wait/notify模拟queue

1.5.1  概述

BlockingQueue;首先它是一个队列,并且支持队列中的阻塞,阻塞的放入和得到数据.我们要实现下面的两个简答的方法put和take

put(Object obj):把obj加到BlockingQueue里,如果BlockingQueue没有空间,则调用此方法的线程被阻塞,知道BlockingQueue里面由空间再继续;

take():取走BlockingQueue里排在首位的元素,若BlockingQueue里面没有元素,则调用此方法的线程阻塞,直到BlockingQueue有新的数据被加入.

1.5.2  代码

/*

 * Description:使用wait/notify模拟队列

 * fileName:MyQueue.java

 * author:@xflig

 * Date:  20171126下午4:47:45

 *

 * Version: @version1.0

 *       

 */

public class MyQueue

{

    //1 创建一个装载元素的集合

    private LinkedList<Object>list = new LinkedList<Object>();

   

    //2 需要一个计数器

    private AtomicIntegercount = new AtomicInteger(0);

   

    //3 集合的上限和下限

    private final int minSize = 0;

   

    private final int maxSize;

   

    //4 构造方法

    public MyQueue(intlen)

    {

        this.maxSize =len;

    }

   

    //5 初始化一个对象用于加锁

    private Objectlock = new Object();

   

    //6 put(Object obj):obj加到BlockingQueue,如果BlockingQueue没有空间,则调用此方法的线程被阻塞,知道BlockingQueue里面由空间再继续;

    public void put(Object obj)

    {

        synchronized (lock)

        {

            while(count.get() ==this.maxSize )

            {

                try

                {

                    lock.wait();

                }

                catch (InterruptedExceptione)

                {

                    e.printStackTrace();

                }

            }

            list.add(obj);

            //计数器+1

            count.incrementAndGet();

            System.out.println("新加入的元素为:" +obj);

            lock.notify();

           

        }

       

    }

   

    //7 take():取走BlockingQueue里排在首位的元素,BlockingQueue里面没有元素,则调用此方法的线程阻塞,直到BlockingQueue有新的数据被加入.

    public Object take()

    {

        Object result = null;

        synchronized (lock)

        {

            while(count.get() ==this.minSize)

            {

                try

                {

                    lock.wait();

                }

                catch (InterruptedExceptione)

                {

                    e.printStackTrace();

                }

            }

            //1 移除元素

            result = list.removeFirst();

            //2 计数器-1

            count.decrementAndGet();

            //3 唤醒操作

            lock.notify();

           

        }

       

        returnresult;

    }

   

 

    public int getSize()

    {

        returnlist.size();

    }

   

    public static void main(String[] args)

    {

        final MyQueuemyQueue = new MyQueue(5);

       

        //添加初始化元素

        myQueue.put("a");

        myQueue.put("b");

        myQueue.put("c");

        myQueue.put("d");

        myQueue.put("e");

       

        System.out.println("当前容器的元素个数为:" +myQueue.getSize());

       

        //创建线程1--添加元素

        Thread t1 = new Thread(new Runnable()

        {

            @Override

            public void run()

            {

                myQueue.put("f");

                myQueue.put("g");

            }

        },"t1");

        t1.start();

       

        //创建线程2--移除元素

       

        Thread t2 = new Thread(new Runnable()

        {

           

            @Override

            public void run()

            {

                Object obj = myQueue.take();

                System.out.println("移除的元素为:" +obj);

                Object obj2 = myQueue.take();

                System.out.println("移除的元素为:" +obj2);

               

               

            }

        },"t2");

        try

        {

            TimeUnit.SECONDS.sleep(2);

        }

        catch (InterruptedExceptione)

        {

            e.printStackTrace();

        }

        t2.start();

       

 

    }

 

}

 

   

 

 

 

1.5.3  运行结果




 

原创粉丝点击