第四章——Lock的使用

ReentrantLock类

ReentrantLock类能达到synchronized关键字的效果,且它的扩展功能更多。

lock()方法与unlock()方法

通过ReentrantLock对象lock()方法获得锁,unlock()方法释放锁。

public class myTask {
    private Lock lock = new ReentrantLock();
    public void testMethod() {
        lock.lock();
        for(int i = 0; i < 5; i++) {
            System.out.println("线程:" + Thread.currentThread().getName());
        }
        lock.unlock();
    }
}

public class myThread extends Thread {
    private myTask task;
    public myThread(myTask task) {
        this.task = task;
    }

    @Override
    public void run() {
        task.testMethod();
    }
}

public class Run {
    public static void main(String[] args) {
        myTask task = new myTask();
        myThread a1 = new myThread(task);
        myThread a2 = new myThread(task);
        myThread a3 = new myThread(task);

        a1.start(); a2.start(); a3.start();
    }
}

 

Condition实现等待/通知

借助Condition对象,ReentrantLock类可以实现等待/通知。在一个Lock对象里,可以创建多个Condition实例。

public class myTask {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void await() {
        try {
            lock.lock();
            System.out.println("等待时间为:" + System.currentTimeMillis());
            condition.await();
        }catch (InterruptedException e ){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void signal() {
        try {
            lock.lock();
            System.out.println("通知时间为:" + System.currentTimeMillis());
            condition.signal();
            System.out.println("B");
        }finally {
            lock.unlock();
        }
    }
}

public class ThreadA extends Thread {
    private myTask task;
    public ThreadA(myTask task) {
        this.task = task;
    }

    @Override
    public void run() {
        task.await();
    }
}

public class Run {
    public static void main(String[] args) throws InterruptedException{
        myTask task = new myTask();
        ThreadA a1 = new ThreadA(task);
        Thread.sleep(3000);
        task.signal();
        a1.start();
    }
}

 

与notify()/notifyAll()方法相比,可以通过使用多个Condition实现”选择性通知“。

public class myTask {
    private Lock lock = new ReentrantLock();
    private Condition conditionA = lock.newCondition();
    private Condition conditionB = lock.newCondition();

