您的位置:

java锁的类型aqs(java锁的类型)

本文目录一览:

Java中有哪些锁,区别是什么

【1】公平所和非公平所。

公平锁:是指按照申请锁的顺序来获取锁,

非公平所:线程获取锁的顺序不一定按照申请锁的顺序来的。

//默认是不公平锁,传入true为公平锁,否则为非公平锁

ReentrantLock reentrantLock = new ReetrantLock();

1

2

【2】共享锁和独享锁

独享锁:一次只能被一个线程所访问

共享锁:线程可以被多个线程所持有。

ReadWriteLock 读锁是共享锁,写锁是独享锁。

【3】乐观锁和悲观锁。

乐观锁:对于一个数据的操作并发,是不会发生修改的。在更新数据的时候,会尝试采用更新,不断重入的方式,更新数据。

悲观锁:对于同一个数据的并发操作,是一定会发生修改的。因此对于同一个数据的并发操作,悲观锁采用加锁的形式。悲观锁认为,不加锁的操作一定会出问题,

【4】分段锁

1.7及之前的concurrenthashmap。并发操作就是分段锁,其思想就是让锁的粒度变小。

【5】偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价

轻量级锁

重量级锁

【6】自旋锁

自旋锁

说说java锁有哪些种类,以及区别

锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) 。这些已经写好提供的锁为我们开发提供了便利,但是锁的具体性质以及类型却很少被提及。本系列文章将分析JAVA下常见的锁名称以及特性,为大家答疑解惑。

1、自旋锁

自旋锁是采用让当前线程不停地的在循环体内执行实现的,当循环的条件被其他线程改变时 才能进入临界区。如下

01 public class SpinLock {

02

03 private AtomicReferenceThread sign =newAtomicReference();

04

05 public void lock(){

06 Thread current = Thread.currentThread();

07 while(!sign .compareAndSet(null, current)){

08 }

09 }

10

11 public void unlock (){

12 Thread current = Thread.currentThread();

13 sign .compareAndSet(current, null);

14 }

15 }

使用了CAS原子操作,lock函数将owner设置为当前线程,并且预测原来的值为空。unlock函数将owner设置为null,并且预测值为当前线程。

当有第二个线程调用lock操作时由于owner值不为空,导致循环一直被执行,直至第一个线程调用unlock函数将owner设置为null,第二个线程才能进入临界区。

由于自旋锁只是将当前线程不停地执行循环体,不进行线程状态的改变,所以响应速度更快。但当线程数不停增加时,性能下降明显,因为每个线程都需要执行,占用CPU时间。如果线程竞争不激烈,并且保持锁的时间段。适合使用自旋锁。

注:该例子为非公平锁,获得锁的先后顺序,不会按照进入lock的先后顺序进行。

Java锁的种类以及辨析(二):自旋锁的其他种类

锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) 。这些已经写好提供的锁为我们开发提供了便利,但是锁的具体性质以及类型却很少被提及。本系列文章将分析JAVA下常见的锁名称以及特性,为大家答疑解惑。

2.自旋锁的其他种类

上篇我们讲到了自旋锁,在自旋锁中 另有三种常见的锁形式:TicketLock ,CLHlock 和MCSlock

Ticket锁主要解决的是访问顺序的问题,主要的问题是在多核cpu上

01 package com.alipay.titan.dcc.dal.entity;

02

03 import java.util.concurrent.atomic.AtomicInteger;

04

05 public class TicketLock {

06 private AtomicInteger serviceNum = new AtomicInteger();

07 private AtomicInteger ticketNum = new AtomicInteger();

08 private static final ThreadLocalInteger LOCAL = new ThreadLocalInteger();

09

10 public void lock() {

11 int myticket = ticketNum.getAndIncrement();

12 LOCAL.set(myticket);

13 while (myticket != serviceNum.get()) {

14 }

15

16 }

17

18 public void unlock() {

19 int myticket = LOCAL.get();

20 serviceNum.compareAndSet(myticket, myticket + 1);

21 }

22 }

每次都要查询一个serviceNum 服务号,影响性能(必须要到主内存读取,并阻止其他cpu修改)。

CLHLock 和MCSLock 则是两种类型相似的公平锁,采用链表的形式进行排序,

