Juc(常用工具,CAS算法)

您所在的位置:网站首页 juc工具 Juc(常用工具,CAS算法)

Juc(常用工具,CAS算法)

2023-09-02 21:27| 来源: 网络整理| 查看: 265

Java中的管程(了解)

我们这里就说一下ObjectMonitor中几个关键字段的含义:

_count:记录owner线程获取锁的次数。这句话很好理解,这也决定了synchronized是可重入的。

_owner:指向拥有该对象的线程

_WaitSet:存放处于wait状态的线程队列。

_EntryList:存放等待锁而被block的线程队列。

_count和_owner很好理解,后面两个队列

总结

Java对象与monitor的关联:通过在Java对象头中的mark word中存储了指向monitor的指针。

明白了Java中的管程ObjectMonitor的工作原理,知道了Java语法中的wait/notify/notifyAll方法底层都是调用了ObjectMonitor的方法。

明白了管程的概念,一种程序结构,封装了同步操作,避免直接使用PV信号量。。

管程

管程(monitor),直译为“监视器”,是保证了同一时刻只有一个进程在管程内活动,即管程内定义的操作在同一时刻只被一个进程调用(由编译器实现).但是这样并不能保证进程以设计的顺序执行,所说“锁”

JVM中同步是基于进入和退出管程(monitor)对象实现的,每个对象都会有一个管程(monitor)对象,管程(monitor)会随着java对象一同创建和销毁

执行线程首先要持有管程对象,然后才能执行方法,当方法完成之后会释放管程,方法在执行时候会持有管程,其他线程无法再获取同一个管程

Java中类似于管程的机制:Synchronized

JDK 1.6引入了偏向锁、轻量级锁、适应性自旋、锁粗化

想要获取monitor的线程先进入monitor的_EntryList队列阻塞等待。即遇到synchronized关键字时。

如果monitor的_owner为空,则从队列中移出并赋值与_owner。

如果在程序里调用了wait()方法,则该线程进入_WaitSet队列。注意wait方法我们之前讲过,它会释放monitor锁,即将_owner赋值为null并进入_WaitSet队列阻塞等待。这时其他在_EntryList中的线程就可以获取锁了。

当程序里其他线程调用了notify/notifyAll方法时,就会唤醒_WaitSet中的某个线程,这个线程就会再次尝试获取monitor锁。如果成功,则就会成为monitor的owner。

当程序里遇到synchronized关键字的作用范围结束时,就会将monitor的owner设为null,退出。

引入管程的原因

信号量机制的缺点:进程自备同步操作,P(S)和V(S)操作大量分散在各个进程中,不易管理,易发生死锁。

管程特点:管程封装了同步操作,对进程隐蔽了同步细节,简化了同步功能的调用界面。

简单地说管程就是一个概念,任何语言都可以实现。目的就是为了简化同步调用的过程。

回想一下刚才我们说的Java中的实现ObjectMonitor。它具体干了什么事,其实就是说管程需要干哪些事。

JUC常用工具 CountDownLatch: 减少计数CyclicBarrier: 循环栅栏Semaphore: 信号灯 CountDownLatch Java 5.0 在java.util.concurrent 包中提供了多种并发容器类来改进同步容器的性能。CountDownLatch 一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。闭锁

CountDownLatch类可以设置一个计数器,然后通过countDown方法来进行减1的操作,使用await方法等待计数器不大于0,然后继续执行await方法之后的语句。

CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞)当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行 案例 import java.util.concurrent.CountDownLatch; /* * CountDownLatch :闭锁,在完成某些运算是,只有其他所有线程的运算全部完成,当前运算才继续执行 */ public class TestCountDownLatch { public static void main(String[] args) { final CountDownLatch latch = new CountDownLatch(50); LatchDemo ld = new LatchDemo(latch); long start = System.currentTimeMillis(); for (int i = 0; i latch.await(); } catch (InterruptedException e) { } long end = System.currentTimeMillis(); System.out.println("耗费时间为:" + (end - start)); } } class LatchDemo implements Runnable { private CountDownLatch latch; public LatchDemo(CountDownLatch latch) { this.latch = latch; } @Override public void run() { try { for (int i = 0; i System.out.println(i); } } } finally { latch.countDown(); } } } 源码

countDownLatch类中只提供了一个构造器:

//参数count为计数值 public CountDownLatch(int count) { };

类中有三个方法是最重要的:

//调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行 public void await() throws InterruptedException { }; //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行 public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; //将count值减1 public void countDown() { }; 循环栅栏CyclicBarrier

CyclicBarrier看英文单词可以看出大概就是循环阻塞的意思,在使用中CyclicBarrier的构造方法第一个参数是目标障碍数,每次执行CyclicBarrier一次障碍数会加一,如果达到了目标障碍数,才会执行cyclicBarrier.await()之后的语句。可以将CyclicBarrier理解为加1操作

