程序员Java 学习日志

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

2017-08-03  本文已影响0人  知耻而后勇_zjh

重入锁可以替代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();
    }
}

重入锁的高级功能:

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()方法有两个参数,第一个是等待时长,第二个是计时单位,如果超过给定时间还没有得到锁就会返回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.

上一篇 下一篇

猜你喜欢

热点阅读