01 importjava.util.concurrent.atomic.AtomicReferenceFieldUpdater;

02

03 public class CLHLock {

04 public static class CLHNode {

05 private volatile boolean isLocked = true;

06 }

07

08 @SuppressWarnings("unused")

09 private volatileCLHNode tail;

10 private static finalThreadLocalCLHNode LOCAL = new ThreadLocalCLHNode();

11 private static finalAtomicReferenceFieldUpdaterCLHLock, CLHNode UPDATER = AtomicReferenceFieldUpdater.newUpdater(CLHLock.class,

12 CLHNode.class,"tail");

13

14 public void lock() {

15 CLHNode node = new CLHNode();

16 LOCAL.set(node);

17 CLHNode preNode = UPDATER.getAndSet(this, node);

18 if (preNode != null) {

19 while (preNode.isLocked) {

20 }

21 preNode = null;

22 LOCAL.set(node);

23 }

24 }

25

26 public void unlock() {

27 CLHNode node = LOCAL.get();

28 if (!UPDATER.compareAndSet(this, node,null)) {

29 node.isLocked = false;

30 }

31 node = null;

32 }

33 }

CLHlock是不停的查询前驱变量, 导致不适合在NUMA 架构下使用(在这种结构下,每个线程分布在不同的物理内存区域)

MCSLock则是对本地变量的节点进行循环。不存在CLHlock 的问题。

01 importjava.util.concurrent.atomic.AtomicReferenceFieldUpdater;

02

03 public class MCSLock {

04 public static class MCSNode {

05 volatile MCSNode next;

06 volatile boolean isLocked = true;

07 }

08

09 private static finalThreadLocalMCSNode NODE = new ThreadLocalMCSNode();

10 @SuppressWarnings("unused")

11 private volatileMCSNode queue;

12 private static finalAtomicReferenceFieldUpdaterMCSLock, MCSNode UPDATER = AtomicReferenceFieldUpdater.newUpdater(MCSLock.class,

13 MCSNode.class,"queue");

14

15 public void lock() {

16 MCSNode currentNode = new MCSNode();

17 NODE.set(currentNode);

18 MCSNode preNode = UPDATER.getAndSet(this, currentNode);

19 if (preNode != null) {

20 preNode.next = currentNode;

21 while (currentNode.isLocked) {

22

23 }

24 }

25 }

26

27 public void unlock() {

28 MCSNode currentNode = NODE.get();

29 if (currentNode.next == null) {

30 if (UPDATER.compareAndSet(this, currentNode, null)) {

31

32 } else {

33 while (currentNode.next == null) {

34 }

35 }

36 } else {

37 currentNode.next.isLocked = false;

38 currentNode.next = null;

39 }

40 }

41 }

在java中有哪些锁

给你整理了Java中的一些锁:

公平锁/非公平锁

可重入锁

独享锁/共享锁

互斥锁/读写锁

乐观锁/悲观锁

分段锁

偏向锁/轻量级锁/重量级锁

自旋锁

上面是很多锁的名词,这些分类并不是全是指锁的状态,有的指锁的特性,有的指锁的设计

LockSupport与AQS

LockSupport类是Java6(JSR166-JUC)引入的一个类,提供了基本的线程同步原语。

每个线程都会有一个独有的permit(许可)。

相比较于wait/notify/notifyAll有何优点?

注意:LockSupport是不可重入的:unpark三次之后,park一次可以继续运行,再次park还是会被阻塞。可以理解为unpark是把某个标志位标为1,并不是加1。park是将这个标志位标为0,而非减1。

AbstractQueuedSynchronizer,即队列同步器。它是构建锁或者其他同步组件的基础框架(如ReentrantLock、ReentrantReadWriteLock、Semaphore等),它是JUC并发包中的核心基础组件。

AQS简单地说就是使用一个FIFO的等待队列和一个volatile int state来实现同步的。即通过CAS state判断是否被锁(CAS来保证原子性、volatile保证可见性),将阻塞的线程打包放入等待队列中。

1. AQS的使用者一般定义一个内部类来继承AQS,使用组合的方式使用。

2. AQS有两种模式:排他和共享。

排他模式:只有一个线程可以拥有锁。(排他锁)

