java多线程之Lock介绍

java.util.concurrent包中有关于Lock锁的定义。它提供了ReentrantLock、ReetrantReadWriteLock.ReadLock 和 ReetrantReadWriteLock.WriteLock,重入锁、读锁和写锁等。

ReentrantLock

公平锁和非公平锁

公平锁当线程请求锁时,会将其加入到请求队列中,
非公平锁就是当前线程不管有无请求队列,先去请求锁,如果请求不到,加入到队列末尾。
具体分析可以参考
http://www.jianshu.com/p/f7d05d06ef54

默认构造方法创建非公平锁

public ReentrantLock() {
        sync = new NonfairSync();
    }

Lock与synchronized的区别

  1. lock是显示的锁,调用lock()添加锁,必须调用unlock()释放锁。
  2. 使用lock.lock()调用线程的interrupt会中断线程,而synchronized锁调用interrupt不会中断线程。
实例来证明2的结论

我们知道synchronized的interrupt方法只是设置中断标志,并没有真正的中断线程。下面这个代码示例用于说明,
代码如下:

设计两个线程,一个read线程,一个write线程。
当read线程正在read的时候,write线程处于等待状态、
然后中断write线程,看会有什么情况发生
如果write中断,会打印 write end…
因为Reentrant对中断锁做出响应,会中断线程的执行。

public static void syncReaderWriterTest(){
        MyReentrantLockBuffer mySyncBuffer = new MyReentrantLockBuffer();
        final Thread reader = new Thread(new SyncReader(mySyncBuffer));
        final Thread writer = new Thread(new SyncWriter(mySyncBuffer));
        reader.start();
        writer.start();

        System.out.println(Thread.currentThread().getName() +  " writer 不在等待了...");
        writer.interrupt();
        System.out.println(Thread.currentThread().getName() +  " writer 已经中断, " + writer.isInterrupted());
    }

    /**
     * 写操作,传入MySyncBuffer的构造方法,保证两个对象持有相同的对象锁
     */
    static class SyncWriter implements Runnable{

        private MyReentrantLockBuffer mySyncBuffer;

        public SyncWriter(MyReentrantLockBuffer mySyncBuffer){
            this.mySyncBuffer = mySyncBuffer;
        }
        public void run() {
            try {
                mySyncBuffer.write();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " write end." + Thread.currentThread().isInterrupted());
        }
    }

    /**
     * 读操作,传入MySyncBuffer的构造方法,保证两个对象持有相同的对象锁
     */
    static class SyncReader implements Runnable{

        private MyReentrantLockBuffer mySyncBuffer;

        public SyncReader(MyReentrantLockBuffer mySyncBuffer){
            this.mySyncBuffer = mySyncBuffer;
        }
        public void run() {

            mySyncBuffer.read();

        }
    }

可重入性

锁的可重入性体现在两个方面
1. methodA和methodB拥有同一个对象锁,线程进入A方法中,在没有释放锁的前提下,可以直接调用methodB方法。

static class ReentrantDemoRunnable implements Runnable{
        final ReentrantLock reentrantLock = new ReentrantLock();
public void methodA() {
            System.out.println(" lock before methodAAAAAA ========" );
            reentrantLock.lock();

            try {
                System.out.println(" run methodA and lock..." );
                this.methodB();
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                System.out.println(" run methodA end unlock..." );
                reentrantLock.unlock();
            }
            System.out.println(" methodAAAAAA ======== end =====" );
        }

        public void methodB() {
            System.out.println(" lock before methodBBBBBB ========" );
            reentrantLock.lock();

            try {
                System.out.println(" run methodB and lock..." );

            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                System.out.println(" run methodB end unlock..." );
                reentrantLock.unlock();
            }
            System.out.println(" methodBBBBBB ======== end =====" );
        }
  }

多线程调用methodA执行的顺序为:

 lock before methodAAAAAA ========
 run methodA and lock...
 lock before methodBBBBBB ========
 run methodB and lock...
 run methodB end unlock...
 methodBBBBBB ======== end =====
 run methodA end unlock...
 methodAAAAAA ======== end =====
  1. 在加锁的前提下,在子类方法中调用父类的方法。
static class Father{
        final ReentrantLock lock = new ReentrantLock();
        public void method(){
            lock.lock();
            try {
                System.out.println("father do something ");
            } finally {
                lock.unlock();
            }
        }
    }

    static class SubClass extends Father{

        @Override
        public void method(){
            lock.lock();
            try {
                System.out.println("subClass do something ");
                super.method();
            } finally {
                lock.unlock();
            }
        }
    }

public static void reentrantTest2(){
        SubClass subClass = new SubClass();
        subClass.method();
    }

打印结果:

subClass do something 
father do something 

ReentrantReadWriteLock

读写锁,它有两个内部类,分别是ReadLock和WriteLock
这两个锁分别有以下特性

多个读锁不互斥(证明)

一个方法内使用readLock.lock锁,当多个线程访问时候,如果线程A持有锁,线程B也可以持有该锁。两个线程无需互相等待。

 static class ReadRunnable implements Runnable{

        final ReentrantReadWriteLock.ReadLock readLock = new ReentrantReadWriteLock().readLock();

        public void run() {
            readLock.lock();
            System.out.println(Thread.currentThread().getName() + " read start...");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                System.out.println(Thread.currentThread().getName() + " read end...");
                readLock.unlock();
            }
        }
    }