    public void awaitA() {
        try {
            lock.lock();
            System.out.println("A等待时间为:" + System.currentTimeMillis());
            conditionA.await();
        }catch (InterruptedException e ){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void awaitB() {
        try {
            lock.lock();
            System.out.println("B等待时间为:" + System.currentTimeMillis());
            conditionB.await();
        }catch (InterruptedException e ){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void signalB() {
        try {
            lock.lock();
            System.out.println("B通知时间为:" + System.currentTimeMillis());
            conditionB.signalAll();
        }finally {
            lock.unlock();
        }
    }

    public void signalA() {
        try {
            lock.lock();
            System.out.println("A通知时间为:" + System.currentTimeMillis());
            conditionA.signalAll();
        }finally {
            lock.unlock();
        }
    }
}

public class ThreadA extends Thread {
    private myTask task;
    public ThreadA(myTask task) {
        this.task = task;
    }

    @Override
    public void run() {
        task.awaitA();
    }
}

public class ThreadB  extends Thread {
    private myTask task;
    public ThreadB(myTask task) {
        this.task = task;
    }

    @Override
    public void run() {
        task.awaitB();
    }
}

public class Run {
    public static void main(String[] args) throws InterruptedException{
        myTask task = new myTask();
        ThreadA ta = new ThreadA(task);
        ThreadB tb = new ThreadB(task);
        ta.start();     tb.start();
        Thread.sleep(3000);
        task.signalA();
    }
}

 

输出:

 

生产者/消费者模式:多对多

public class myTask {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private boolean hasValue = false;

    public void setValue() {
        try {
            lock.lock();
            while(hasValue == true) {
                System.out.println("生产者进入睡眠");
                condition.await();
            }
            System.out.println("打印");
            hasValue = true;
            condition.signalAll();
        }catch (InterruptedException e ){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void getValue() {
        try {
            lock.lock();
            while(hasValue == false) {
                System.out.println("消费者进入睡眠");
                condition.await();
            }
            System.out.println("消费");
            hasValue = false;
            condition.signalAll();
        }catch (InterruptedException e ){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}


public class ThreadA extends Thread {
    private myTask task;
    public ThreadA(myTask task) {
        this.task = task;
    }

    @Override
    public void run() {
        task.setValue();
    }
}

public class ThreadB  extends Thread {
    private myTask task;
    public ThreadB(myTask task) {
        this.task = task;
    }

    @Override
    public void run() {
        task.getValue();
    }
}

public class Run {
    public static void main(String[] args) throws InterruptedException{
        myTask task = new myTask();
        ThreadA[] ta = new ThreadA[10];
        ThreadB[] tb = new ThreadB[10];
        for(int i = 0; i < 10; i++) {
            ta[i] = new ThreadA(task);
            tb[i] = new ThreadB(task);
            ta[i].start();          tb[i].start();
        }
    }
}

 

公平锁与非公平锁

锁Lock分为公平锁和非公平锁,公平锁即先来先得的顺序;而非公平锁则是随机的,可能造成某些线程一直拿不到锁。

默认情况下,ReentrantLock类使用的是非公平锁。

Lock lock = new ReentrantLock(true);
//false 为非公平

先运行的线程先拿到锁。

非公平锁

 

相关方法

int getHoldCount()

是查询当前线程保存锁定的个数

ReentrantLock lock = new ReentrantLock();
lock.getHoldCount();

int getQueueLength()

返回正等待获取此锁定的线程估计数。比如有5个线程,1个线程执行了await()方法,则在调用该方法返回的是4,即有4个线程同时等待lock的释放。

ReentrantLock lock = new ReentrantLock();
​lock.getQueueLength()

int getWaitQueueLength(Condition condition)

返回等待与Condition相关的线程估计数。比如有5个线程都执行了同一Condition对象的await()方法,则其方法返回5。

ReentrantLock lock = new ReentrantLock();
lock.getWaitQueueLength(condition)

boolean hasQueuedThread(Thread thread)

查询指定的线程是否正等待获取此锁。

ReentrantLock lock = new ReentrantLock();
lock.hasQueuedThread();

boolean hasQueuedThreads() 则是查询是否有线程正在等待获取锁

boolean hasWaiters(Condition condition)

查询是否有线程正在等待与这个锁相关的condition条件

ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.hasWaiters(condition);

boolean is Fair()

判断是否为公平锁

boolean isHeldByCurrentThread()

查询当前线程是否是否已上锁

System.out.println(lock.isHeldByCurrentThread());   //false
lock.lock();
System.out.println(lock.isHeldByCurrentThread());   //true

boolean isLocked()

查询此锁是否由任意一个线程保持

System.out.println(lock.isLocked());    //false
lock.lock();
System.out.println(lock.isLocked());    //true

void lockInterruptibly()

若当前线程未被中断,则获取此锁;若中断则出现异常。

一个线程在上锁时,如果用的是lock()方法,那么当它被中断时,该线程不会出现异常;如果是lockInterruptibly(),则会抛出InterrupterException异常。

boolean tryLock()

当该方法被调用时,若该锁没被其他线程拿到,则该所被当前线程获取。

if(lock.tryLock()) {
    System.out.println(Thread.currentThread().getName() + "拿到锁");
}else {
     System.out.println(Thread.currentThread().getName() + "没有拿到锁");
}

boolean tryLock(long timeout, TimeUnit unit)

若该锁在给定时间内没被其他线程拿到,且当前线程x未被中断,则x拿到锁。

if(lock.tryLock(3, TimeUnit.SECONDS)) {
    System.out.println(Thread.currentThread().getName() + "拿到锁");
}else {
     System.out.println(Thread.currentThread().getName() + "没有拿到锁");
}

void awaitUninterruptibly()

该方法与await()大致相同。当线程在lock()后,进入await()后,若此时线程被interrupt(),则会抛出异常;而awaitUninterruptibly()则不会响应异常。

condition.await();
condition.awaitUninterruptibly();

boolean awaitUntil(Date deadline)

线程在Date 时间内等待。

public class myTask {
    public ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void waitMethod() {
        try {
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.SECOND, 10);      // 设置等待时间10s
            lock.lock();
            System.out.println("开始等待:" +System.currentTimeMillis());
            condition.awaitUntil(calendar.getTime());
            System.out.println("结束等待:" +System.currentTimeMillis());
        }catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void notifyMethod() {
        try {
            lock.lock();
            System.out.println("开始通知:" +System.currentTimeMillis());
            condition.signalAll();
            System.out.println("结束通知:" +System.currentTimeMillis());
        }finally {
            lock.unlock();
        }
    }
}

public class Run {
    public static void main(String[] args) throws InterruptedException{
        myTask task = new myTask();
        Runnable waitRun = new Runnable() {
            @Override
            public void run() {
                task.waitMethod();;
            }
        };

        Runnable notifyRun = new Runnable() {
            @Override
            public void run() {
                task.notifyMethod();
            }
        };
        Thread waitThread = new Thread(waitRun);
        waitThread.start();
//        Thread.sleep(2000);
//        Thread notifyThread = new Thread(notifyRun);
//        notifyThread.start();

    }
}

输出:

线程在10后唤醒。取消掉三行的注释,则输出为:

可以发现,在等待时间到达前,可以被其他线程提前唤醒。

 

使用Condition实现顺序执行

通过condition对象可以线程执行的任务进行排序规划

public class Run {
    volatile private static int nextTaskNum = 1;
    private static ReentrantLock lock = new ReentrantLock();
    private static Condition conditionA = lock.newCondition();
    private static Condition conditionB = lock.newCondition();
    private static Condition conditionC = lock.newCondition();

    public static void main(String[] args) throws InterruptedException{
       Thread threadA = new Thread() {
           @Override
           public void run() {
               try {
                   lock.lock();
                   while(nextTaskNum != 1) {
                       conditionA.await();
                   }
                   for(int i = 0; i < 3; i++) {
                       System.out.println("A:" + i);
                   }
                   nextTaskNum = 2;
                   conditionB.signalAll();
               }catch (InterruptedException e) {
                   e.printStackTrace();
               }finally {
                   lock.unlock();
               }
           }
       };

        Thread threadB = new Thread() {
            @Override
            public void run() {
                try {
                    lock.lock();
                    while(nextTaskNum != 2) {
                        conditionB.await();
                    }
                    for(int i = 0; i < 3; i++) {
                        System.out.println("B:" + i);
                    }
                    nextTaskNum = 3;
                    conditionC.signalAll();
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                }
            }
        };

        Thread threadC = new Thread() {
            @Override
            public void run() {
                try {
                    lock.lock();
                    while(nextTaskNum != 3) {
                        conditionC.await();
                    }
                    for(int i = 0; i < 3; i++) {
                        System.out.println("C:" + i);
                    }
                    nextTaskNum = 1;
                    conditionA.signalAll();
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                }
            }
        };

        Thread[] aArray = new Thread[5];
        Thread[] bArray = new Thread[5];
        Thread[] cArray = new Thread[5];
        for(int i = 0; i < 5; i++) {
            aArray[i] = new Thread(threadA);
            bArray[i] = new Thread(threadB);
            cArray[i] = new Thread(threadC);

            aArray[i].start();
            bArray[i].start();
            cArray[i].start();
        }
    }
}

 


ReentrantReadWriteLock类

ReentrantLock保证了同一时间内只有一个线程在执行lock()后面的任务,这样虽然保证的变量的线程安全性,但效率低下。而ReentrantReadWriteLock类可以加快运行效率,在某些不需要操作实例变量的方法中,完全可以使用读写锁ReentrantReadWriteLock来提升运行速度。

读写锁有两个锁:读操作相关的锁,也叫共享锁;另一个是写操作相关的锁,也叫排他锁。多个读锁不互斥,即进行读操作的多个线程都可以获取读锁;读锁与写锁互斥,即同一时刻只允许一个线程进行写操作。

运行下面代码,可以发现两个线程几乎同时进入lock()后面的代码。

public class myTask {
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void read() {
        try {
            try {
                lock.readLock().lock();         // 写锁为: lock.writeLock().lock()
                System.out.println(Thread.currentThread().getName() +" 获得读锁,时间:" +System.currentTimeMillis());
                Thread.sleep(10000);
            }finally {
                lock.readLock().unlock();       // 写锁为: lock.writeLock().unlock()
            }
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void write() {
        try {
            try {
                lock.writeLock().lock()
                System.out.println(Thread.currentThread().getName() +" 获得写锁,时间:" +System.currentTimeMillis());
                Thread.sleep(10000);
            }finally {
                lock.writeLock().unlock()
            }
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


public class ThreadA extends Thread {
    private myTask task;
    public ThreadA(myTask task) {
        this.task = task;
    }

    @Override
    public void run() {
        task.read();
    }
}

public class Run {
    public static void main(String[] args) {
        myTask task = new myTask();
        ThreadA ta = new ThreadA(task);
        ta.setName("ta");
        ThreadB tb = new ThreadB(task);
        tb.setName("tb");
        ta.start();             tb.start();
    }
}

public class ThreadB  extends Thread {
    private ReadWriteLock.myTask task;
    public ThreadB(myTask task) {
        this.task = task;
    }

    @Override
    public void run() {
        task.read();
    }
}

 

 

 

 

 

 

 

发布了17 篇原创文章 · 获赞 3 · 访问量 8471
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览