多线程学习笔记(六)--重入锁

重入锁

重入锁可以完全替代synchronized关键字,jdk5的早期版本中,重入锁的性能远远好于synchronized,在jdk6之后,jdk在synchronized上做了大量的优化,使得两者的性能差距并不大。

·重入锁简单使用


method1拿到锁之后执行业务代码,method2需要等待method1释放锁资源,从而保证了安全性

public class UseReentrantLock {
    private Lock lock = new ReentrantLock();

    public void method1(){
        try {
            lock.lock();
            System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method1...");
            Thread.sleep(1000);
            System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method1...");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void method2(){
        try {
            lock.lock();
            System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method2..");
            Thread.sleep(2000);
            System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method2..");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        final UseReentrantLock ur = new UseReentrantLock();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                ur.method1();
                ur.method2();
            }
        },"t1");

        t1.start();
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

执行的结果如下:

当前线程:t1进入method1...
当前线程:t1退出method1...
当前线程:t1进入method2..
当前线程:t1退出method2..

·中断响应


使用synchronized,对于一个线程在等待锁,要么就获得了锁,可以执行业务代码,要么就没获得到锁,从而必须等待。
使用重入锁,线程可以被中断,也就是在等待锁的过程中,程序可以根据需要取消对锁的请求
将上述方法改造,线程t1和线程t2会产生死锁,这时我们将t2中断,从而t1线程会正常执行:

public class UseReentrantLock {
    public static ReentrantLock lock1 = new ReentrantLock();
    public static ReentrantLock lock2 = new ReentrantLock();

    public void method1(){
        try {
            lock1.lockInterruptibly();
            System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method1...");
            Thread.sleep(1000);
            lock2.lockInterruptibly();
            System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method1...");

        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            if(lock1.isHeldByCurrentThread()){
                lock1.unlock();
            }
            if(lock2.isHeldByCurrentThread()){
                lock2.unlock();
            }

        }
    }

    public void method2(){
        try {
            lock2.lockInterruptibly();
            System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method2..");
            Thread.sleep(2000);
            lock1.lockInterruptibly();
            System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method2..");

        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            if(lock1.isHeldByCurrentThread()){
                lock1.unlock();
            }
            if(lock2.isHeldByCurrentThread()){
                lock2.unlock();
            }
        }
    }

    public static void main(String[] args) {
        final UseReentrantLock ur = new UseReentrantLock();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                ur.method1();
            }
        },"t1");

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                ur.method2();
            }
        },"t2");

        t1.start();
        t2.start();
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.interrupt();
    }
}

·锁申请等待限时


要避免死锁,另一种方法就是限时等待,在一定时间内还没有拿到锁,那就自动放弃,可以使用tryLock()进行限时等待:

public class TimeLock implements Runnable{
    public static ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        try {
            if(lock.tryLock(5, TimeUnit.SECONDS)){
            System.out.println(Thread.currentThread().getName() + ": get lock success");
                Thread.sleep(6000);
            }else{
              System.out.println(Thread.currentThread().getName() + ": get lock failed");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }

    }

    public static void main(String[] args) {
        TimeLock t1 = new TimeLock();
        Thread t = new Thread(t1);
        Thread t2 = new Thread(t1);
        t.start();
        t2.start();
    }
}

执行结果:

Thread-0: get lock success
Thread-1: get lock failed

线程t1获得锁成功后,线程会睡眠6s,线程t2再去获得锁时,在5s内t1线程 还未释放,所以t2线程获取锁失败。

·公平锁


一般锁的申请都是非公平的,当锁可用时,多个线程同时去争抢这个锁资源,那么哪个线程可以获得到锁是不确定的

public class FairLock implements Runnable{
    public static ReentrantLock fairLock = new ReentrantLock(true);

    @Override
    public void run() {
        while (true){
            try {
                fairLock.lock();
                System.out.println(Thread.currentThread().getName() + " 获得锁");
            } finally {
                fairLock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        FairLock r1 = new FairLock();
        Thread t1 = new Thread(r1,"t1");
        Thread t2 = new Thread(r1,"t2");
        t1.start();
        t2.start();

    }
}

当参数为true时,是公平锁,当参数为false时,是非公平锁,执行结果如下:

t1 获得锁
t2 获得锁
t1 获得锁
t2 获得锁
t1 获得锁
t2 获得锁
...

实现原理

重入锁是对一个线程来讲的,一个线程获取了锁之后,可以循环递归的获取同一个锁(计数+1),当释放一次锁时,即将计数-1,当减为0,会立即释放锁,其他线程才能获取
参考链接如下:https://blog.csdn.net/yanyan19880509/article/details/52345422

猜你喜欢

转载自blog.csdn.net/zh15732621679/article/details/80289765
今日推荐