Java多线程编程核心技术--第4章 lock的使用

4.1 使用ReentrantLock类

4.1.1 使用ReentrantLock实现同步

class MyService {
    //锁
    private Lock lock = new ReentrantLock();
    public void testMethod(){
        lock.lock();
        for(int i=0;i < 5; i++){
            System.out.println("thread name="+Thread.currentThread().getName());
        }
        lock.unlock();
    }
}

class MyThread extends Thread {
    private MyService service;

    public MyThread(MyService service){
        super();
        this.service = service;
    }

    public void run(){
        service.testMethod();
    }
}

public class Run {
    public static void main(String[] args){
        MyService service = new MyService();
        MyThread a = new MyThread(service);
        MyThread b = new MyThread(service);
        a.start();
        b.start();
    }
}

调用lock.lock()时,线程就有了“对象监视器”,其他线程只能等待锁被释放时再争抢。

4.1.3 使用Condition实现等待/通知

synchronized和wait(),notify(),notifyAll()结合实现等待/通知模式。
借助于Condition对象也可以实现。
一个Lock对象里面可以创建多个Condition(对象监视器)实例,线程对象可注册在指定的Condition中,选择性进行线程通知。

在synchronized中,通知是由JVM随机选择的,无法选择。
synchronized相当于整个Lock对象只有一个单一的Condition对象,所有线程都注册在该对象上。没有线程的通知选择,效率会出问题。

lock用来获取对象锁,不加的话Condition方法调用报错

