欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

08ReentrantLock

程序员文章站 2024-03-24 19:25:40
...

重入锁,手动加锁和释放锁,退出临界区时必须手动释放,否则其它线程无法访问资源了。

主要有一下几个特性:

1. 可重入

同一线程可以多次获得锁,但是在释放锁时,也要释放相同的次数,如果释放次数多,会得到一个IllegalMonitorStateException异常,如果释放次数少,相当于还持有锁。

public class Test01 {

    public static class LockTest implements Runnable{

        public static ReentrantLock lock = new ReentrantLock();

        public static  int i = 0;

        @Override
        public void run() {
            lock.lock();
            lock.lock();
            try {
                for (int j = 0; j < 10000; j++) {
                    i++;
                }
            }finally {
                lock.unlock();
                lock.unlock();
            }

        }
    }


    @Test
    public void test01() throws Exception{
        LockTest lock1 = new LockTest();
        LockTest lock2 = new LockTest();

        Thread t1 = new Thread(lock1);
        Thread t2 = new Thread(lock2);

        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(LockTest.i);
    }
}

2. 可中断

可中断 lockInterruptibly()方法,可重入锁在等待过程中可以被中断,而synchronized在等待锁只有两种情况,要么获得锁要么继续等待。

首先构造一个死锁,然后中断其中一个锁。

public class Test02 {

    public static class LockTest implements Runnable{

        public static ReentrantLock lock1 = new ReentrantLock();
        public static ReentrantLock lock2 = new ReentrantLock();
        int lock;
        public LockTest(int lock){
            this.lock  = lock;
        }

        @Override
        public void run() {
            try {
                if(lock == 1){
                    lock1.lockInterruptibly();
                    Thread.sleep(500);
                    lock2.lockInterruptibly();
                }else {
                    lock2.lockInterruptibly();
                    Thread.sleep(500);
                    lock1.lockInterruptibly();
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                if(lock1.isHeldByCurrentThread()){
                    lock1.unlock();
                }
                if(lock2.isHeldByCurrentThread()){
                    lock2.unlock();
                }
                System.out.println(Thread.currentThread().getId()+"线程退出");
            }
        }
    }

    @Test
    public void test01() throws Exception{
        LockTest r1 = new LockTest(1);
        LockTest r2 = new LockTest(2);

        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);

        t1.start();
        t2.start();
        Thread.sleep(2000);

        t1.interrupt();
    }

}

3. 可限时

除了等待外部通知,要避免死锁的另外一个方法:限时等待。tryLock(),若不加参数运行,锁被其它线程占用,不会等待,立即返回

public class Test03 {


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

        @Override
        public void run() {
            try {
                if(lock.tryLock(3000, TimeUnit.MILLISECONDS)) {
                    Thread.sleep(6000);
                }else {
                    System.out.println("get lock failed");
                }

            }catch (Exception e){
                e.printStackTrace();
            }finally {
                if(lock.isHeldByCurrentThread()){
                    lock.unlock();
                }
            }
        }
    }

    @Test
    public void test01() throws Exception{
        LockTest lockTest = new LockTest();

        Thread t1 = new Thread(lockTest);
        Thread t2 = new Thread(lockTest);

        t1.start();
        t2.start();

        t1.join();
        t2.join();
    }

}

4. 公平锁

大多数情况下,锁申请是不公平的,可以指定new ReentrantLock(true);实现公平锁获得锁时按照请求锁的顺序。

public class Test04 {

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

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

    @Test
    public void test01() throws Exception{
        TestLock testLock = new TestLock();

        Thread t1 = new Thread(testLock, "th1");
        Thread t2 = new Thread(testLock, "th2");
        Thread t3 = new Thread(testLock, "th3");

        t1.start();
        t2.start();
        t3.start();

        t1.join();
        t2.join();
        t3.join();

    }

}

5. 重要方法整理

lock()

lockInterruptibly()

tryLock()

unlock

6. 重入锁实现主要三个要素:

  1. 原子状态:原子状态使用cas操作来储存当前锁的状态,判断锁是否已经被别的线程持有。
  2. 等待队列:所有没有请求到锁的线程,会进入等待队列进行等待。待有线程释放锁后,系统就能从等待中唤醒一个线程,继续工作。
  3. 阻塞原语park()和unpark():用来挂起和恢复线程。没有得到锁的线程将会被挂起。

实时内容请关注微信公众号,公众号与博客同时更新:程序员星星

08ReentrantLock

相关标签: ReentrantLock

推荐阅读