Lock

其实,Lock和synchronized的功能差很少,可是Lock比synchronized控制更细。spa

一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其余线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种状况:线程

  • 获取锁的线程执行完了该代码块,而后线程释放对锁的占有;
  • 线程执行发生异常,此时JVM会让线程自动释放锁。

那么若是这个获取锁的线程因为要等待IO或者其余缘由(好比调用sleep方法)被阻塞了,可是又没有释放锁,其余线程便只能干巴巴地等待,试想一下,这多么影响程序执行效率。code

所以就须要有一种机制能够不让等待的线程一直无期限地等待下去(好比只等待必定的时间或者可以响应中断),经过Lock就能够办到。blog

Lock接口只有六个方法。接口

可是其中4个方法来获取锁get

lock()

正常状况下这个就好了,用来获取锁,若是锁被其余线程获取,则等待。同步

因为在前面讲到若是采用Lock,必须主动去释放锁,而且在发生异常时,不会自动释放锁。所以通常来讲,使用Lock必须在try{}catch{}块中进行,而且将释放锁的操做放在finally块中进行,以保证锁必定被被释放,防止死锁的发生。一般使用Lock来进行同步的话,是如下面这种形式去使用的:it

public class Test {
    private ArrayList<Integer> arrayList = new ArrayList<Integer>();
    public static void main(String[] args)  {
        final Test test = new Test();
 
        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();
 
        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();
    }  
 
    public void insert(Thread thread) {
        Lock lock = new ReentrantLock();    //注意这个地方(局部变量),若是在类的属性中会怎样
        lock.lock();
        try {
            System.out.println(thread.getName()+"获得了锁");
            for(int i=0;i<5;i++) {
                arrayList.add(i);
            }
        } catch (Exception e) {
            // TODO: handle exception
        }finally {
            System.out.println(thread.getName()+"释放了锁");
            lock.unlock();
        }
    }
}

为啥会出如今锁未释放就获得锁这种状况。这里面Lock为局部变量,没调用一次,都会产生一个副本io

正确写法应该是class

public class Test {
    private ArrayList<Integer> arrayList = new ArrayList<Integer>();
    private Lock lock = new ReentrantLock();    //注意这个地方
    public static void main(String[] args)  {
        final Test test = new Test();

        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();

        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();
    }

    public void insert(Thread thread) {
        if(lock.tryLock()) {
            try {
                System.out.println(thread.getName()+"获得了锁");
                for(int i=0;i<5;i++) {
                    arrayList.add(i);
                }
            } catch (Exception e) {
                // TODO: handle exception
            }finally {
                System.out.println(thread.getName()+"释放了锁");
                lock.unlock();
            }
        } else {
            System.out.println(thread.getName()+"获取锁失败");
        }
    }
}

tryLock()

tryLock()方法是有返回值的,它表示用来尝试获取锁,若是获取成功,则返回true,若是获取失败(即锁已被其余线程获取),则返回false,也就说这个方法不管如何都会当即返回。在拿不到锁时不会一直在那等待。

public class Test {
    private ArrayList<Integer> arrayList = new ArrayList<Integer>();
    private Lock lock = new ReentrantLock();    //注意这个地方
    public static void main(String[] args)  {
        final Test test = new Test();
 
        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();
 
        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();
    }  
 
    public void insert(Thread thread) {
        if(lock.tryLock()) {
            try {
                System.out.println(thread.getName()+"获得了锁");
          Thread.sleep(1000);
for(int i=0;i<5;i++) { arrayList.add(i); } } catch (Exception e) { // TODO: handle exception }finally { System.out.println(thread.getName()+"释放了锁"); lock.unlock(); } } else { System.out.println(thread.getName()+"获取锁失败"); } } }

tryLock(long time, TimeUnit unit)

和tryLock()方法是相似的,只不过区别在于这个方法在拿不到锁时会等待必定的时间,在时间期限以内若是还拿不到锁,就返回false。若是若是一开始拿到锁或者在等待期间内拿到了锁,则返回true。

lockInterruptibly()

当经过这个方法去获取锁时,若是线程正在等待获取锁,则这个线程可以响应中断,即中断线程的等待状态。也就使说,当两个线程同时经过lock.lockInterruptibly()想获取某个锁时,倘若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法可以中断线程B的等待过程。

因为lockInterruptibly()的声明中抛出了异常,因此lock.lockInterruptibly()必须放在try块中或者在调用lockInterruptibly()的方法外声明抛出InterruptedException。

public class Test {
    private Lock lock = new ReentrantLock();
    public static void main(String[] args)  {
        Test test = new Test();
        MyThread thread1 = new MyThread(test);
        MyThread thread2 = new MyThread(test);
        thread1.start();
        thread2.start();

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread2.interrupt();
    }

    public void insert(Thread thread) throws InterruptedException{
        lock.lockInterruptibly();   //注意,若是须要正确中断等待锁的线程,必须将获取锁放在外面,而后将InterruptedException抛出
        try {
            System.out.println(thread.getName()+"获得了锁");
            long startTime = System.currentTimeMillis();
            for(    ;     ;) {
                if(System.currentTimeMillis() - startTime >= Integer.MAX_VALUE)
                    break;
                //插入数据
            }
        }
        finally {
            System.out.println(Thread.currentThread().getName()+"执行finally");
            lock.unlock();
            System.out.println(thread.getName()+"释放了锁");
        }
    }
}

注意,当一个线程获取了锁以后,是不会被interrupt()方法中断的。由于自己在前面的文章中讲过单独调用interrupt()方法不能中断正在运行过程当中的线程,只能中断阻塞过程当中的线程。

所以当经过lockInterruptibly()方法获取某个锁时,若是不能获取到,只有进行等待的状况下,是能够响应中断的。

而用synchronized修饰的话,当一个线程处于等待某个锁的状态,是没法被中断的,只有一直等待下去。

相关文章
相关标签/搜索