Java AQS与ReentrantLock

AbstractQueuedSynchronizer简称AQS,是Java并发容器的一个抽象类,顾名思义抽象同步队列,即队列同步器。

  1. AQS原理
    AQS实现了一个FIFO(FirstIn、FisrtOut先进先出)的队列,底层实现的数据结构是一个双向链表。同步队列,是一个双向链表,包括head节点和tail节点。head节点主要用作后续的调度。 Conditionqueue:非必须,单向链表。当程序中存在cindition的时候才会存在此列表。
    AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。AQS使用CAS对state同步状态进行原子操作实现对其值的修改。
  2. ReentrantLock唯一实现了Lock接口
    基于AQS有一个同步组件,叫做ReentrantLock。Lock提供了一种方式来实现同步访问,ReentrantLock是唯一实现了Lock接口的类。在这个组件里,stste表示获取锁的线程数,假如state=0,表示还没有线程获取锁,1表示有线程获取了锁。大于1表示重入锁的数量。
    Lock相比synchronized主要有如下不同点:
    1) synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
    2)Lock可以让等待锁的线程响应中断;
    3)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到;
    4)Lock可以提高多个线程进行读操作的效率。
    在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。(引用[1])
    ReentrantLock锁用法示例如下:
public class Test {
    private ArrayList arrayList = new ArrayList();
    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) {
        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();
        }
    }
}

ReentrantLock获取锁失败示例如下:

public class TestTry {
    private ArrayList arrayList = new ArrayList();
    private Lock lock = new ReentrantLock();    //成员变量
    public static void main(String[] args)  {
        final TestTry test = new TestTry();
       
        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()+"获取锁失败");
        }
    }
}

ReentrantLock获取锁中断示例如下:

public class TestInterruptibly {

     private Lock lock = new ReentrantLock();   
        public static void main(String[] args)  {
            TestInterruptibly test = new TestInterruptibly();
            MyThread thread1 = new MyThread(test);
            MyThread thread2 = new MyThread(test);
            thread1.start();
            //留时间,让下个Thread得到锁并中断
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            thread2.start();
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            thread2.interrupt();
        }  
         
        public void insert(Thread thread) throws InterruptedException{
            System.out.println(thread.getName()+"得到了锁");
            lock.lockInterruptibly();   //注意,如果需要正确中断等待锁的线程,必须将获取锁放在外面,然后将InterruptedException抛出
            try {  
                System.out.println(thread.getName()+"lock得到了锁");
                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()+"释放了锁");
            }  
        }
}
class MyThread extends Thread {
    private TestInterruptibly test = null;
    public MyThread(TestInterruptibly test) {
        this.test = test;
    }
    @Override
    public void run() {
         
        try {
            test.insert(Thread.currentThread());
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName()+"被中断");
        }
    }
}    

参考:
[1]Java并发编程:Lock
[2]什么是AQS及其原理
[3]Java并发编程原理与实战十七:AQS实现重入锁

你可能感兴趣的:(Java AQS与ReentrantLock)