public static void readLockNotMutex(){
        ReadRunnable readRunnable = new ReadRunnable();
        //创建3个多线程,来说明读锁不互斥
        for (int i = 0; i < 3; i++) {
            Thread thread = new Thread(readRunnable);
            thread.start();
        }
    }

例子说明:创建多个线程,每个线程执行MyRunnable的run方法,run方法中使用读锁设置同步。发现会有多个run方法中打印乱序。
打印结果

     Thread-0 read start...
     Thread-1 read start...
     Thread-2 read start...
     Thread-0 read end...
     Thread-1 read end...
     Thread-2 read end...

说明读锁不互斥

多个写锁互斥(证明)

原理与读锁相反

static class WriteRunnable implements Runnable{

        final ReentrantReadWriteLock.WriteLock writeLock = new ReentrantReadWriteLock().writeLock();

        public void run() {
            writeLock.lock();
            System.out.println(Thread.currentThread().getName() + " write start...");
            try {
                Thread.sleep(2000);

            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                System.out.println(Thread.currentThread().getName() + " write end...");
                writeLock.unlock();
            }
        }
    }
public static void writeLockMutex(){
        WriteRunnable writeRunnable = new WriteRunnable();
        //创建3个多线程,来说明读锁不互斥
        for (int i = 0; i < 3; i++) {
            Thread thread = new Thread(writeRunnable);
            thread.start();
        }
    }

创建多个线程,每个线程执行MyRunnable的run方法,run方法中使用写锁设置同步。发现会有多个run方法中打印不乱序。
打印结果:

     Thread-0 write start...
     Thread-0 write end...
     Thread-1 write start...
     Thread-1 write end...
     Thread-2 write start...
     Thread-2 write end...

说明写锁互斥

读锁和写锁互斥
  • 线程A执行writeMethod()方法,获取到写锁,
  • 此时线程B执行readMethod()方法,因为读写锁(写读锁)互斥,所以要等待线程A释放锁,然后才能继续执行。
    实例如下:
static class ReadAndWriteLock{
        final ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
        final ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        final ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();

        public void writeMethod(){
            writeLock.lock();
            System.out.println(Thread.currentThread().getName() + " 当前为write Lock========");
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                System.out.println(Thread.currentThread().getName() + " write lock end ========");
                writeLock.unlock();
            }
            System.out.println(Thread.currentThread().getName() + " 程序结束 end");
        }

        public void readMethod(){
            readLock.lock();
            System.out.println(Thread.currentThread().getName() + " 当前为read Lock========");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                System.out.println(Thread.currentThread().getName() + " read lock end ========");
                readLock.unlock();
            }
            System.out.println(Thread.currentThread().getName() + " 程序结束 end");
        }
    }
public static void readWriteMutex(){
        ReadAndWriteLock readAndWriteLock = new ReadAndWriteLock();

        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                readAndWriteLock.writeMethod();
            }
        });
        t.start();


        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                readAndWriteLock.readMethod();
            }
        });
        t1.start();
    }

打印结果
读锁与写锁内容分别顺序打印。说明读写锁互斥。

     Thread-0 当前为write Lock========
     Thread-0 write lock end ========
     Thread-0 程序结束 end
     Thread-1 当前为read Lock========
     Thread-1 read lock end ========
     Thread-1 程序结束 end
读锁不支持Condition操作,而写锁支持

从源码中看到,ReadLock的newCondition方法被禁用了

 public Condition newCondition() {
            throw new UnsupportedOperationException();
        }

而WriteLock可以创建

public Condition newCondition() {
            return sync.newCondition();
        }
支持降级锁,不支持升级锁

降级锁: 线程进入writeLock,没有释放锁,同时获取到readLock。这时,writeLock会降级为readLock。但是writeLock的锁仍需手动释放。ReentrantReadWriteLock支持降级锁。

public void downGradeLock(){
            writeLock.lock();

            try {
                System.out.println(Thread.currentThread().getName() + " 当前进入写锁 write...");
                readLock.lock();
                System.out.println(Thread.currentThread().getName() + " 当前进入读锁 read...");
            } finally {
                System.out.println(Thread.currentThread().getName() + " 释放读锁...");
                readLock.unlock();
                System.out.println(Thread.currentThread().getName() + " 释放写锁...");
                writeLock.unlock();
            }
            System.out.println(Thread.currentThread().getName() + " 程序结束...");
        }

打印结果:
程序在持有写锁的同时,可以获得读锁。说明,ReentrantReadWriteLock支持降级锁。

main 当前进入写锁 write...
main 当前进入读锁 read...
main 释放读锁...
main 释放写锁...
main 程序结束...

升级锁: 线程进入readLock,没有释放锁,同时要获取writeLock。
这时,程序进入死锁状态。ReentrantReadWriteLock不支持升级锁。
调用下面的方法,会出现死锁现象。

public void upGradeLock(){
            readLock.lock();
            System.out.println(Thread.currentThread().getName() + " 当前进入写锁 read...");
            writeLock.lock();
            System.out.println(Thread.currentThread().getName() + " 当前进入写锁 write...");
            writeLock.unlock();
            System.out.println("程序结束....");
        }

打印结果:

main 当前进入写锁 read...

并且程序一直在等待,尝试获取writeLock。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值