美文网首页程序员Java 学习日志
七:重入锁(锁中断,等待限时)

七:重入锁(锁中断,等待限时)

作者: 知耻而后勇_zjh | 来源:发表于2017-08-03 11:42 被阅读0次

    重入锁可以替代synchronized关键字,在JDK 5.0的以前版本中,重入锁的性能远远好于synchronized ,从6.0开始,JDK在synchronized上做了大量优化,使两者的性能差别并不大.

    public class ReenterLock implements Runnable{
        public static ReentrantLock lock = new ReentrantLock();
        public static int i = 0;
        @Override
        public void run() {
            for (int j = 0; j < 10000000; j++) {
                lock.lock();
                try {
                    i++;
                }finally {
                    lock.unlock();
                }
            }
        }
    
        public static void main(String[] args) throws Exception{
            ReenterLock rt = new ReenterLock();
            Thread t1 = new Thread(rt);
            Thread t2 = new Thread(rt);
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            System.out.println(i);
        }
    }
    

    从代码中可以看出,重入锁可以显示的对代码块进行加锁,释放锁,这样会比synchronized更加灵活,但是需要注意得是,用ReentrantLock 必须手动释放锁.

    重入锁的意义: 同一个线程可以两次获取同一把锁,如果不是重入锁,会产生死锁的情况.
    在以下程序中,子类改写了父类的 synchronized 方法,然后调用父类中的方法,此时如果内置锁不是可重入的,等待一个永远等不到的锁,那么这段代码将产生死锁.

    public class Widget{
        public synchronized void doSomething(){
            ........
        }
    }
    
    public class LoggingWidget extends Widget{
        public synchronized void doSomething(){
            super.doSomething();
        }
    }
    

    重入锁的高级功能:

    • 中断响应
      对于synchronized来说,如果一个线程正在证带锁,那么结果只有两种情况,要么得到锁继续执行,要么它就保持等待. 而使用重入锁,则提供另外一种可能,那就是线程可以被中断. 这种情况可以解决一般情况下的死锁问题.
    public class DeadLock implements Runnable{
    
        public static ReentrantLock lock1 = new ReentrantLock();
    
        public static ReentrantLock lock2 = new ReentrantLock();
    
        int lock;
    
        public DeadLock(int lock) {
            this.lock = lock;
        }
    
        @Override
        public void run() {
            try {
                if (lock == 1){
                    lock1.lockInterruptibly();
                    try {
                        Thread.sleep(500);
                    }catch (InterruptedException e){}
                        lock2.lockInterruptibly();
    
                }else {
                    lock2.lockInterruptibly();
                    try {
                        Thread.sleep(500);
                    }catch (InterruptedException e){}
                        lock1.lockInterruptibly();
    
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }finally {
                if (lock1.isHeldByCurrentThread())
                    lock1.unlock();
                if (lock2.isHeldByCurrentThread())
                    lock2.unlock();
                System.out.println(Thread.currentThread().getId());
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            DeadLock deadLock1 = new DeadLock(1);
            DeadLock deadLock2 = new DeadLock(2);
            Thread t1 = new Thread(deadLock1);
            Thread t2 = new Thread(deadLock2);
            t1.start();
            t2.start();
            Thread.sleep(1000);
            t2.interrupt();
    
        }
    }
    

    在上面程序中,如果t1 先占用lock1 在占用lock2,t2 先占用lock2 再占用lock1,很容易发生 : 在t1去占用lock2时,lock2还没有被释放,或者t2去占用lock1时,lock1还没有被释放.这就会造成等待现象.但是在程序最后,将t2线程终止,此时t2线程接收到命令后会中断自己,然后t2释放锁,由t1占用,就会解决死锁问题.

    • 限时等待
      通常,一个线程拿不到锁,可能是因为死锁,也可能是因为饥饿,但是如果给定一个等待时间,让线程自动放弃,就会解决这个问题.
      我们可以用tryLock()方法对线程进行限时等待的限制.

    tryLock()方法有两个参数,第一个是等待时长,第二个是计时单位,如果超过给定时间还没有得到锁就会返回false,如果拿到锁就会返回true. 如以下例子:

    public class TimeLock implements Runnable{
        public static ReentrantLock lock = new ReentrantLock();
        @Override
        public void run() {
            try {
                if (lock.tryLock(5, TimeUnit.SECONDS)){
                    Thread.sleep(6000);
                }else {
                    System.out.println("get lock failed");
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }finally {
                if (lock.isHeldByCurrentThread()){
                    lock.unlock();
                }
            }
        }
    
        public static void main(String[] args) {
            TimeLock timeLock = new TimeLock();
            Thread t1 = new Thread(timeLock);
            Thread t2 = new Thread(timeLock);
            t1.start();
            t2.start();
        }
    }
    

    当t1 或者 t2其中一个线程拿到锁以后,会占用锁6秒钟,所以第二个线程会在五秒内尝试获得失败.避免了线程等待.
    lock.tryLock()也可以用不传递人和参数,代表如果锁没有被其他线程占用就会返回true,反之返回false.

    相关文章

      网友评论

        本文标题:七:重入锁(锁中断,等待限时)

        本文链接:https://www.haomeiwen.com/subject/jykylxtx.html