ReentrantLock详解

java多线程中,ReentrantLock和synchronized关键字都可以实现线程之间的同步,但是ReentrantLock的功能比synchronized功能更加强大。

1、ReentrantLock是公平锁,非公平锁,可重入锁

默认是非公平锁,传入参数true是公平锁

公平锁:采用先到先得的策略,每次获取锁之前,都会检查队列里边有没有排队等待的线程,没有就会尝试获取锁,如果有就将当前线程添加到等待队列。

非公平锁: 采用有机会插队的策略,一个线程获取锁之前,先尝试获取锁,而不是在线程队列等待,如果获取锁成功,那么线程就成功启动,没有获取到锁,就到等待队列等待。

ReentrantLock lock new ReentrantLock(tag); 
public class Lock2Main {
    public static void main(String[] args) throws InterruptedException {
        testServiceA();
    }
    
    public static void testServiceA() throws InterruptedException {
        
        ServiceA serviceA = new ServiceA(true);

        ThreadA threadA = new ThreadA(serviceA);
        ThreadA threadb = new ThreadA(serviceA);

        threadA.start();
        threadb.start();

        Thread.sleep(100);
    }
}
    
class ThreadA extends Thread {

    private ServiceA serviceA;

    public ThreadA(ServiceA serviceA) {
        this.serviceA = serviceA;
    }

    @Override
    public void run() {
        serviceA.testMeth();
    }
}
    
    
class ServiceA {
    ReentrantLock lock;

    public ServiceA(boolean tag) {
        this.lock = new ReentrantLock(tag);
    }

    public void testMeth() {
        lock.lock();

        System.out.println(Thread.currentThread().getName() + "+++++上锁: testMeth()");

        testMeth1();

        System.out.println(Thread.currentThread().getName() + ": running");
        lock.unlock();
        System.out.println(Thread.currentThread().getName() + "-----解锁 testMeth()");
    }

    public void testMeth1() {
        lock.lock();

        System.out.println(Thread.currentThread().getName() + "+++++上锁: testMeth1()");

        System.out.println(Thread.currentThread().getName() + ": running");
        lock.unlock();
        System.out.println(Thread.currentThread().getName() + "-----解锁 testMeth1()");
    }


}

main方法执行结果

Thread-0+++++上锁: testMeth()
Thread-0+++++上锁: testMeth1()
Thread-0: running
Thread-0-----解锁 testMeth1()
Thread-0: running
Thread-0-----解锁 testMeth()
Thread-1+++++上锁: testMeth()
Thread-1+++++上锁: testMeth1()
Thread-1: running
Thread-1-----解锁 testMeth1()
Thread-1: running
Thread-1-----解锁 testMeth()

Process finished with exit code 0

2、ReentrantLock锁的释放,以及线程的唤醒

private ReentrantLock lock = new ReentrantLock();   // 加锁
private Condition condition = lock.newCondition();  // 定向唤醒线程

通过Condition来让当前线程等待(await方法),和通知线程就绪(signal方法),但是在在调用await方法之前必须获取锁,即调用lock.lock()方法,否则会抛出异常。

样例代码

public class Lock2Main {
    public static void main(String[] args) throws InterruptedException {
        testServiceC();
    }
    public static void testServiceC() throws InterruptedException {

        ServiceC ServiceC = new ServiceC();

        ThreadC threadC = new ThreadC(ServiceC);
        ThreadC1 threadC1 = new ThreadC1(ServiceC);

        threadC1.start();
        threadC.start();

        Thread.sleep(5000);

        ServiceC.singleA();
        ServiceC.singleB();

    }
}
class ThreadC extends Thread {

    private ServiceC serviceB;

    public ThreadC(ServiceC serviceB) {
        this.serviceB = serviceB;
    }

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

class ThreadC1 extends Thread {

    private ServiceC serviceB;

    public ThreadC1(ServiceC serviceB) {
        this.serviceB = serviceB;
    }

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

class ServiceC {
    private ReentrantLock lock = new ReentrantLock();
    private Condition condition_A = lock.newCondition();
    private Condition condition_B = lock.newCondition();

