多线程之锁的核心基础(AQS)与ReentranLock以及死锁的理解

一、为什么我们要使用AQS(AbstraceQueueSynchronized) ?

           这里我们先介绍下我们使用这个AQS背景,在jdk 1.5之前我们多线程的安全性主要是用Synchronized进行操作,而Synchronized比较死板,当Synchronized修饰的方法、对象获取锁之后其他的线程在当前线程没有释放锁之前是不能访问当前方法的(他的锁是比较重量级的),并且有些时候容易产生死锁、效率低下。为此我们引入了新的锁结构(Lock),Lock接口主要的方法如下(获得锁、释放锁):

多线程之锁的核心基础(AQS)与ReentranLock以及死锁的理解_第1张图片

ReentrantLock作为Lock的唯一实现类,ReentrantLock同时"继承"了AbstactQueueSynchronized,这里不是直接继承AQS,源码如下图所示:多线程之锁的核心基础(AQS)与ReentranLock以及死锁的理解_第2张图片

java 中类图如下:

多线程之锁的核心基础(AQS)与ReentranLock以及死锁的理解_第3张图片

  因为ReentrantLock继承(扩展)了AQS所以拥有了更多控制并发中锁的能力,使得Lock比Synchronized更加灵活。我们接下来就研究下AQS为什么拥有这些神奇的能力。

 

二、AQS的原理分析

         AQS我们称它为队列同步器,这个队列同步器控制着多个线程并发执行,当多个线程进行锁竞争的时候,获得锁的当然是直接进行对应的操作,而没有获得锁的线程则被封装(构造)成结点(Node)形成双向链表在队列同步器进行等待当前线程进行锁的释放(可能多个线程获取到同一把锁:共享锁,排它锁只能由同一个线程获取),然后队列对链表进行自旋操作进行循环尝试获取锁。

多线程之锁的核心基础(AQS)与ReentranLock以及死锁的理解_第4张图片

 

           三、锁的使用

 

package com.gpdi.security;

import java.util.ArrayList;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 *
 * @descrption:可重入锁
 *
 *       功能特性:独占性的非公平锁
 *
 *       和Synchronized比较:更加灵活,功能更加强大,提供了中断,轮训等操作
 *
 *
 */
public class ReentranLockDemo extends AbstractQueuedSynchronizer {


    Lock lock = new ReentrantLock();    //注意这个地方

    private ArrayList arrayList = new ArrayList();
    public static void main(String[] args)  {
        final ReentranLockDemo test = new ReentranLockDemo();
         //启动线程1
        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();

        //启动线程2
        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();
        }
    }
}

4、死锁

package com.gpdi.security;
/**
 * @description: 死锁产生的几个条件必须具备的条件
 *
 *       一、当前线程没有执行完不会释放锁
 *
 *       二、循环等待
 *
 *       三、线程间形成一种头尾相接的关系
 *
 *             背景分析:
 *                     两个线程thread1、thread2 两个锁(LockA、LockB)
 *
 *                     A线程需要获得两个锁,B线程同样需要获取两个锁
 *
 *                                      【锁1、锁2】
 *              ===Thread1===================》《=====================Thread2============
 *   A端(锁1),需要获取(锁2)才释放锁21                     B端(锁2),需要获取(锁1)才释放锁2
 *
 *            A获取Lock1===》B获取Lock2==> A尝试获取锁Lock2(锁2没有释放)===》B获锁1(锁1没有释放)
 *
 *
 *
 *
 *
 */
public class DeadLock {

    private static Object lock1 = new Object();
    private static Object lock2 = new Object();

    public static void main(String[] args) {

        Thread thread1 = new Thread(() -> {
            synchronized (lock1) {
                try {
                    System.out.println("线程" + Thread.currentThread().getName() + "获得了Lock1锁");
                    /**
                     * 时间片的切换是很快的,2s内线程B一定能拿到锁
                     */
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2) {
                    System.out.println("线程" +Thread.currentThread().getName() + "获得了lock2锁");
                }

            }
        }, "thread1");
        Thread thread2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("线程" + Thread.currentThread().getName() + "获得了Lock2锁");
                synchronized (lock1) {
                    System.out.println("线程" +Thread.currentThread().getName() + "获得了Lock1锁");
                }

            }
        }, "thread2");

        thread1.start();
        thread2.start();
    }


}

 

你可能感兴趣的:(java多线程,AQS,ReentrantLock,死锁)