public class Test {
    public static void main(String[] args) throws Exception {
        Service service = new Service();
        ThreadA a = new ThreadA(service);
        a.start();
        Thread.sleep(3000);
        service.signal();
    }
}

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

    public void await(){
        try{
            lock.lock();    //lock用来获取对象锁,不加的话Condition方法调用报错
            System.out.println("await time="+System.currentTimeMillis());
            condition.await();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void signal(){
        try {
            lock.lock();
            System.out.println("signal time="+System.currentTimeMillis());
            condition.signal();
        } finally {
            lock.unlock();
        }
    }
}

class ThreadA extends Thread {
    private Service service;
    public ThreadA(Service service){
        super();
        this.service = service;
    }

    public void run(){
        service.await();
    }
}

4.1.4 唤醒全部线程

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

    public void awaitA(){
        try{
            lock.lock();
            System.out.println("begin awaitA时间为: "+System.currentTimeMillis() + "Thread name="+Thread.currentThread().getName());
            condition.await();
            System.out.println("end awaitA时间为: "+System.currentTimeMillis() + "Thread name="+Thread.currentThread().getName());
        } catch(InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void awaitB(){
        try{
            lock.lock();
            System.out.println("begin awaitB时间为: "+System.currentTimeMillis() + "Thread name="+Thread.currentThread().getName());
            condition.await();
            System.out.println("end awaitB时间为: "+System.currentTimeMillis() + "Thread name="+Thread.currentThread().getName());
        } catch(InterruptedException e) {
            e.printStackTrace();
        } finally {

        }
    }

    public void signalAll() {
        try{
            lock.lock();
            System.out.println("signalAll 时间为: "+System.currentTimeMillis() + "Thread name="+Thread.currentThread().getName());
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
}

class ThreadA extends Thread {
    private MyService service;

    public ThreadA(MyService service){
        this.service = service;
    }
    public void run(){
        service.awaitA();
    }
}

class ThreadB extends Thread {
    private MyService service;

    public ThreadB(MyService service){
        this.service = service;
    }
    public void run(){
        service.awaitB();
    }
}

public class Run {
    public static void main(String[] args){
        MyService service = new MyService();
        ThreadA a = new ThreadA(service);
        a.setName("A");
        a.start();
        ThreadB b = new ThreadB(service);
        b.setName("B");
        b.start();
        Thread.sleep(3000);
        service.signalAll();
    }
}

A和B线程都被唤醒了

4.1.5 只唤醒部分线程,自己选择

需要使用多个Condition对象,Condition对象可唤醒部分指定线程,有助于提升程序运行效率。可先对线程进行分组,再唤醒指定组中的线程。

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

    public void awaitA(){
        try{
            lock.lock();
            System.out.println("begin awaitA时间为: "+System.currentTimeMillis() + "Thread name="+Thread.currentThread().getName());
            conditionA.await();
            System.out.println("end awaitA时间为: "+System.currentTimeMillis() + "Thread name="+Thread.currentThread().getName());
        } catch(InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void awaitB(){
        try{
            lock.lock();
            System.out.println("begin awaitB时间为: "+System.currentTimeMillis() + "Thread name="+Thread.currentThread().getName());
            conditionB.await();
            System.out.println("end awaitB时间为: "+System.currentTimeMillis() + "Thread name="+Thread.currentThread().getName());
        } catch(InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void signalAll_A() {
        try{
            lock.lock();
            System.out.println("signalAll_A 时间为: "+System.currentTimeMillis() + "Thread name="+Thread.currentThread().getName());
            conditionA.await();
        } finally {
            lock.unlock();
        }
    }

    public void signalAll_B() {
        try{
            lock.lock();
            System.out.println("signalAll_B 时间为: "+System.currentTimeMillis() + "Thread name="+Thread.currentThread().getName());
            conditionB.await();
        } finally {
            lock.unlock();
        }
    }
}

public class Run {
    public static void main(String[] args){
        MyService service = new MyService();
        ThreadA a = new ThreadA(service);
        a.setName("A");
        a.start();
        ThreadB b = new ThreadB(service);
        b.setName("B");
        b.start();
        Thread.sleep(3000);
        service.signalAll_A();
    }
}

可见只有线程A被唤醒了

4.1.7 生产/消费模式:一对一

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

    public void set(){
        try{
            lock.lock();
            while(hasValue == true){
                condition.await();
            }
            System.out.println("打印1");
            hasValue = true;
            condition.signal();
        } catch(InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void get(){
        try{
            lock.lock();
            while(hasValue == false){
                condition.await();
            }
            System.out.println("打印2");
            hasValue = false;
            condition.signal();
        } catch(InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

class ThreadA extends Thread {
    private MyService service;

    public ThreadA(MyService service){
        super();
        this.service = service;
    }

    public void run(){
        for(int i=0;i<Integer.MAX_VALUE;i++){
            service.set();
        }
    }
}

class ThreadB extends Thread {
    private MyService service;

    public ThreadB(MyService service){
        super();
        this.service = service;
    }

    public void run(){
        for(int i=0;i<Integer.MAX_VALUE;i++){
            service.get();
        }
    }
}

public class Run {
    public static void main(String[] args){
        MyService service = new MyService();
        ThreadA a = new ThreadA(service);
        a.start();
        ThreadB b = new ThreadB(service);
        b.start();
    }
}

多对多。for循环批量创建线程
这里写图片描述

需要将set()和get()中的signal()改成signalAll()方法。保证不会出现假死问题。

4.1.9 公平锁和非公平锁

公平锁表示线程获得锁的顺序是按线程加锁的顺序来分配的,先进先出的顺序。
非公平锁就是一个获得锁的抢占机制,随机获取锁的,和公平锁不一样的就是先来的不一定先得到锁,这种方式可能造成某些线程一直拿不到锁,结果也就是不公平的。

class Service {
    private ReentrantLock lock;

    public Service(boolean isFair){
        super();
        lock = new ReentrantLock(isFair);
    }

    public void serviceMethod(){
        try{
            lock.lock();
            System.out.println("thread name="+Thread.currentThread().getName()+"获得锁了");
        }finally {
            lock.unlock();
        }
    }
}

class RunFair {
    public static void main(String[] args) {
        final Service service = new Service(true);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("**线程"+Thread.currentThread().getName()+"运行了");
                service.serviceMethod();
            }
        };
        Thread[] array = new Thread[10];
        for(int i=0;i<10;i++){
            array[i] = new Thread(runnable);
        }
        for(int i=0;i<10;i++){
            array[i].start();
        }
    }
}

打印结果基本呈有序状态,这是公平锁的特点。

如果非公平锁new ReentrantLock(false)
运行结果基本是乱序的,说明先start启动的线程不代表先获得锁。

4.1.10 getHolaCount(),getQueueLength(),getWaitQueueLnegh

1.int getHoldCount()作用
查询当前线程保持此锁定的个数,调用lock()方法的次数。

class Service {
    private ReentrantLock lock = new ReentrantLock();

    public void serviceMethod1(){
        try{
            lock.lock();
            System.out.println("serviceMethod1 count="+lock.getHoldCount());
            serviceMethod2();
        }finally{
            lock.unlock();
        }
    }

    public void serviceMethod2(){
        try{
            lock.lock();
            System.out.println("serviceMethod2 count="+lock.getHoldCount());
        }finally{
            lock.unlock();
        }
    }
}

public class Run{
    public static void main(String[] args){
        Service service = new Service();
        service.serviceMethod1();
    }
}

/*
serviceMethod1 count=1
serviceMethod1 count=2
*/

2.int getQueueLength()作用
返回正等待获取此锁定的线程估计数,比如:
5个线程,1个线程先执行await()方法,调用该方法后返回值是4,说明4个线程在等待lock的释放。

class Service {
    public ReentrantLock lock = new ReentrantLock();

    public void serviceMethod1(){
        try{
            lock.lock();
            System.out.println("thread name="+Thread.currentThread().getName()+"进入方法!");
            Thread.sleep(Integer.MAX_VALUE);
        }catch(InterruptedException e){
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    }
}

public class Run {
    public static void main(String[] args){
        final Service service = new Service();
        Runnable runnable = new Runnable(){
            @Override
            public void run(){
                service.serviceMethod1();
            }
        };
        Thread[] array = new Thread[10];
        for(int i=0;i<10;i++){
            array[i] = new Thread(runnable);
        }
        for(int i=0;i<10;i++){
            array[i].start();
        }
        Thread.sleep(2000);
        System.out.println("有线程数:"+service.lock.getQueueLength()+"在等待获取锁!");
    }
}

3.int getWaitQueueLength(Condition condition)作用
作用是返回等待与此锁定相关的给定条件Condition的线程估计数。
例如:5个线程,每个线程都执行了同一个condition对象的await()方法,调用该方法返回值是5.

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

    public void waitMethod(){
        try{
            lock.lock();
            condition.await();
        }catch(InterruptedException e){
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    }

    public void notifyMethod(){
        try{
            lock.lock();
            System.out.println("有"+lock.getWaitQueueLength(condition)+"个线程正在等待Condition");
        } finally {
            lock.unlock();
        }
    }
}

public class Run {
    public static void main(String[] args) throws InterruptedException {
        final Service service = new Service();
        Runnable runnable = new Runnable(){
            @Override
            public void run(){
                service.waitMethod();
            }
        };
        Thread[] array = new Thread[10];
        for(int i=0;i<10;i++){
            array[i] = new Thread(runnable);
        }
        for(int i=0;i<10;i++){
            array[i].start();
        }
        Thread.sleep(2000);
        service.notifyMethod();
    }
}
/*
有10个线程正在等待Condition
*/

4.1.11 hasQueuedThread(),hasQueuedThreads(),hasWaiters()

1.boolean hasQueuedThread(Thread thread)、boolean hasQueuedThreads()作用
查询指定的线程是否正在等待获得此锁定

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

    public void waitMethod(){
        try{
            lock.lock();
            Thread.sleep(Integer,MAX_VALUE);
        }catch(InterruptedException e){
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    }
}

public class Run {
    public static void main(String[] args) throws InterruptedException {
        final Service service = new Service();
        Runnable runnable = new Runnable(){
            @Override
            public void run(){
                service.waitMethod();
            }
        };
        Thread a = nnew Thread(runnable);
        a.start();
        Thread.sleep(500);
        Thread b = nnew Thread(runnable);
        b.start();
        Thread.sleep(500);
        System.out.println(service.lock.hasQueuedThread(a));
        System.out.println(service.lock.hasQueuedThread(b));
        System.out.println(service.lock.hasQueuedThreads());
    }
}

2.boolean hasWaiters(Condition condition)作用
查询是否有线程正在等待与此锁定有关的condition条件。

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

    public void waitMethod(){
        try{
            lock.lock();
            condition.await();
        }catch(InterruptedException e){
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    }

    public void notifyMethod(){
        try{
            lock.lock();
            System.out.println("有没有线程正等待condition?"+lock.hasWaiters(condition)+"线程数是多少?"+lock.getWaitQueueLength(condition));
        }finally{
            lock.unlock();
        }
    }
}

4.1.12 isFair(),isHeldByCurrentThread(),isLocked()测试

1.boolean isFair()
判断是否是公平锁

class Service {
    private ReentrantLock lock;

    public Service(boolean isFair){
        super();
        lock = new ReentrantLock(isFair);
    }

    public void serviceMethod(){
        try{
            lock.lock();
            System.out.println("是不是公平锁"+lock.isFair());
        } finally {
            lock.unlock();
        }
    }
}

public class Run {
    public static void main(String[] args) throws InterruptedException {
        final Service service1 = new Service(true);
        Runnable runnable = new Runnable(){
            @Override
            public void run(){
                service1.serviceMethod();
            }
        };
        Thread a = nnew Thread(runnable);
        a.start();

        final Service service2 = new Service(true);
        Runnable runnable = new Runnable(){
            @Override
            public void run(){
                service2.serviceMethod();
            }
        };
        Thread b = nnew Thread(runnable);
        b.start();
    }
}

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

2.boolean isHeldByCurrentThread()
作用是查询当前线程是否保持此锁定。

class Service{
    private ReentrantLock lock;

    public Service(boolean isFair){
        super();
        lock = new ReentrantLock(isFair);
    }

    public void serviceMethod(){
        try{
            System.out.println(lock.isHeldByCurrentThread());
            lock.lock();
            System.out.println(lock.isHeldByCurrentThread());
        } finally {
            lock.unlock();
        }
    }
}

public class Run {
    public static void main(String[] args) throws InterruptedException {
        final Service service1 = new Service(true);
        Runnable runnable = new Runnable(){
            @Override
            public void run(){
                service1.serviceMethod();
            }
        };
        Thread a = nnew Thread(runnable);
        a.start();
    }
}

3.boolean isLocked()
查询此锁定是否由任意线程保持

class Service{
    private ReentrantLock lock;

    public Service(boolean isFair){
        super();
        lock = new ReentrantLock(isFair);
    }

    public void serviceMethod(){
        try{
            System.out.println(lock.isLocked());
            lock.lock();
            System.out.println(lock.isLocked());
        } finally {
            lock.unlock();
        }
    }
}

public class Run {
    public static void main(String[] args) throws InterruptedException {
        final Service service1 = new Service(true);
        Runnable runnable = new Runnable(){
            @Override
            public void run(){
                service1.serviceMethod();
            }
        };
        Thread a = nnew Thread(runnable);
        a.start();
    }
}

4.1.13 lockInterruptibly(),tryLock()

1.void lockInterruptibly()
当前线程未被中断,则获取锁定,如果已经被中断则出现异常。

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

    public void waitMethod(){
        try{
            lock.lock();    //lock.lockInterruptibly();
            System.out.println("lock begin"+Thread.currentThread().getName());
            for(int i=0; i<Integer.MAX_VALUE/10; i++){
                String str = new String();
                Nath.random();
            }
            System.out.println("lock end"+Thread.currentThread().getName());
        } finally {
            if(lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }
    }
}

2.boolean tryLock()
仅在调用时锁未被另一个线程持有的情况下,才获取该锁。

class Service{
    public ReentrantLock lock = new ReentrantLock();

    public void waitMethod(){
        if(lock.tryLock()){
            System.out.println(Thread.currentThread().getName()+"获得锁");
        } else {
            System.out.println(Thread.currentThread().getName()+"没有获得锁");
        }
    }
}

public class Run{
    public static void main(String[] args) throws InterruptedException {
        final Service service = new Service(true);
        Runnable runnable = new Runnable(){
            @Override
            public void run(){
                service.serviceMethod();
            }
        };
        Thread a = nnew Thread(runnable);
        a.setName("A");
        a.start();
        Thread b = nnew Thread(runnable);
        b.setName("A");
        b.start();
    }
}
/*
A获得锁
B没有获得锁
*/

3.boolean tryLock(long timeout,TimeUnit unit)
作用是,如果锁在给定等待时间内没被另一个线程持有,且当前线程未被中断,就获得该锁。

4.1.16 使用Condition实现顺序执行

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

    public static void main(String[] args) throws Exception {
        Thread threadA = new Thread() {
            @Override
            public void run() {
                try {
                    lock.lock();
                    while (nextPrintWho != 1) {
                        conditionA.await();
                    }
                    for (int i = 0; i < 3; i++) {
                        System.out.println("ThreadA" + (i + 1));
                    }
                    nextPrintWho = 2;
                    conditionB.signalAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        };
        Thread threadB = new Thread() {
            @Override
            public void run() {
                try {
                    lock.lock();
                    while (nextPrintWho != 2) {
                        conditionB.await();
                    }
                    for (int i = 0; i < 3; i++) {
                        System.out.println("ThreadB" + (i + 1));
                    }
                    nextPrintWho = 3;
                    conditionC.signalAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        };
        Thread threadC = new Thread() {
            @Override
            public void run() {
                try {
                    lock.lock();
                    while (nextPrintWho != 3) {
                        conditionC.await();
                    }
                    for (int i = 0; i < 3; i++) {
                        System.out.println("ThreadC" + (i + 1));
                    }
                    nextPrintWho = 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();
        }
    }
}
/*
ThreadA1
ThreadA2
ThreadA3
ThreadB1
ThreadB2
ThreadB3
ThreadC1
ThreadC2
ThreadC3
ThreadA1
ThreadA2
ThreadA3
ThreadB1
ThreadB2
ThreadB3
ThreadC1
ThreadC2
ThreadC3
ThreadA1
ThreadA2
ThreadA3
ThreadB1
ThreadB2
ThreadB3
ThreadC1
ThreadC2
ThreadC3
ThreadA1
ThreadA2
ThreadA3
ThreadB1
ThreadB2
ThreadB3
ThreadC1
ThreadC2
ThreadC3
ThreadA1
ThreadA2
ThreadA3
ThreadB1
ThreadB2
ThreadB3
ThreadC1
ThreadC2
ThreadC3
*/

4.2 使用ReentrantReadWriteLock类

ReentrantLock具有完全互斥排他效果,即同一时间只有一个线程在执行ReentrantLock.lock()方法后面的任务。

虽然保证实例变量的线程安全,但效率很底下。

ReentrantReadWriteLock类,使用它可加快运行效率,在某些不需要操作实例变量的方法中,完全可用该读写锁来提升该方法的代码运行速度。

说明:在没有线程Thread进行写入操作时,多个线程读取都可以获得锁;而进行写入操作的Thread只有在获取写锁后才能进行写入。

4.2.1 ReentrantReadWriteLock的使用:读读共享/写写互斥

class Service {
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void read(){
        try{
            try{
                lock.readLock.lock();   //读锁
                //lock.writeLock().lock(); 这个是写锁,互斥的
                System.out.println("获得读锁"+Thread.currentThread().getName());
                Thread.sleep(10000);
            } finally {
                lock.readLock().unlock();
            }
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
}

class ThreadA extends Thread {
    private Service service;

    public ThreadA(Service service){
        super();
        this.service = service;
    }

    public void run(){
        service.read();
    }
}

public class Run {
    public static void main(String[] args){
        Service service = new Service();
        ThreadA a = new ThreadA(service);
        a.setName("A");
        ThreadB b = new ThreadB(service);
        b.setName("B");
        a.start();
        b.start();
    }
}

猜你喜欢

转载自blog.csdn.net/a464700300/article/details/80335603