    public void awaitA() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "--begin await--" + System.currentTimeMillis());
            condition_A.await();
            System.out.println(Thread.currentThread().getName() + "--end await--" + System.currentTimeMillis());
        } catch (InterruptedException e) {

        } finally {
            lock.unlock();
        }
    }

    public void awaitB() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "--begin await--" + System.currentTimeMillis());
            condition_B.await();
            System.out.println(Thread.currentThread().getName() + "--end await--" + System.currentTimeMillis());
        } catch (InterruptedException e) {

        } finally {
            lock.unlock();
        }
    }

    public void singleA() {
        try {
            lock.lock();
            System.out.println("开始调用single方法--" + System.currentTimeMillis());
            condition_A.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public void singleB() {
        try {
            lock.lock();
            System.out.println("开始调用single方法--" + System.currentTimeMillis());
            condition_B.signalAll();
        } finally {
            lock.unlock();
        }
    }
}

main运行结果

Thread-0--begin await--1696480662130
Thread-1--begin await--1696480662130

开始调用single方法--1696480667134
开始调用single方法--1696480667134

Thread-1--end await--1696480667134
Thread-0--end await--1696480667134

Process finished with exit code 0

调用condition.await();会释放当前的锁。

//和notify与notifyAll方法一样
condition.signal();
condition.signalAll(); 

一个lock可以创建多个Condition,同一个condition的notify、notifyAll和wait也是搭配使用的,同一个condition.notify()的只能唤醒同一个condition的await()。

wait的一些特性:

wait是可以被中断的(interrupt方法),会抛出InterruptedException异常

condition.waitUninterruptibly(); 不会被打断,线程调用interrupt方法后,方法不会被打断,且不抛出InterruptedException异常

多个类似的wait方法

long awaitNanos(long nanosTimeout) throws InterruptedException;        //具有自动唤醒功能,单位纳秒
boolean await(long time, TimeUnit unit) throws InterruptedException;   //具有自动唤醒功能,可以指定时间单位
boolean awaitUntil(Date deadline) throws InterruptedException;         //在指定日期结束等待

3、ReentrantReadWriteLock-读写锁

ReentrantLock具有完全排他的特性,同一时间只有一个线程在执行lock方法后的代码,虽然保证了线程的安全,但是效率非常低。ReentrantReadWriteLock在执行读操作时可以同时读,而不执行同步操作。

读写锁有两个锁,读锁和写锁。

读读不互斥,读写互斥,写写互斥。

public class LockReadWrite {
    public static void main(String[] args) throws InterruptedException {

        System.out.println("写锁测试+++++++++");
        ServiceD serviceD = new ServiceD();
        ThreadD threadD = new ThreadD(serviceD);
        ThreadE threadD1 = new ThreadE(serviceD);


        threadD.start();
        threadD1.start();

        Thread.sleep(10000);
        System.out.println("读锁测试--------");

        ThreadD threadD2 = new ThreadD(serviceD);
        ThreadD threadD3 = new ThreadD(serviceD);

        threadD2.start();
        threadD3.start();

    }
}


class ThreadD extends Thread{
    ServiceD serviceD;

    public ThreadD(ServiceD serviceD){
        this.serviceD = serviceD;
    }

    @Override
    public void run() {
        serviceD.readContext();
    }
}

class ThreadE extends Thread{
    ServiceD serviceD;

    public ThreadE(ServiceD serviceD){
        this.serviceD = serviceD;
    }

    @Override
    public void run() {
        serviceD.writeLock();
    }
}

class ServiceD{

    ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void readContext(){
        try{
            lock.readLock().lock();
            System.out.println(Thread.currentThread().getName() + "正在读......" + System.currentTimeMillis());
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.readLock().unlock();
            System.out.println(Thread.currentThread().getName() + "读退出------" + System.currentTimeMillis());
        }
    }


    public void writeLock(){
        try{
            lock.writeLock().lock();
            System.out.println(Thread.currentThread().getName() + "正在写++++++" + System.currentTimeMillis());
            Thread.sleep(3000);
            System.out.println(Thread.currentThread().getName() + "写完毕------" + System.currentTimeMillis());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.writeLock().unlock();
        }
    }


}

输出结果:

写锁测试+++++++++
Thread-0正在读......1696479655992
Thread-0读退出------1696479659000
Thread-1正在写++++++1696479659000
Thread-1写完毕------1696479662008
读锁测试--------
Thread-2正在读......1696479665999
Thread-3正在读......1696479665999
Thread-2读退出------1696479669003
Thread-3读退出------1696479669003

Process finished with exit code 0

你可能感兴趣的:(java,java)