精华内容
下载资源
问答
  • 独占锁与共享锁

    千次阅读 2019-08-09 19:38:11
    独占锁与共享锁前言概念引入独占锁概念共享锁概念源码分析ReentrantReadWriteLock源码读锁和写锁的具体加锁方式有什么区别 前言 独占锁和共享锁同样是一种概念。我们先介绍一下具体的概念,然后通过ReentrantLock和...

    前言

    独占锁和共享锁同样是一种概念。我们先介绍一下具体的概念,然后通过 ReentrantLock 和 ReentrantReadWriteLock 的源码来介绍独占锁和共享锁。

    概念引入

    独占锁概念

    独占锁也叫排他锁,是指该锁一次只能被一个线程所持有。如果线程T对数据A加上排他锁后,则其他线程不能再对A加任何类型的锁。获得排它锁的线程即能读数据又能修改数据。JDK中的synchronized和 JUC中Lock的实现类就是互斥锁。

    共享锁概念

    共享锁是指该锁可被多个线程所持有。如果线程T对数据A加上共享锁后,则其他线程只能对A再加共享锁,不能加排它锁。获得共享锁的线程只能读数据,不能修改数据。 独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。

    源码分析

    ReentrantReadWriteLock源码

    在这里插入图片描述
    我们看到 ReentrantReadWriteLock 有两把锁:ReadLock和WriteLock,见名知意,一个读锁一个写锁, 合称“读写锁”。
    再进一步观察可以发现 ReadLock 和 WriteLock 是靠内部类 Sync 实现的锁。
    Sync 是 AQS 的一个子类,这种结构在 CountDownLatch 、ReentrantLock 、Semaphore 里面也都存在。
    在ReentrantReadWriteLock 里面,读锁和写锁的锁主体都是 Sync ,但读锁和写锁的加锁方式不一样。
    读锁是共享锁,写锁是独占锁。读锁的共享锁可保证并发读非常高效,而读写、写读、写写的过程互斥,因为读锁和写锁是分离的。所以ReentrantReadWriteLock的并发性相比一般的互斥锁有了很大提升。

    读锁和写锁的具体加锁方式有什么区别

    在了解源码之前我们需要回顾一下其他知识。 在最开始提及 AQS 的时候我们也提到了state字段(int类型,32位),该字段用来描述有多少线程获持有锁。 在独享锁中这个值通常是0或者1(如果是重入锁的话state值就是重入的次数),在共享锁中state就是持有锁的数量。但是在 ReentrantReadWriteLock 中有读、写两把锁,所以需要在一个整型变量state上分别描述读锁和写锁的数量(或者也可以叫状态)。于是将state变量“按位切割”切分成了两个部分,高16位 表示读锁状态(读锁个数),低16位表示写锁状态(写锁个数)。如下图所示:
    在这里插入图片描述

    protected final boolean tryAcquire(int acquires) {
    	Thread current = Thread.currentThread();
    	int c = getState(); // 取到当前锁的个数
    	int w = exclusiveCount(c); // 取写锁的个数w
    	if (c != 0) { // 如果已经有线程持有了锁(c!=0)
    		// (Note: if c != 0 and w == 0 then shared count != 0)
    		if (w == 0 || current != getExclusiveOwnerThread()) // 如果写线程数(w)为0(换言之存在读锁) 或者持有锁的线程	不是当前线 程就返回失败
    				return false;
    		if (w + exclusiveCount(acquires) > MAX_COUNT) // 如果写入锁的数量大于最大数(65535,2的16次方-1)就抛出一个Error。
    		throw new Error("Maximum lock count exceeded");
    // Reentrant acquire
    		 setState(c + acquires);
    		return true;
     }
    if (writerShouldBlock() || !compareAndSetState(c, c + acquires)) // 如果当且写线程数为0,并且当前线程需要阻塞那么就返回失败;或者如果通过CAS增加写线程数失败也返回失败。
    		return false;
    	setExclusiveOwnerThread(current); // 如果c=0,w=0或者c>0,w>0(重入),则设置当前线程或锁的拥有者
    	return true; 
    }
    
    • 这段代码首先取到当前锁的个数c,然后再通过c来获取写锁的个数w。因为写锁是低16位,所以取低16位的最大值与当前的c做与运算( int w = exclusiveCount©; ),高16位和0与运算后是0,剩下的就是低位运算的值,同时也是持 有写锁的线程数目。
    • 在取到写锁线程的数目后,首先判断是否已经有线程持有了锁。如果已经有线程持有了锁(c!=0),则查看当前写锁线程的数目,如果写线程数为0(即此时存在读锁)或者持有锁的线程不是当前线程就返回失败。
    • 如果写入锁的数量大于最大数(65535,2的16次方-1)就抛出一个Error。
    • 如果当且写线程数为0(那么读线程也应该为0,因为上面已经处理c!=0的情况),并且当前线程需要阻塞那么就返 回失败;如果通过CAS增加写线程数失败也返回失败。
    • 如果c=0,w=0或者c>0,w>0(重入),则设置当前线程或锁的拥有者,返回成功!

    tryAcquire()除了重入条件(当前线程为获取了写锁的线程)之外,增加了一个读锁是否存在的判断。
    如果存在读锁,则写锁不能被获取,原因在于:
    必须确保写锁的操作对读锁可见,如果允许读锁在已被获取 的情况下对写锁的获取,那么正在运行的其他读线程就无法感知到当前写线程的操作。
    因此,只有等待其他读线程都释放了读锁,写锁才能被当前线程获取,而写锁一旦被获取,则其他读写线程的后续访问均被阻塞。写锁的释放与 ReentrantLock 的释放过程基本类似,每次释放均减少写状态,当写状态为0时表示写锁已被释放,然后等待的读写线程才能够继续访问读写锁,同时前次写线程的修改对 后续的读写线程可见。 接着是读锁的代码:

    protected final int tryAcquireShared(int unused) {
     	Thread current = Thread.currentThread();
    	int c = getState();
    	if (exclusiveCount(c) != 0 &&getExclusiveOwnerThread() != current)
    		return -1; // 如果其他线程已经获取了写锁,则当前线程获取读锁失败,进入等待状态
    	int r = sharedCount(c);
    	if (!readerShouldBlock() &&r < MAX_COUNT &&compareAndSetState(c, c + SHARED_UNIT)) {
    		if (r == 0) {
     			firstReader = current;
     			firstReaderHoldCount = 1;
     		} else if (firstReader == current) {
     			firstReaderHoldCount++;
     		} else {
     			HoldCounter rh = cachedHoldCounter;
    			if (rh == null || rh.tid != getThreadId(current))
     				cachedHoldCounter = rh = readHolds.get();
    			else if (rh.count == 0)
     				readHolds.set(rh);
     				rh.count++;
     		}
    		return 1;
     	}
    	return fullTryAcquireShared(current);
    }
    

    可以看到在 tryAcquireShared(int unused) 方法中,如果其他线程已经获取了写锁,则当前线程获取读锁 失败,进入等待状态。如果当前线程获取了写锁或者写锁未被获取,则当前线程(线程安全,依靠CAS保证)增加读状态,成功获取读锁。读锁的每次释放(线程安全的,可能有多个读线程同时释放读锁)均减少读状态,减少的值是“1<<16”。所以读写锁才能实现读读的过程共享,而读写、写读、写写的过程互斥。

    展开全文
  • 个人理解记录ReentrantLock基于aqs...一般用try finally来实现,相对于synchronized,reentrantlock提供了功能更强大的api,例如超时锁、可中断锁、公平锁、非公平锁、非阻塞锁获取等等,ReentrantLock是独占锁,...

    个人理解记录

    ReentrantLock基于aqs实现,他的基本原理是aqs的status为0时表示锁被占用,为1时表示锁被释放。ReentrantLock在使用时需要显式的获取和释放锁,一般用try finally来实现,相对于synchronized,reentrantlock提供了功能更强大的api,例如超时锁、可中断锁、公平锁、非公平锁、非阻塞锁获取等等,ReentrantLock是独占锁,它分为公平锁和非公平锁两种模式,公平锁保证按照获取锁的顺序来得到锁,非公平锁则则可以进行抢占,而像countdownlatch、semaphore等组件是基于共享锁实现的,也就是同一时刻可以有多个线程获取锁,锁的数量由用户指定。

    独占公平锁原理:

    调用aqs的lock方法尝试获取锁

    调用上层组件reentrantlock的trylock方法尝试获取同步状态

    如果获取成功,则成功获取锁,如果获取失败,则被构造成node节点后,利用cas线程安全的加到同步对列的末尾

    然后该线程进入自旋状态

    自旋时首先判断前驱节点是否为头节点并且能否成功获取到同步状态,如果都成立,则成功获取锁,如果不成立,则先讲将其前驱节点等待状态设置为signal,然后利用Locksupport挂起,等待前驱线程唤醒当被前驱节点唤醒,且成功回去同步状态后,才成功获取到了锁。

    对于释放锁,就是通过aqs设置同步状态为1的过程,同时唤醒后继节点

    独占非公平锁:

    独占非公平锁与公平锁的唯一区别是,在获取锁时,不管是否有线程在等待锁,直接通过aqs修改同步状态,进行锁抢占,如果抢占失败,那后面的流程就与公平锁一致了。

    共享锁原理:

    共享锁的基本流程与独占锁相同,主要区别在于判断锁获取的条件上,由于是共享锁,也就允许多个线程同时获取,所以同步状态的数量同时的大于1的,如果同步状态为非0,则线程就可以获取锁,只有当同步状态为0时,才说明共享数量的锁已经被全部获取,其余线程只能等待。

    共享锁的释放过程正好与之相反,释放锁对应的AQS操作时增加同步状态的值。

    展开全文
  • java并发-独占锁与共享锁

    千次阅读 2019-06-28 09:21:11
    java并发包提供的加锁模式分为独占锁共享锁独占锁模式下,每次只能有一个线程能持有锁,ReentrantLock就是以独占方式实现的互斥锁。共享锁,则允许多个线程同时获取锁,并发访问 共享...

    转自:https://blog.csdn.net/wojiushiwo945you/article/details/42292999

    另外可以看下这个:https://blog.csdn.net/luofenghan/article/details/75065001

    1 锁的独占与共享

          java并发包提供的加锁模式分为独占锁和共享锁,独占锁模式下,每次只能有一个线程能持有锁,ReentrantLock就是以独占方式实现的互斥锁。共享锁,则允许多个线程同时获取锁,并发访问 共享资源,如:ReadWriteLock。AQS的内部类Node定义了两个常量SHARED和EXCLUSIVE,他们分别标识 AQS队列中等待线程的锁获取模式。

         很显然,独占锁是一种悲观保守的加锁策略,它避免了读/读冲突,如果某个只读线程获取锁,则其他读线程都只能等待,这种情况下就限制了不必要的并发性,因为读操作并不会影响数据的一致性。共享锁则是一种乐观锁,它放宽了加锁策略,允许多个执行读操作的线程同时访问共享资源。 java的并发包中提供了ReadWriteLock,读-写锁。它允许一个资源可以被多个读操作访问,或者被一个 写操作访问,但两者不能同时进行。

    2 锁的公平与非公平

         锁的公平与非公平,是指线程请求获取锁的过程中,是否允许插队。在公平锁上,线程将按他们发出请求的顺序来获得锁;而非公平锁则允许在线程发出请求后立即尝试获取锁,如果可用则可直接获取锁,尝试失败才进行排队等待。ReentrantLock提供了两种锁获取方式,FairSyn和NofairSync。结论:ReentrantLock是以独占锁的加锁策略实现的互斥锁,同时它提供了公平和非公平两种锁获取方式。最初看源码时竟然把这两个概念弄混了。

     3 AQS提供的模板方法

        AQS提供了独占锁和共享锁必须实现的方法,具有独占锁功能的子类,它必须实现tryAcquire、tryRelease、isHeldExclusively等;共享锁功能的子类,必须实现tryAcquireShared和tryReleaseShared等方法,带有Shared后缀的方法都是支持共享锁加锁的语义。Semaphore是一种共享锁,ReentrantLock是一种独占锁。

        独占锁获取锁时,设置节点模式为Node.EXCLUSIVE

    public final void acquire(int arg) {
            if (!tryAcquire(arg) &&
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                selfInterrupt();
        }
    
         private void doAcquireShared(int arg) {
            final Node node = addWaiter(Node.SHARED);
            boolean failed = true;
            .....
        }
    

     4  对ConditionObject的认识

         ReentrantLock是独占锁,而且AQS的ConditionObject只能与ReentrantLock一起使用,它是为了支持条件队列的锁更方便。ConditionObject的signal和await方法都是基于独占锁的,如果线程非锁的独占线程,则会抛出IllegalMonitorStateException。例如signalAll源码:

            public final void signalAll() {
                if (!isHeldExclusively())
                    throw new IllegalMonitorStateException();
                Node first = firstWaiter;
                if (first != null)
                    doSignalAll(first);
            }
    

          我在想,既然Condtion是为了支持Lock的,为什么ConditionObject不作为ReentrantLock的内部类呢?对于实现锁功能的子类,直接扩展它就可以实现对条件队列的支持。但是,对于其它非锁语义的实现类如Semaphore、CountDownLatch等类来说,条件队列是无用的,也会给开发者扩展AQS带来困惑。总之,是各有利弊,大师们的思想,还需要仔细揣摩啊!

    展开全文
  • 文章目录java有哪些锁的分类:乐观锁悲观锁:什么是悲观锁,什么是乐观锁悲观锁:乐观锁:乐观锁的实现方式:一二公平锁与非公平锁公平锁与非公平锁的直接的区别独占锁与共享锁独占锁与共享锁之间的区别可重入性CAS(自旋...

    java有哪些锁的分类:

    1. 悲观与乐观锁
    2. 公平锁与非公平锁
    3. 自旋锁/重入锁
    4. 重量级锁与轻量级锁
    5. 独占锁与共享锁

    乐观锁悲观锁:

    什么是悲观锁,什么是乐观锁

    悲观锁:

    • mysql的角度分析: 悲观锁就是比较悲观,当多个线程同一个数据实现修改的时候,最后只有一个线程才能修改成功,只要谁能够获取到行锁 则其他线程时不能够对数据做任何修改操作,且是阻塞状态

    • java锁层面:如果没有获取到锁,则会阻塞等待,后期唤醒的锁成本就会非常高,从新被我们CPU从就绪调度为运行状态

      Lock synchronized 锁 悲观锁 没有获取到锁的线程会阻塞等待;

    在这里插入图片描述

    乐观锁:

    乐观锁比较乐观,通过预值或者版本号比较,如果不一致性的情况则通过循环控制修改,当 前线程不会被阻塞,是乐观,效率比较高,但是乐观锁比较消耗 cpu 的资源。

    在这里插入图片描述

    乐观锁:获取锁–如果没有获取到锁,当前线程是不会阻塞等待,通过死循环控制

    乐观锁属于无锁机制,没有竞争锁的流程

    注意:mysql 的 innodb 引擎中存在行锁的概念/

    Mysql层面如何实现乐观锁了?

    在我们表结构中,会新增一个字段就是版字段

    ``version varchar(255) DEFAULT NULL,`

    多线程对同一行数据实现修改操作,提前查询当前最新的version版本号码

    作为update条件查询,如果当前version版本号码发生了变化,则查询不到该数据,

    表示如果修改数据失败,则不断重试,有从新查询最新的版本实现update

    需要注意的是 控制乐观锁的循环的次数,避免cpu飙高的问题

    mysql的innodb引擎中存在行锁的概念

    乐观锁的实现方式:

    乐观锁,使用版本标识来确定读到的数据与提交时的数据是否一致。提交后修改版本标识,不一致时可以采取丢弃和再次尝试的策略。

    ​ 记录1,id,status1,status2,stauts3,version,表示有三个不同的状态,以及数据当前的版本

    ​ 操作1:update table set status1=1,status2=0,status3=0 where id=111;

    ​ 操作2:update table set status1=0,status2=1,status3=0 where id=111;

    ​ 操作3:update table set status1=0,status2=0,status3=1 where id=111;

    ​ 没有任何控制的情况下,顺序执行3个操作,最后前两个操作会被直接覆盖。

    ​ 加上version字段,每一次的操作都会更新version,提交时如果version不匹配,停止本次提交,可以尝试下一次的提交,以保证拿到的是操作1提交后的结果。

    ​ 这是一种经典的乐观锁实现。

    ​ 另外,java中的compareandswap即cas,解决多线程并行情况下使用锁造成性能损耗的一种机制。

    ​ CAS操作包含三个操作数,内存位置(V),预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会西东将该位置值更新为新值。否则,处理器不做任何操作。

    ​ 记录2: id,stauts,status 包含3种状态值 1,2,3

    ​ 操作,update status=3 where id=111 and status=1;

    ​ 即 如果内存值为1,预期值为1,则修改新值。对于没有执行的操作则丢弃。

    在这里插入图片描述

    公平锁与非公平锁

    公平锁与非公平锁的直接的区别

    公平锁: 就是比较公平,根据请求锁的顺序排列,先来请求先来请求的就先获取锁,后来获取锁就最 后获取到, 采取队列存放…类似类似于吃饭排队。

    队列---底层实现方式---数组或者链表实现

    在这里插入图片描述

    非公平锁:不是据请求的顺序排列,通过争抢的方式获取锁

    非公平锁效率是公平锁效率要高,Synchronized 是非公平锁

    New ReentramtLock()(true)—公平锁

    New ReentramtLock()(false)—非公平锁

    底层基于 aqs 实现

    独占锁与共享锁

    独占锁与共享锁之间的区别

    独占锁: 在多线程中,只允许有一个线程获取到锁,其他线程都会等待。

    共享锁: 多个线程可以同时持有锁,

    例如 ReentrantLock 读写锁。读读可以共享、写写互斥、读写互斥

    /**
     * 独占锁(写锁) 一次只能被一个线程占有
     * 共享锁(读锁) 多个线程可以同时占有
     * ReadWriteLock
     * 读-读  可以共存!
     * 读-写  不能共存!
     * 写-写  不能共存!
     */
    public class ReadWriteLockDemo {
        public static void main(String[] args) {
            MyCache myCache = new MyCache();
            // 写入
            for (int i = 1; i <= 5; i++) {
                final int temp = i;
                new Thread(() -> {
                    myCache.put(temp + "", temp + "");
                }, String.valueOf(i)).start();
            }
            // 读取
            for (int i = 1; i <= 5; i++) {
                final int temp = i;
                new Thread(() -> {
                    myCache.get(temp + "");
                }, String.valueOf(i)).start();
            }
        }
    }
    
    // 加锁的
    class MyCacheLock {
        private volatile Map<String, Object> map = new HashMap<>();
        // 读写锁: 更加细粒度的控制
        private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
        private Lock lock = new ReentrantLock();
    
        // 存,写入的时候,只希望同时只有一个线程写
        public void put(String key, Object value) {
            readWriteLock.writeLock().lock();
            try {
                System.out.println(Thread.currentThread().getName() + "写入" + key);
                map.put(key, value);
                System.out.println(Thread.currentThread().getName() + "写入OK");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                readWriteLock.writeLock().unlock();
            }
        }
    
        // 取,读,所有人都可以读!
        public void get(String key) {
            readWriteLock.readLock().lock();
            try {
                System.out.println(Thread.currentThread().getName() + "读取" + key);
                Object o = map.get(key);
                System.out.println(Thread.currentThread().getName() + "读取OK");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                readWriteLock.readLock().unlock();
            }
        }
    }
    
    /**
     * 自定义缓存
     */
    class MyCache {
    
        private volatile Map<String, Object> map = new HashMap<>();
    
        // 存,写
        public void put(String key, Object value) {
            System.out.println(Thread.currentThread().getName() + "写入" + key);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "写入OK");
        }
    
        // 取,读
        public void get(String key) {
            System.out.println(Thread.currentThread().getName() + "读取" + key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName() + "读取OK");
        }
    }
    

    可重入性

    在同一个线程中锁可以不断传递的,可以直接获取Syn/lock ``aqs

    CAS(自旋锁)

    CAS: 没有获得到锁的线程是不会阻塞的,通过循环控制一直不断的获取锁

    CAS:Compare and Swap,翻译成比较并交换,执行函数CAS(V,E,N)

    三个操作数: 内存值V, 旧的预期值E,要修改的新值N当且仅当预期值E和内存值V相同时,将内存值V修改为N,负责什么都不做

    在这里插入图片描述

    1. CAS是通过硬件指令,保证了原子性
    2. java是通过unsafe jni技术

    原子类: AtomicBooleanAtomicIntegerAtomicLong等使用 CAS 实现

    unsafe类

    在这里插入图片描述

    在这里插入图片描述

    优点:

    没有获取到锁的资源,会一直子啊用户态,不会阻塞,没有锁的线程会一直通过循环控 制重试

    缺点:

    通过死循环控制,消耗 cpu 资源比较高,需要控制循次数,避免 cpu 飙高问题;

    Cas 本质的原理:

    旧的预期值===>>>V(共享变量中值),才会修改我们的V

    基于 cas 实现锁机制原理

    Cas 无锁机制原理:

    1. 定义一个锁的状态
    2. 状态状态值=0,则表示没有线程获取到该锁
    3. 状态状态值=1,则表示有线程已经持有该锁
    实现细节:
    CAS获取锁:

    将该锁的状态从0到1----能够修改成功 cas 成功则表示获取锁成功

    如果获取锁失败–修改失败,则不会阻塞而是通过循环==(自旋控制重试)==

    CAS 释放锁:

    将该锁的状态从 1 改为 0 如果能够改成功 cas 成功则表示释放锁成功。

    CAS 如何解决 ABA 的问题

    CAS主要检查内存值V 与旧的预值值=E是否一致,如果一致的情况下,则修改

    这时会存在ABA的问题:

    如果将原来的值A,改为B,B有改为了A发现没有发生变化,实际上已经发生了变化,所以存在ABA问题

    解决方法,通过版本号,对没个变量更新的版本号+1

    引用原子引用,对应的思想:乐观锁

    解决 aba 问题是否大:概念产生冲突,但是不影响结果,换一种方式 通过版本号码方式

    package com.nie.juc.cas;/*
     *
     *@auth  wenzhao
     *@date 2021/4/25  17:19
     */
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.concurrent.atomic.AtomicStampedReference;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 演示 aba 的问题
     * (1)第一个参数 expectedReference:表示预期值。
     * (2)第二个参数 newReference:表示要更新的值。
     * (3)第三个参数 expectedStamp:表示预期的时间戳。
     * (4)第四个参数 newStamp:表示要更新的时间戳。
     */
    public class CASDemo {
    
        //AtomicStampedReference 注意,如果泛型是一个包装类,注意对象的引用问题
        // 注意:如果引用类型是 Long、Integer、Short、Byte、Character 一定一定要注意值的缓存区间!
        // 比如 Long、Integer、Short、Byte 缓存区间是在-128~127,
        // 会直接存在常量池中,而不在这个区间内对象的值 则会每次都 new 一个对象,
        // 那么即使两个对象的值相同,CAS 方法都会返回 false
        // 先声明初始值,修改后的值和临时的值是为了保证使用 CAS 方法不会因为对象不一样而返回 false
    
    
        // 正常在业务操作,这里面比较的都是一个个对象
        static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);
    
        // CAS  compareAndSet : 比较并交换!
        public static void main(String[] args) {
            ;
            new Thread(() -> {
                int stamp = atomicStampedReference.getStamp(); // 获得版本号
                System.out.println("a1=>" + stamp);
    
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                Lock lock = new ReentrantLock(true);
    
                atomicStampedReference.compareAndSet(1, 2,
                        atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
    
                System.out.println("a2=>----" + atomicStampedReference.getStamp());
    
    
                System.out.println("更改" + atomicStampedReference.compareAndSet(2, 1,
                        atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
    
                System.out.println("a3=>" + atomicStampedReference.getStamp());
    
            }, "a").start();
    
    
            // 乐观锁的原理相同!
            new Thread(() -> {
                int stamp = atomicStampedReference.getStamp(); // 获得版本号
                System.out.println("b1=>" + stamp);
    
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("+++++++++++++++++++++++++++++++++++++++++++++");
                System.out.println(atomicStampedReference.compareAndSet(1, 6,
                       stamp, stamp + 1));
    
                System.out.println("b2=>" + atomicStampedReference.getStamp());
    
            }, "b").start();
    
        }
    }
    
    

    输出:

    a1=>1
    b1=>1
    a2=>----2
    更改true
    a3=>3
    +++++++++++++++++++++++++++++++++++++++++++++
    false
    b2=>3
    

    利用原子类手写 CAS 无锁

    package com.nie.juc.cas;/*
     *
     *@auth  wenzhao
     *@date 2021/4/25  20:36
     */
    
    import java.util.concurrent.atomic.AtomicLong;
    import java.util.stream.IntStream;
    
    public class AtomicTryLock {
        private AtomicLong cas = new AtomicLong(0);
        private Thread lockCurrentThread;
    
        /**
         * 1 表示锁已经被获取
         * 0 表示锁没有获取
         * 利用 cas 将 0 改为 1 成功则表示获取锁
         *
         * @return
         */
    
        //加锁
        private boolean tryLock() {
            boolean result = cas.compareAndSet(0, 1);
            if (result) {
                lockCurrentThread = Thread.currentThread();
            }
            return result;
        }
    
        //释放锁
        private boolean unLock() {
            if (lockCurrentThread != Thread.currentThread()) {
                return false;
            }
            return cas.compareAndSet(0, 1);
        }
    
        public static void main(String[] args) {
            AtomicTryLock atomicTryLock = new AtomicTryLock();
            IntStream.range(1, 10).forEach((i) -> new Thread(() ->
            {
                try {
                    boolean result = atomicTryLock.tryLock();
                    if (result) {
                        atomicTryLock.lockCurrentThread = Thread.currentThread();
                        System.out.println(Thread.currentThread().getName() + ",获取锁成功~");
                    } else {
                        System.out.println(Thread.currentThread().getName() + ",获取锁失败~");
                    }
                } catch (Exception e) {
                } finally {
                    //释放锁
                    if (atomicTryLock != null) {
                        atomicTryLock.unLock();
                    }
                }
            }).start());
        }
    
    }
    
    

    输出

    Thread-0,获取锁成功~
    Thread-3,获取锁失败~
    Thread-2,获取锁失败~
    Thread-1,获取锁失败~
    Thread-4,获取锁失败~
    Thread-7,获取锁失败~
    Thread-8,获取锁失败~
    Thread-6,获取锁失败~
    Thread-5,获取锁失败~
    

    在这里插入图片描述

    展开全文
  • java.util.concurrent.locks包下,包含了多种锁,ReentrantLock独占锁、ReentrantReadWriteLock读写锁等,还有java.util.concurrent下的Semaphore、CyclicBarrier、CountDownLatch、Condition都是基于AQS实现的。...
  • Java-Lock独占锁与共享锁原理

    千次阅读 2018-07-22 09:15:22
    个人理解记录 ReentrantLock基于aqs实现...一般用try finally来实现,相对于synchronized,reentrantlock提供了功能更强大的api,例如超时、可中断、公平、非公平、非阻塞获取等等,ReentrantLock是独占...
  • 作者简介:笔名seaboat,擅长工程算法、人工智能算法、自然语言处理、计算机视觉、架构、分布式、高并发、大数据和搜索引擎等方面的技术,大多数编程语言都会使用...详细分析了的获取释放的逻辑、等待队列的管理、.
  • 前面了解了J.U.C中两个重要的类或接口,其它的实现类都是以此为基础的,现在还需要了解一下一些的概念,有助于后面学习一些实现类或接口。重入在并发中,无论是synchronized还是lock也好,内部都有重入的特性,...
  • 真就是套娃结构23333 一般自行实现一个同步组件需要实现...答案也很明显,前面讲 synchronized 说过,要获取一个,本质是获取它的监视器(monitor),如果没有成功获取,则进入该对象的同步队列,然后状态变为阻塞...
  • 一、概述  AbstractQueuedSynchronizer (简称AQS),位于java.util.... AQS是一个用于构建和同步容器的框架。事实上concurrent包内许多类都是基于AQS构建,例如ReentrantLock,Semaphore,CountDownLatch,R...
  • java并发包提供的加锁模式分为独占锁共享锁独占锁模式下,每次只能有一个线程能持有锁,ReentrantLock就是以独占方式实现的互斥锁。共享锁,则允许多个线程同时获取锁,并发访问 共享资源,如:ReadWriteLock。...
  • 独占锁共享锁

    2019-08-19 11:14:59
    多线程访问共享变量存在数据同时修改导致不一致问题,就需要来对共享数据的访问进行管理。 多个线程A B C 去竞争同一个L,存在线程获取的同步状态管理,排队获取,竞争获取,等待获取,释放唤醒其他...
  • java并发包提供的加锁模式分为独占锁共享锁独占锁模式下,每次只能有一个线程能持有锁,ReentrantLock就是以独占方式实现的互斥锁。共享锁,则允许多个线程同时获取锁,并发访问 共享资源,如:ReadWriteLock。...
  • AQS共享锁与独占锁对比
  • 共享锁与独占锁

    2012-04-29 17:27:34
    共享锁独占锁的经典应用,是控制最初用于读取的共享文件的更新。某个进程要读取文件,会先取得该文件或该文件部分区域的共享锁。第二个希望读取相同文件区域的进程也会请求共享锁。两个进程可以并行读取,互不影响...
  • 一、公平锁与非公平锁1.1 概述公平锁:是指多个线程按照申请锁的顺序来获取锁。非公平锁:是指在多线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取到锁,在高并发的情况下,有...
  • Java并发编程锁之独占公平锁与非公平锁比较公平锁和非公平锁理解:在上一篇文章中,我们知道了非公平锁。其实Java中还存在着公平锁呢。公平二字怎么理解呢?和我们现实理解是一样的。大家取排队本着先来先得到的原则...
  • 博主最近在复习 MySQL ...本文以下内容基于数据表(test 表):+----+-------+|id|name|+----+-------+|1|111|+----+-------+|2|222|+----+-------+共享锁共享锁也叫读锁,就是在读取数据的时候加上共享锁,如果不提交...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 678
精华内容 271
关键字:

独占锁与共享锁