//集齐7颗龙珠就可以召唤神龙 public class CyclicBarrierDemo { //创建固定值 private static final int NUMBER = 7; public static void main(String[] args) { //创建CyclicBarrier CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER,()->{ System.out.println("*****集齐7颗龙珠就可以召唤神龙"); }); //集齐七颗龙珠过程 for (int i = 1; i try { System.out.println(Thread.currentThread().getName()+" 星龙被收集到了"); //等待 cyclicBarrier.await(); } catch (Exception e) { e.printStackTrace(); } },String.valueOf(i)).start(); } } } 信号灯Semaphore

Semaphore的构造方法中传入的第一个参数是最大信号量(可以看成最大线程池),每个信号量初始化为一个最多只能分发一个许可证。使用acquire方法获得许可证,release方法释放许可

//6辆汽车,停3个车位 public class SemaphoreDemo { public static void main(String[] args) { //创建Semaphore,设置许可数量 Semaphore semaphore = new Semaphore(3); //模拟6辆汽车 for (int i = 1; i try { //抢占 semaphore.acquire(); System.out.println(Thread.currentThread().getName()+" 抢到了车位"); //设置随机停车时间 TimeUnit.SECONDS.sleep(new Random().nextInt(5)); System.out.println(Thread.currentThread().getName()+" ------离开了车位"); } catch (InterruptedException e) { e.printStackTrace(); } finally { //释放 semaphore.release(); } },String.valueOf(i)).start(); } } } CAS 算法 CAS (Compare-And-Swap) 是一种硬件对并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并发访问。CAS 是一种无锁的非阻塞算法的实现。CAS 包含了3 个操作数: 需要读写的内存值V进行比较的值A拟写入的新值B 当且仅当V 的值等于A 时,CAS 通过原子方式用新值B 来更新V 的值,否则不会执行任何操作。 案例: /* * 模拟 CAS 算法 */ public class TestCompareAndSwap { public static void main(String[] args) { final CompareAndSwap cas = new CompareAndSwap(); for (int i = 0; i @Override public void run() { int expectedValue = cas.get(); boolean b = cas.compareAndSet(expectedValue, (int) (Math.random() * 101)); System.out.println(b); } }).start(); } } } class CompareAndSwap { private int value; // 获取内存值 public synchronized int get() { return value; } // 比较 public synchronized int compareAndSwap(int expectedValue, int newValue) { int oldValue = value; if (oldValue == expectedValue) { this.value = newValue; } return oldValue; } // 设置 public synchronized boolean compareAndSet(int expectedValue, int newValue) { return expectedValue == compareAndSwap(expectedValue, newValue); } } true false false false false ... 原子变量

类的小工具包,支持在单个变量上解除锁的线程安全编程。事实上,此包中的类可将volatile 值、字段和数组元素的概念扩展到那些也提供原子条件更新操作的类。

类AtomicBoolean、AtomicInteger、AtomicLong 和AtomicReference 的实例各自提供对相应类型单个变量的访问和更新。每个类也为该类型提供适当的实用工具方法。

AtomicIntegerArray、AtomicLongArray 和AtomicReferenceArray 类进一步扩展了原子操作,对这些类型的数组提供了支持。这些类在为其数组元素提供volatile 访问语义方面也引人注目,这对于普通数组来说是不受支持的。

核心方法:boolean compareAndSet(expectedValue, updateValue)

java.util.concurrent.atomic 包下提供了一些原子操作的常用类:

AtomicBoolean 、AtomicInteger 、AtomicLong 、AtomicReferenceAtomicIntegerArray 、AtomicLongArrayAtomicMarkableReferenceAtomicReferenceArrayAtomicStampedReference 案例 import java.util.concurrent.atomic.AtomicInteger; /* * 一、i++ 的原子性问题:i++ 的操作实际上分为三个步骤“读-改-写” * int i = 10; * i = i++; //10 * * int temp = i; * i = i + 1; * i = temp; * * 二、原子变量:在 java.util.concurrent.atomic 包下提供了一些原子变量。 * 1. volatile 保证内存可见性 * 2. CAS(Compare-And-Swap) 算法保证数据变量的原子性 * CAS 算法是硬件对于并发操作的支持 * CAS 包含了三个操作数: * ①内存值 V * ②预估值 A * ③更新值 B * 当且仅当 V == A 时, V = B; 否则,不会执行任何操作。 */ public class TestAtomicDemo { public static void main(String[] args) { AtomicDemo ad = new AtomicDemo(); for (int i = 0; i // private volatile int serialNumber = 0; private AtomicInteger serialNumber = new AtomicInteger(0); @Override public void run() { try { Thread.sleep(200); } catch (InterruptedException e) { } System.out.println(getSerialNumber()); } public int getSerialNumber(){ return serialNumber.getAndIncrement(); } } 1 9 6 3 7 8 ....


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3