JUC——线程同步锁(Condition精准控制)

  在进行锁处理的时候还有一个接口:Condition,这个接口可以由用户来自己进行锁的对象创建。

  Condition的作用是对锁进行更精确的控制。

  Conditionawait()方法相当于Objectwait()方法,Conditionsignal()方法相当于Objectnotify()方法,ConditionsignalAll()方法相当于ObjectnotifyAll()方法。

  不同的是Objectwait(), notify(), notifyAll() 方法是和“同步锁”(synchronized关键字)捆绑使用的;而Condition是需要与“互斥锁/共享锁”捆绑使用。

  Object Condition
休眠 wait() await()
唤醒单个线程 notify() signal()
唤醒多个线程 notifyAll() signalAll()

范例:观察Condition的基本使用

package so.strong.mall.concurrent;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionDemo {
    private static String msg = null; //设置一个字符串
    public static void main(String[] args)  throws Exception{
        final Lock myLock = new ReentrantLock(); //实例化Lock接口对象
        final Condition condition = myLock.newCondition(); //创建一个新的Condition接口对象
        myLock.lock();
        //如果现在不进行锁定,那么Condition无法执行等代理处理机制,会出现IllegalMonitorStateException
        try {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    myLock.lock();
                    try {
                        msg = "itermis.com";
                        condition.signal(); //唤醒等待的Condition
                    } finally {
                        myLock.unlock();
                    }
                }
            }).start();
            condition.await(); //线程等待
            System.out.println("*******主线程执行完毕,msg="+msg);
        } finally {
            myLock.unlock(); //解除阻塞状态
        }
    }
}
//*******主线程执行完毕,msg=itermis.com

  与之前的Object相比,唯一的区别在于:现在看不见明确的synchronized关键字,而取代synchronizedLock接口中的lock(),unlock()两个方法,而后在阻塞状态(同步状态)下可以使用Condition中的await()signal()方法进行等待与唤醒的操作处理。

范例:实现数据的缓冲控制

package so.strong.mall.concurrent;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author Termis
 * @date 2018/5/3
 */
public class DataBufferDemo {
    public static void main(String[] args) {
        final DataBuffer db = new DataBuffer();
        for (int i = 0; i < 3; i++) { //创建3个写线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 2; j++) {
                        try {
                            TimeUnit.SECONDS.sleep(1);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        db.put(Thread.currentThread().getName() + "写入数据,j=" + j);
                    }
                }
            }, "生产者-" + i).start();
        }

        for (int i = 0; i < 5; i++) { //创建5个读线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            TimeUnit.SECONDS.sleep(3);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        System.out.println("[(" + Thread.currentThread().getName() + ")CONSUMER]" + db.get());
                    }
                }
            }, "消费者-" + i).start();
        }
    }
}

class DataBuffer { //进行数据的缓冲操作控制
    private static final int MAX_LENGTH = 5; // 该类之中保存的数组长度的个数为5
    private Object[] data = new Object[MAX_LENGTH]; //定义一个数组进行全部数据的保存控制
    private Lock myLock = new ReentrantLock(); //创建数据锁
    private Condition putCondition = myLock.newCondition(); //数据保存的Condition控制
    private Condition getCondition = myLock.newCondition(); //数据读取的Condition控制
    private int putIndex = 0; //写入数据的索引
    private int getIndex = 0; //读取数据的索引
    private int count = 0; //当前保存的元素个数

    public Object get() {
        Object getObj = null;
        this.myLock.lock();
        try {
            if (this.count == 0) //没有写入
                this.getCondition.await(); //读取的线程要进行等待
            getObj = this.data[this.getIndex++]; //读取指定索引数据
            if (this.getIndex == MAX_LENGTH)
                this.getIndex = 0; //重新开始读
            this.count--; //因为读了一个数据之后,现在需要减少个数
            this.putCondition.signal(); //告诉写线程可以写入
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            this.myLock.unlock();
        }
        return getObj;
    }

    public void put(Object obj) { //进行缓冲数据的写入操作
        this.myLock.lock(); //进入独占锁状态
        try {
            if (this.count == MAX_LENGTH)  //保存的数据已经满了
                this.putCondition.await(); //暂时先别进行数据保存了
            this.data[this.putIndex++] = obj; //保存当前数据
            if (this.putIndex == MAX_LENGTH) //现在索引已经写满
                this.putIndex = 0; //重置数组操作的索引脚标
            this.count++; //保存的个数需要做一个追加
            this.getCondition.signal(); //唤醒消费线程
            System.out.println("[(" + Thread.currentThread().getName() + ")写入缓冲-put()]" + obj);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            this.myLock.unlock(); //不管如何最终一定要进行解锁
        }
    }
}
[(生产者-2)写入缓冲-put()]生产者-2写入数据,j=0
[(生产者-1)写入缓冲-put()]生产者-1写入数据,j=0
[(生产者-0)写入缓冲-put()]生产者-0写入数据,j=0
[(生产者-1)写入缓冲-put()]生产者-1写入数据,j=1
[(生产者-2)写入缓冲-put()]生产者-2写入数据,j=1
[(消费者-3)CONSUMER]生产者-2写入数据,j=0
[(消费者-4)CONSUMER]生产者-1写入数据,j=1
[(消费者-1)CONSUMER]生产者-0写入数据,j=0
[(消费者-2)CONSUMER]生产者-1写入数据,j=0
[(生产者-0)写入缓冲-put()]生产者-0写入数据,j=1
[(消费者-0)CONSUMER]生产者-2写入数据,j=1
[(消费者-3)CONSUMER]生产者-0写入数据,j=1

  对于生产者和消费者模型的实现,除了多线程基础实现之外,也可以采用以上的模式利用LockCondition进行精确控制。

猜你喜欢

转载自www.cnblogs.com/itermis/p/8986899.html