使用Condition类来代替 Object 监视器方法(wait,notify)

java.util.concurrent.locks 包中,规定了三个接口,来代替synchronized和Object的监视器方法,wait,notify。

即帮助我们更加灵活的控制线程的状态和同步。至于如何使用Lock请参考ReentrantLock和synchronized的区别

  1. Condition 代替Object的监视器方法,wait,notify。
  2. Lock  代替synchronized关键字
  3. ReadWriteLock 实现读写锁,写写互斥,读写互斥,读读不互斥。

本文使用Condition实现一个固定同步容器。

在容器已满时,生产者的线程等待,生产者生产时,即唤醒所有等待的消费者(如果有的话)。

在容器已空时,消费者的容器等待,消费者消费时,即唤醒所有等待的生产者(如果有的话)。

import java.util.Arrays;

/**
 * 使用synchronized和Object.wait();Object.notifyAll();
 * 完成的固定长度同步容器
 * @author TomcatLikeYou
 * @param <E>
 */
public class SynchronizedContainer<E>
{
    private int size = 10;
    private E[] array = null;
    private int index = 0;

    @SuppressWarnings("all")
    public SynchronizedContainer()
    {
        array = (E[]) new Object[size];
    }

    @SuppressWarnings("all")
    public SynchronizedContainer(int size)
    {
        this.size = size;
        array = (E[]) new Object[size];
    }

    public synchronized void add(E element)
    {
        while (index >= size)
        {
            try
            {
                System.out.println("线程:" + Thread.currentThread().getName() + "  等待消费");
                this.wait();
                System.out.println("线程:" + Thread.currentThread().getName() + "  被唤醒");
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
        array[index] = element;
        index++;
        System.out.println("线程:" + Thread.currentThread().getName() + "  将数组增加至" + this.toString());
        this.notifyAll();
    }

    public synchronized E get()
    {
        while (index <= 0)
        {
            try
            {
                System.out.println("线程:" + Thread.currentThread().getName() + "  等待生产");
                this.wait();
                System.out.println("线程:" + Thread.currentThread().getName() + "  被唤醒");
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
        index--;
        E element =  array[index];
        //下面语句这是没必要的操作,但是为了看的清楚被消费了,加上这句
        array[index] = null;
        this.notifyAll();
        System.out.println("线程:" + Thread.currentThread().getName() + "  将数组减少至" + this.toString());
        return element;
    }

    @Override
    public String toString()
    {
        return Arrays.toString(array);
    }

    public static void main(String[] args)
    {
        SynchronizedContainer array = new SynchronizedContainer(20);

        for (int i = 0 ; i <2 ; i++)
        {
            new Thread(()->{
                for (int j = 0 ; j < 500 ; j++)
                {
                    array.get();
                }
            },"消费者线程" + String.valueOf(i)).start();
        }
        for (int i = 0 ; i <10 ; i++)
        {
           new Thread(()->{
               for (int j = 0 ; j < 100 ; j++)
               {
                   array.add(String.valueOf(j));
               }
           },"生产者线程" + String.valueOf(i)).start();
        }
    }
}
import java.util.Arrays;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 使用Lock和Condition
 * 完成的固定长度同步容器
 * @author TomcatLikeYou
 */
public class ConditionLockContainer<E>
{
    //容器的大小
    private int size = 10;
    //实际存储容器的数组
    private E[] array = null;
    //index表示下一个能被添加的元素在数组中的下标
    private int index = 0;

    //实现同步的锁
    Lock lock = new ReentrantLock();
    //get条件
    Condition getCondition = lock.newCondition();
    //put条件
    Condition addCondition = lock.newCondition();

    //不用加synchronized,内部使用了lock加锁
    public E get()
    {
        //获取锁
        lock.lock();
        E element;
        //由于如果lock时发生异常,不会自动释放锁,故在finally中释放锁(虽然此例中不需要)
        try
        {
            //只要index等于0,就等待
            while (index <= 0)
            {
                try
                {
                    System.out.println("线程:" + Thread.currentThread().getName() + "  等待生产");
                    getCondition.await();
                    System.out.println("线程:" + Thread.currentThread().getName() + "  被唤醒");
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
            index--;
            element = array[index];
            //下面语句这是没必要的操作,但是为了看的清楚被消费了,加上这句
            array[index] = null;
            //唤醒所有等待的生产者线程
            System.out.println("线程:" + Thread.currentThread().getName() + "  将数组减少至" + this.toString());
            addCondition.signalAll();
        }
        finally
        {
            //释放锁
            lock.unlock();
        }
        return element;
    }

    public void add(E element)
    {
        lock.lock();
        try
        {
            while (index >= size)
            {
                try
                {
                    System.out.println("线程:" + Thread.currentThread().getName() + "  等待消费");
                    addCondition.await();
                    System.out.println("线程:" + Thread.currentThread().getName() + "  被唤醒");
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
            array[index] = element;
            index++;
            System.out.println("线程:" + Thread.currentThread().getName() + "  将数组增加至" + this.toString());
            //唤醒所有生产者线程
            getCondition.signalAll();
        }
        finally
        {
            lock.unlock();
        }
    }

    @SuppressWarnings("all")
    public ConditionLockContainer()
    {
        array = (E[]) new Object[size];
    }

    @SuppressWarnings("all")
    public ConditionLockContainer(int size)
    {
        this.size = size;
        array = (E[]) new Object[size];
    }

    @Override
    public String toString()
    {
        return Arrays.toString(array);
    }

    public static void main(String[] args)
    {
        ConditionLockContainer array = new ConditionLockContainer(20);

        for (int i = 0 ; i <2 ; i++)
        {
            new Thread(()->{
                for (int j = 0 ; j < 500 ; j++)
                {
                    array.get();
                }
            },"消费者线程" + String.valueOf(i)).start();
        }
        for (int i = 0 ; i <10 ; i++)
        {
            new Thread(()->{
                for (int j = 0 ; j < 100 ; j++)
                {
                    array.add(String.valueOf(j));
                }
            },"生产者线程" + String.valueOf(i)).start();
        }
    }
}

分析代码可知,使用Condition效率会更高,更灵活,唤醒的不是所有等待线程,而是指定线程。而且Condition的await可以指定多个不同方法,见API文档


猜你喜欢

转载自blog.csdn.net/qq_37293230/article/details/80634123