本文源自Recently祝祝,创自Recently祝祝。转载请标注出处。
此处理办法在企业中有所运用,适合Java初级开发学习,参考。
本文字数与行数,耐心阅读必有收成。
1.JUC说明
JUC便是java的一个包java.util.concurrent的简称,java中用于并发编程常用的一个东西包,具有强壮的功能,能补偿synchronized缺点,synchronized不能够设置堵塞时长,JUC不仅仅能够设置超时时刻而且还能够主动的中止线程。而且在读多写少的场合JUC能够有多种处理办法,而避免了synchronized加锁带来的开支。
JUC是处理多线程安全一个东西包,供给了非常好用的东西以及类,比方接下来要讲的原子类(Automic),锁(Lock)等处理一些不满足多线程特性的问题,进步程序功能和安全性,跟方便的办理与调度。JUC是Java并发编程不可或缺的部分。
2.JUC运用在哪里
JUC用于支撑高效、安全的多线程编程。详细来说,JUC 包含线程池、原子类、锁(Lock)、并发调集、并发容器、堵塞行列
- 线程池:经过 Executor、ExecutorService、ThreadPoolExecutor 等类,完成线程池的创建、办理和调度,避免了线程频频创建和销毁的开支,进步了多线程程序的功率。
- 原子类:Automic包,java.util.concurrent.automtic,经过 AtomicInteger(整数原子型)、AtomicLong(长整型原子类)、AtomicReference(引证类型原子类) 等类,供给线程安全的原子操作,用法简略功能高效,有效避免Volatie原子性操作变量的问题,避免了多线程并发状况下的数据竞赛和安全问题。
- 并发调集:经过 ConcurrentHashMap、ConcurrentLinkedQueue、ConcurrentSkipListMap 等类,供给线程安全的调集操作,避免了多线程并发状况下的数据竞赛和安全问题。
- 堵塞行列:经过 DelayQueue 类,完成基于时刻的任务调度,支撑在指定时刻后履行任务,避免了运用 Thread.sleep() 的功率问题。
- 锁和条件变量:经过 Lock、Condition 等接口和完成类,供给更灵活、更高效的线程同步机制,支撑更细粒度的锁操控。
3.JUC首要运用
3.1原子类
原子类能够供给线程安全的原子操作,避免多线程并发状况下的数据竞赛和安全问题。
Aotumtic包共13个类,4种类型的原子更新办法(原子更新基本类型、原子更新数组、原子更新引证和原子更新特点(字段))Atomic包里的类基本都是运用Unsafe完成的包装类。
举例:
public class VolatileNoAtomicity {
public static void main(String[] args) throws InterruptedException {
VolatileDemo demo = new VolatileDemo();
for (int i = 0; i < 2; i++) {
Thread t = new Thread(demo);
t.start();
}
Thread.sleep(1000);
System.out.println("count = "+demo.count);
}
static class VolatileDemo implements Runnable {
public volatile int count;
//public volatile AtomicInteger count = new AtomicInteger(0);
public void run() {
addCount();
}
public void addCount() {
for (int i = 0; i < 10000; i++) {
count++;//可是实际状况是三条汇编指令
}
}
}
}
你会发现成果不是20000,由于volatile不具备原子性。
原因剖析:
-
线程1读取count的值为5
-
线程2读取count的值为5
-
线程2加1操作
-
线程2最新count的值为6
-
线程2写入值到主内存的最新值为6
-
线程1履行加1count=6,写入到主内存的值是6。
-
成果:对count进行了两次加1操作,主内存实际上仅仅加1一次。成果为6
-
这样的状况出现屡次之后,就会放成果与预期成果不一致。
处理运用原子操作:
你就会发现,成果变成了20000,这便是原子类的保证了原子性。
3.2什么是CAS
CAS(Compare and Swap:比较替换)一种无锁算法(达观锁机制),能够保证在多线程并发状况下对变量的操作是原子性的。同步组件中大量运用CAS技能完成了Java多线程的并发操作。整个AQS同步组件、Atomic原子类操作等等都是以CAS完成的,乃至ConcurrentHashMap在1.8的版别中也调整为了CAS+Synchronized。能够说CAS是整个JUC的柱石。
CAS 的基本思想是比较当前内存中的值和期望值是否持平,假如持平,则履行操作并更新内存中的值,否则不履行任何操作。实质一个办法调用一行CPU原子操作履行函数:CAS(V,E,N)
当且仅当内存地址V中的值等于 预期值E 时,将内存V中的值改为N,进行暂停一瞬间。
3.3Lock锁
Lock 锁,锁所锁住的是一个目标显式锁机制,与 synchronized 关键字相对应,运用起来粒度更细致,而且更灵活,能够完成公正锁、非公正锁、可重入锁等,同时还能够完成多个条件变量等高级功能。能够完成异步变同步的转换。
3.4什么是AQS
AQS是一个并发基础框架类,用于构建lock、同步器等的底层框架模型。AQS是一个行列,为了让线程来排队,其间每个节点表明一个等候线程,当一个线程请求锁资源时,假如锁已被占用,该线程将被加入到行列中等候,称为线程同步器。本身并不是一种详细的同步器,而是一个供给了同步器完成所需的底层机制的抽象类。完成自定义服务器,也能够运用AQS供给的一些模板来完成同步器的基本功能。
3.5JUC东西类
线程协作东西类,操控线程协作的东西类,让线程之间的协作变得更加简略
1) CountDownLatch倒数门栓
倒数完毕之前,一向处于等候状况,直到数到0完毕,此线程才持续作业。
场景:购物拼团,大巴人满发车,分布式锁
首要办法:
- 结构函数:new CountDownLatch(int count):只要一个结构函数,参数count为需求倒数的数值。
- await():当一个或多个线程调用await()时,这些线程会堵塞。
- countDown():其他线程调用countDown()会将计数器减1,调用countDown办法的线程不会阻 塞。当计数器的值变为0时,因await办法堵塞的线程会被唤醒,持续履行
/**
* CountDownLatch事例:6个程序猿加班
* 当计数器的值变为0时,因await办法堵塞的线程会被唤醒,持续履行
*/
public class Demo11CountDownLatch {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
new Thread(()->{
try { TimeUnit.SECONDS.sleep(5); } catch
(InterruptedException e) {e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + "\t上
完班,离开公司");
countDownLatch.countDown();
}, String.valueOf(i)).start();
}
new Thread(()->{
try {
countDownLatch.await();//卷王也是有极限的,设置超时时刻
System.out.println(Thread.currentThread().getName()+"\t卷王最
后关灯走人");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "7").start();
}
}
2) Semaphore信号量:限流
用来限制或办理数量有限资源的运用状况。 (雪崩)
场景:Hystrix、Sentinel限流
信号量的作用便是保护一个”许可证”的计数,线程能够”获取”许可证,那信号量剩下的许可证就减少一个,线程也能够”开释”一个许可证,那信号量剩下的许可证就能够加一个。当信号量拥有的许可证数为0时,下一个还要要获取许可证的线程就需求等候,直到有别的的线程开释了许可证。
首要办法: 中心办法不多
- 结构函数:
new Semaphore(int permits,Boolean fair)
:能够设置是否运用公正策略,假如传入true,则Semaphore
会把之前等候的线程放到FIFO行列里,以便有了新许可证能够分给之前等候时刻最长 的线程。 -
acquire()
:获取许可证,当一个线程调用acquire操作时,他要么经过成功获取信号量(信号量减 1),要么一向等候下去,直到有线程开释信号量,或超时。 -
release()
:开释许可证,会将信号量加1,然后唤醒等候的线程。
/**
* Semaphore事例:三辆小汽车抢车位
* Semaphore信号量首要作用:1.用于多个共享资源的互斥运用,2.用于并发线程数的操控
*6个线程抢3个车位
*/
public class Demo12Semaphore {
public static void main(String[] args) {
//模拟资源类,有3个空车位
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 6; i++) {
new Thread(()->{
try{
//占有资源
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"\t
抢到车位");
try { TimeUnit.SECONDS.sleep(3); } catch
(InterruptedException e) {e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"\t
泊车3秒后离开车位");
} catch (Exception e) {
e.printStackTrace();
} finally {
//开释资源
semaphore.release();
}
}, "Thread-Car-"+String.valueOf(i)).start();
}
}
}
3)CyclicBarrier循环栅门
线程会等候,直到线程到了事前规定的数目,然后触发履行条件进行下一步动作
场景:并行核算
当有大量线程互相配合,别离核算不同任务,而且需求最后一致汇总时,就能够用CyclicBarrier,它能够结构一个集结点,当某一个线程履行完,它就会到集结点等候,直到一切线程都到集结点,则该栅门 就被吊销,一切线程一致出再,持续履行剩下的任务。
首要办法:
-
结构函数:new CyclicBarrier(int parties, Runnable barrierAction),设置集合的线程数量和集齐线程 数的成果之后要履行的动作。
-
await():堵塞当前线程,待凑齐线程数量之后持续履行
import java.util.concurrent.CyclicBarrier;
/**
* 事例:集齐7龙珠呼唤神龙
*/
public class Demo13CyclicBarrier {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("======呼唤神龙");
});
for (int i = 1; i <= 7; i++) {
final int tempInt = i;
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName() +
"\t收集到第" + tempInt + "颗龙珠");
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName() +
"\t第" + tempInt + "颗龙珠飞走了");
} catch (Exception e) {
e.printStackTrace();
}
}, "Thread-"+String.valueOf(i)).start();
}
}
}
文章全为个人了解,假如发现部分跟你所知道的有出入,欢迎在谈论区指出,欢迎探讨