共享模式:可以同时多个线程拥有锁。(读锁)

AQS中两种模式下的waiting thread共用一个queue,所以一般使用者都只是使用一种模式。ReentrantReadWriteLock是同时使用了两种模式。

使用者继承AQS,实现AQS中的几个未实现的方法。然后就可以调用AQS的方法来实现自己的接口功能了。

我们可以看到ReentrantLock使用一个内部类Sync来继承AQS,然后实现排他锁的三个方法。

我们知道ReentrantLock有lock和unlock接口,可以看到这两个接口的实现就是调用AQS原有的方法。

前三个是排他锁所要实现的,后两个是共享锁所要实现了。注意:这五个函数并不是abstract,原因是因为一般都是使用某一种模式(排他或共享模式),所以子类只需使用其中一组就可以了。

在使用AQS的类中用来加锁和解锁的方法。

这里我们可以看到“获取锁,如果失败则加入队列”这个行为是由AQS来实现的。而如何判断失败?这个是由子类来决定的。这个决定支持了可重入性、是否公平性等功能。

共享模式下的对应的四个方法。

我们知道公平锁:先来的一定先获取锁。

非公平锁:当多个线程在争取锁,谁先获取锁的顺序是不固定的。

AQS的公平性是由使用者来决定的。

我们知道AQS中的acquire函数是大致这样实现的。

因为每次acquire的步骤是:先try再入队列。所以就可以出现这种情况:队列中有两个线程在等待,当锁被释放时,刚好又来了一个线程,则try的时候成功了,这样这个线程就获得锁了。

如果想要实现公平锁:tryAcquire的时候判断一下,如果有线程在等待,这个函数直接返回false。

显然非公平锁要比公平锁的效果要高。

什么是重入锁和AQS

什么是重入锁

java.util.concurrent.locks.ReentrantLock

ReenTrantLock独有的能力:

1.      ReenTrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。

2.      ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。

3.      ReenTrantLock提供了一种能够中断等待锁的线程的机制,通过lock.lockInterruptibly()来实现这个机制。

非重入锁

当A方法获取锁去锁住一段需要做原子性操作的B方法时,如果这段B方法又需要锁去做原子性操作,那么A方法就必定要与B方法出现死锁。这种会出现问题的重入一把锁的情况,叫不可重入锁。

lock的过程:

   首先尝试获取资源,如果当前状态为0,表示没有线程占有锁,设置该线程为独占模式,使用CAS设置状态,否则如果当前线程和独占线程是一个线程,修改状态值,否则返回false。

  若获取资源失败,则通过addWaiter -(aqs)方法创建一个节点并放在CLH队列的尾部。head tail未初始化会创建虚拟节点同时指向

为什么 AQS 需要一个虚拟 head 节点

每个节点都需要设置前置Node 的 waitStatus  状态(这个状态为是为了保证数据一致性),防止重复释放操作。而第一个节点是没有前置节点的,所以需要创建一个虚拟节点。

  逐步去执行CLH队列中的线程,当前线程会公平性的阻塞一直到获取锁为止,返回线程在等待的过程中还是否中断过。

unlock的过程

一次unlock操作需要修改状态位,然后唤醒节点。整个释放操作也是使用unpark()来唤醒队列最前面的节点。其实lock中比较重要的也就是lock和release,它们又和AQS联系紧密,下面会单独谈谈AQS的重要方法。

Condition的await和signal

wait和notify/notify VS await和signal

Condition能够支持不响应中断,而通过使用Object方式不支持;

Condition能够支持多个等待队列(new 多个Condition对象),而Object方式只能支持一个;

Condition能够支持超时时间的设置,而Object不支持

对标Object的wait方法

void await() throws InterruptedException:当前线程进入等待状态,如果其他线程调用condition的signal或者signalAll方法并且当前线程获取Lock从await方法返回,如果在等待状态中被中断会抛出被中断异常;

long awaitNanos(long nanosTimeout):当前线程进入等待状态直到被通知,中断或者超时;

boolean await(long time, TimeUnit unit)throws InterruptedException:同第二种,支持自定义时间单位

boolean awaitUntil(Date deadline) throws InterruptedException:当前线程进入等待状态直到被通知,中断或者到了某个时间

对标Object的notify/notifyAll方法

void signal():唤醒一个等待在condition上的线程,将该线程从等待队列中转移到同步队列中,如果在同步队列中能够竞争到Lock则可以从等待方法中返回。

void signalAll():与1的区别在于能够唤醒所有等待在condition上的线程

如图所示,ConditionObject是AQS的内部类,因此每个ConditionObject能够访问到AQS提供的方法,相当于每个Condition都拥有所属同步器的引用。

调用condition.await方法的线程必须是已经获得了lock,也就是当前线程是同步队列中的头结点。调用该方法后会使得当前线程所封装的Node尾插入到等待队列中。

如图,线程awaitThread先通过lock.lock()方法获取锁成功后调用了condition.await方法进入等待队列,而另一个线程signalThread通过lock.lock()方法获取锁成功后调用了condition.signal或者signalAll方法,使得线程awaitThread能够有机会移入到同步队列中,当其他线程释放lock后使得线程awaitThread能够有机会获取lock,从而使得线程awaitThread能够从await方法中退出执行后续操作。如果awaitThread获取lock失败会直接进入到同步队列。

// 线程已被取消

    static final int CANCELLED =  1;

    // 当前线程的后继线程需要被unpark(唤醒)

    // 一般发生情况是:当前线程的后继线程处于阻塞状态,而当前线程被release或cancel掉,因此需要唤醒当前线程的后继线程。

    static final int SIGNAL    = -1;

    // 在Condition休眠状态,在等待Condition唤醒

    static final int CONDITION = -2;

    // (共享锁)其它线程获取到“共享锁”,对应的waitStatus的值

    static final int PROPAGATE = -3;

volatile int waitStatus;

---------------------

/**

    * 这个方法也就是lock()方法的关键方法。tryAcquire获得资源,返回true,直接结束。若未获取资源,新建一个节点插入队尾,

*addWaiter用于添加节点,也就是把当前线程对应的节点插入CLH队列的尾部。

    * @param arg the acquire argument.  This value is conveyed to

    *        {@link #tryAcquire} but is otherwise uninterpreted and

    *        can represent anything you like.

    */

    public final void acquire(int arg) {

        if (!tryAcquire(arg) //获取资源立刻结束

            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//没有被中断过,也结束

            selfInterrupt();

    }

---------------------

protected final boolean tryAcquire(int acquires) {

            final Thread current = Thread.currentThread();

            int c = getState();

            if (c == 0) {

                if (!hasQueuedPredecessors()

                    compareAndSetState(0, acquires)) {

                    setExclusiveOwnerThread(current);

                    return true;

                }

            }

            else if (current == getExclusiveOwnerThread()) { //判断是否持有锁的是自己,重入

                int nextc = c + acquires;

                if (nextc 0)

                    throw new Error("Maximum lock count exceeded");

                setState(nextc);

                return true;

            }

            return false;

        }

---------------------

  * 非公平锁

    */

    static final class NonfairSync extends Sync {

        private static final long serialVersionUID = 7316153563782823691L;

        /**

        * Performs lock.  Try immediate barge, backing up to normal

        * acquire on failure.

        */

        final void lock() {

            if (compareAndSetState(0, 1))//CAS设置当前为0 的时候上锁

                setExclusiveOwnerThread(Thread.currentThread());

            else

                acquire(1);//否则尝试获得锁。

        }

        protected final boolean tryAcquire(int acquires) {

            return nonfairTryAcquire(acquires);

        }

    }

    /**

    * 公平锁

    */

    static final class FairSync extends Sync {

        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {

            acquire(1);

        }

        /**

        *

        */

        protected final boolean tryAcquire(int acquires) {

            final Thread current = Thread.currentThread();

            int c = getState();

            if (c == 0) {

                if (!hasQueuedPredecessors()

                    compareAndSetState(0, acquires)) {//没有前驱节点并且CAS设置成功

                    setExclusiveOwnerThread(current);//设置当前线程为独占线程

                    return true;

                }

            }

            else if (current == getExclusiveOwnerThread()) {//这里和非公平锁类似

                int nextc = c + acquires;

                if (nextc 0)

                    throw new Error("Maximum lock count exceeded");

                setState(nextc);

                return true;

            }

            return false;

        }

    }