图片参考:https://www.processon.com/view/link/5e586f5fe4b069f82a16e8a0
package com.xbin;
public class ThreadTest {
private static final Object object=new Object();
private static final Object object2=new Object();
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
System.out.println("t1 NEW 状态:"+t1.getState());
t1.start();
System.out.println("t1 RUNNABLE 状态:"+t1.getState());
Thread.sleep(3000);
System.out.println("t1 TIMED_WAITING 状态:"+t1.getState());
Thread.sleep(3000);
System.out.println("t1 TERMINATED 状态:"+t1.getState());
Thread t2=new Thread(()->{
synchronized (object){
while (true){
}
}
});
Thread t3=new Thread(()->{
synchronized (object){
}
});
t2.start();
Thread.sleep(200);
t3.start();
Thread.sleep(200);
System.out.println("t3 BLOCKED 状态"+t3.getState());
Thread t4=new Thread(()->{
synchronized (object2){
try {
object2.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t5=new Thread(()->{
synchronized (object2){
}
});
t4.start();
Thread.sleep(200);
t5.start();
System.out.println("t4 WAITING 状态 "+t4.getState());
}
}
package com.xbin;
public class ThreadStop extends Thread {
private int i ,j;
@Override
public void run() {
//添加 synchronized 保证原子操作
synchronized (this){
i++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
j++;
}
}
public void print(){
System.out.println("i="+i+",j="+j);
}
}
package com.xbin;
public class ThreadStop1 {
public static void main(String[] args) throws InterruptedException {
ThreadStop threadStop=new ThreadStop();
threadStop.start();
Thread.sleep(200);
//会破坏原子性操作
threadStop.stop();
// threadStop.interrupt();
while (threadStop.isAlive()){
}
threadStop.print();
}
}
从运行结果可以看出这已经破坏了操作的原子性
package com.xbin;
/**
*使用 interrupt 中止线程
*/
public class ThreadStop2 {
public static void main(String[] args) throws InterruptedException {
ThreadStop threadStop=new ThreadStop();
threadStop.start();
Thread.sleep(200);
//会破坏原子性操作
// threadStop.stop();
threadStop.interrupt();
while (threadStop.isAlive()){
}
threadStop.print();
}
}
从结果可以看出这不会破坏操作的原子性
package com.xbin;
/**
* 标志位中止线程
*/
public class ThreadStop3 {
private static volatile boolean flag = true;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
try {
while (flag) {
System.out.println("正在运行------");
Thread.sleep(300);
}
System.out.println("结束运行————————————");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
Thread.sleep(3000);
flag=false;
}
}
**小结:**一般都会使用标识中止线程,有利用程序编写一些事后处理工作。
缓存
L1 Cache 是CPU第一层高速缓存,分为数据缓存和指令缓存。一般服务器CPU的L1缓存的容量通常在32–4096kb.
L2 Cache 由于L1级高速缓存容量的限制,为了再次提高CPU的运算速度,在CPU外部放置一高速存储器,即二级缓存。
L3 Cache 现在的都是内置的。而它的实际作用即是。L3缓存的应用可以通过进一步的降低内存延迟,同时提升大数据量计算时处理器的性能。具有较大L3缓存的处理器提供更有效的文件系统缓存行为及较短消息和处理器队列长度。一般是多核共享一个L3缓存
缓存同步协议
在这种高速缓存回写的场景下,有一个缓存一致性协议多数CPU厂商对它进行了实现。
MESI协议 ,它规定每条缓存有个状态位,同时定义了下面四个状态:
修改态 此cache行已被修改过,内存不同于主存,为此cache专有。
专有态 此cache行内容同于主存,但不出现于其它cache中。
共享态 此cache行内容同于主存,但也出现于其它cache中。
无效态 此cache行内容无效。
多处理器时,单个CPU对缓存中数据进行了改动,需要通知给其他CPU。
CPU处理要控制自己的读写操作,还要监听其他CPU发出的通知,从而保证最终一致。
指令重排的场景:当CPU写缓存时发现缓存区块正被其它CPU占用,为了提高CPU处理性能,可能将后面的读缓存命令优先执行。
并非随便重排,需要遵守as-if-serial语义
as-if-serial语义的意思指:不管怎么重排序(编译器和处理器为了提高并行度),(单线程)程序的执行结果不能被改变。编译器,runtime和处理器都必须遵守as-if-serial语义。
编译器和处理器不会对存在数据依赖关系的操作做重排序。
缓存和指令重排引起的两个问题。
CPU高速缓存问题:缓存中数据和主内存的数据并不是实时同步的,各CPU间缓存的数据也不是实时同步。同一个时间点,各CPU所看到同一内存地址的数据的值可能是不一致的。
CPU执行指令重排序问题:虽然遵守了as-if-serial语义,单仅在单CPU自己执行的情况下能保证结果正确。
多核多线程中,指令逻辑无法分辨因果关联,可能出现乱序执行,导致程序运行结果错误。
内存屏障
处理器提供了两个内存屏障指令用于解决上述两个问题。
写内存屏障;在指令后插入Store Barrier,能让写入缓存中的最新数据更新写入主内存,让其线程可见。强制写入主内存,这种显示调用,CPU就不会因为性能考虑而去对指令重排。
读内存屏障(Load Memory Barrier):在指令前插入Load Barrier,可以让高速缓存中数据失效,强制从新的主内存加载数据。强制读取主内存内容,让CPU缓存与主内存保持一致,避免了缓存导致的一致性问题。
package com.xbin;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
/**
* 文件共享
*/
public class FileSharding {
public static void main(String[] args) {
Thread write = new Thread(() -> {
try {
while (true) {
Files.write(Paths.get("log.txt"), ("当前时间" + System.currentTimeMillis()).getBytes());
Thread.sleep(1000L);
}
} catch (Exception e) {
e.printStackTrace();
}
});
write.start();
Thread read = new Thread(() -> {
try {
while (true) {
Thread.sleep(1000L);
byte[] bytes = Files.readAllBytes(Paths.get("log.txt"));
System.out.println(new String(bytes));
}
} catch (Exception e) {
}
});
read.start();
}
}
package com.xbin;
/**
* 变量共享
*/
public class VariableSharding {
private static String context="";
public static void main(String[] args) {
Thread write =new Thread(()->{
try {
while (true){
context="当前时间:"+System.currentTimeMillis();
Thread.sleep(1000L);
}
}catch (Exception e){
e.printStackTrace();
}
});
write.start();
Thread read =new Thread(()->{
try {
while (true){
Thread.sleep(1000L);
System.out.println(context);
}
}catch (Exception e){
e.printStackTrace();
}
});
read.start();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9LsecR2m-1591524544496)(https://upload-images.jianshu.io/upload_images/22932958-0b7155eac6cc06e0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]
JDK中对于需要多线程协作完成某一任务的场景,提供了对应API支持。
多线程协作的典型场景是:生产者,消费者模型。(线程阻塞,线程唤醒)
suspend和resume的使用
package com.xbin;
public class SuspendResumeTest {
private Object object=null;
/**
* 方法的调用顺序。(如果先调用resume,在调用suspend就会引起死锁)
*/
public void suspendResumeTest03() {
Thread t1 = new Thread(() -> {
while (object == null) {
System.out.println("1.等待包子生产");
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread.currentThread().suspend();
}
System.out.println("3.买到包子了");
});
t1.start();
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
object = new Object();
t1.resume();
System.out.println("2.生产包子");
}
/**
* 对象锁操作 --会导致死锁 --suspend()不会释放锁
*/
public void suspendResumeTest02() {
Thread t1 = new Thread(() -> {
while (object == null) {
synchronized (this) {
System.out.println("1.等待包子生产");
Thread.currentThread().suspend();
}
}
System.out.println("3.买到包子了");
});
t1.start();
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
object = new Object();
synchronized (this) {
t1.resume();
}
System.out.println("2.生产包子");
}
/**
* suspend 和 resume 正常演示
*/
public void suspendResumeTest() {
Thread t1 = new Thread(() -> {
while (object == null) {
System.out.println("1.等待包子生产");
Thread.currentThread().suspend();
}
System.out.println("3.买到包子了");
});
t1.start();
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
object = new Object();
t1.resume();
System.out.println("2.生产包子");
}
public static void main(String[] args) {
SuspendResumeTest suspendResumeTest=new SuspendResumeTest();
// suspendResumeTest.suspendResumeTest03();
// suspendResumeTest.suspendResumeTest02();
suspendResumeTest.suspendResumeTest();
}
}
wait/notify的使用
wait/notify:方法只能由同一对象锁的持有者线程调用,也就是写在同步块里面,否则会抛出IllegalMonitorStateException异常。
wait方法会导致当前线程等待,加入该对象的等待集合中,并且放弃当前持有的对象锁。
notify/notifyAll方法唤醒一个或所有正在等待这个对象锁的线程。
注意:虽然wait会自动解锁,但是对顺序有要求,如果在notify被调用之后,才开始wait方法的调用,线程就会永远处于WAITING状态。
package com.xbin;
public class WaitNotifyTest {
private Object object=null;
/**
* 虽然wait() 和notify() 会释放锁,但是有先后顺序
*/
public void waitNotifyTest02(){
new Thread(() -> {
while (object == null) {
System.out.println("1.等待包子生产");
try {
Thread.sleep(3000L);
synchronized (this) {
this.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("3.成功买到包子");
}).start();
try {
Thread.sleep(200L);
} catch (InterruptedException e) {
e.printStackTrace();
}
object=new Object();
synchronized (this){
this.notify();
}
System.out.println("2.包子生产完成");
}
/**
* 正确地处理方式
*/
public void watiNotifyTest() {
new Thread(() -> {
while (object == null) {
System.out.println("1.等待包子生产");
try {
synchronized (this) {
this.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("3.成功买到包子");
}).start();
try {
Thread.sleep(200L);
} catch (InterruptedException e) {
e.printStackTrace();
}
object=new Object();
synchronized (this){
this.notify();
}
System.out.println("2.包子生产完成");
}
public static void main(String[] args) {
WaitNotifyTest waitNotifyTest=new WaitNotifyTest();
waitNotifyTest.waitNotifyTest02();
// waitNotifyTest.watiNotifyTest();
}
}
pack/unpack机制的使用
pack/unpack机制:线程调用park则等待“许可”,unpack方法为指定线程提供“许可”。
不要求park和unpark方法的调用顺序,多次调用unpark之后,再调用park,线程会直接运行,但不会叠加。
也就是说,连续多次调用park方法,第一次会拿到“许可”直接运行,后续调用会进入等待。
park方法不会释放锁,也可能会引起死锁操作。
package com.xbin;
import java.util.concurrent.locks.LockSupport;
public class ParkUnParkTest {
private Object object=null;
/**
* 会产生死锁 parK() 方法不会释放锁
*/
public void parkUnpackTest02(){
Thread t1=new Thread(()->{
while (object==null){
System.out.println("1.等待包子生产");
synchronized (this){
LockSupport.park();
}
}
System.out.println("3.成功买到包子");
});
t1.start();
try {
Thread.sleep(200L);
} catch (InterruptedException e) {
e.printStackTrace();
}
object=new Object();
synchronized (this){
LockSupport.unpark(t1);
}
System.out.println("2.店家成功生产包子");
}
/**
* park() 和 unpacke() 对调用的方法的先后顺序没有问题。
*/
public void parkUnpackTest(){
Thread t1=new Thread(()->{
while (object==null){
System.out.println("1.等待包子生产");
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
LockSupport.park();
}
System.out.println("3.成功买到包子");
});
t1.start();
try {
Thread.sleep(200L);
} catch (InterruptedException e) {
e.printStackTrace();
}
object=new Object();
LockSupport.unpark(t1);
System.out.println("2.店家成功生产包子");
}
public static void main(String[] args) {
ParkUnParkTest parkUnParkTest=new ParkUnParkTest();
// parkUnParkTest.parkUnpackTest02();
parkUnParkTest.parkUnpackTest();
}
}
伪唤醒
警告!之前的代码中如果用把while改成if语句来判断是否进入等待状态是错误的
官方建议应该在循环中检查等待条件,原因是处于等待状态的线程可能会收到错误警报和伪唤醒,
如果不在循环中检查等待条件,程序就会在没有满足结束条件的情况下退出。
伪唤醒是指线程并非因为notify,notifyall,unpark等api调用而唤醒,是更底层原因导致的。
线程封闭的概念
多线程访问共享可变数据时,涉及到线程间数据同步的问题。并不是所有时候,都要用到共享数据,所以线程封闭概念就提出来了。
数据都被封闭在各自的线程之中,就不需要同步,这种通过将数据封闭在线程中而避免使用同步的技术称为线程封闭。
线程封闭具体的体现有:ThreadLocal,局部变量
ThreadLocal
ThreadLocal是java里一种特殊的变量。
它是一个线程级别变量,每个线程都有一个ThreadLocal就是每个线程都拥有了自己独立的一个变量,竞争条件被彻底消除了,在并发模式下是绝对安全的变量。
用法:ThreadLocal local=new ThreadLocal();
会自动在每一个线程上创建一个T的副本,副本之间彼此独立,互不影响。可以用ThreadLocal存储一些参数,以便在线程中多个方法中使用,用来代替方法传参的做饭。(线程中控制同一数据库连接对象就是使用ThreadLocal)
实在难以理解的,可以理解未,JVM维护了一个Map
栈封闭
局部变量的固有属性之一就是封闭在线程中。它们位于执行线程的栈中,其他线程无法访问这个栈。
######线程池的概念原理
1 线程池管理器:用于创建并管理线程池,包括创建线程池,销毁线程池,添加新任务;
2 工作线程:线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
3 任务接口:每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
4 任务队列:用于存放没有处理的任务。提供一种缓存机制。
方法名称 | 方法描述 |
---|---|
awaitTermination(long timeout,TimeUnit unit) | ExecutorService是否已经关闭,直到所有任务完成执行,或超时发生,或当前线程被中断 |
invokeAll(Collection extends Callable> tasks) | 执行给定的任务集合,执行完毕后,放回结果 |
invokeAll(Collection extends Callable> tasks,long timeout,TimeUnit unit) | 执行给定的任务集合,执行完毕或者超时后,放回结果,其它任务终止 |
invokeAny(Collection extends Callable>tasks ) | 执行给定的任务,任意一个执行成功则返回结果,其他任务终止 |
invokeAny(Collection extends Callable>tasks,long timeout,TimeUnit unit) | 执行给定的任务,任意一个执行成功或者超时后,则返回结果,其他任务终止 |
isShudown() | 如果此线程池已关闭,则返回true |
isTerminated() | 如果关闭后所有任务都已完成,则返回true |
shutdown() | 优雅关闭线程池,之前提交的任务将被执行,但是不会接受新的任务。 |
shutdownNow() | 尝试停止所有正在执行的任务,停止等待任务的处理,并放回等待执行任务的列表 |
submit(Callable task) | 提交一个用于执行的Callable返回任务,并放回一个Future,用于获取Callable执行结果 |
submit(Runnable task) | 提交可运行任务以执行,并放回一个Future对象,执行结果未null |
submit(Runnable task,T result) | 提交可运行任务以执行,并返回Future,执行结果为传入的result |
方法名称 | 方法描述 |
---|---|
schedule(Callable callable,long delay,TimeUnit unit) | 创建并执行一个一次性任务 |
chedule(Runnable command,long delay,TimeUnit unit) | 创建并执行一个一次性任务 |
scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit | 创建并执行一个周期性任务过了给定的初始延迟时间,会第一次被执行,执行过程中发生了异常,那么任务就终止 。(一次任务执行时长超过了周期时间,下一次任务会等到该次任务执行结束后,立即执行,这就是它和scheduleWithFixedDelay的重要区别) |
scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit) | 创建并执行一个周期性任务过了给定的初始延迟时间,会第一次被执行,执行过程中发生了异常,那么任务就终止 。(一次任务执行时长超过了周期时间,下一次任务会在该次任务执行结束的时间基础上,计算执行延迟,对于超过周期的长时间处理任务的不同处理方式,这是它和scheduleAtFixedRate的重要区别) |
方法名称 | 方法描述 |
---|---|
newFixedThreadPool(int nThreads) | 创建一个固定大小,任务队列容量无界的线程池。核心线程数=最大线程数。 |
newCachedThreadPool() | 创建的是一个大小无界的缓冲线程池。它的任务队列是一个同步队列。任务加入到池中,如果池中有空闲线程,则用空闲线程执行,如无则创建新线程执行。池中的线程空闲超过60秒,将被销毁释放。线程数随任务的多少变化,适用于执行耗时较小的异步任务。池的核心线程数=0,最大线程数=Integer.MAX_VALUE |
newsingleThreadExecutor() | 只有一个线程来执行无界任务队列的单一线程池。该线程池确保任务按加入的顺序一个一个依次执行,当唯一的线程因任务异常终止时,将创建一个新的线程来继续执行后续的任务。与newFixedThreadPool(1)的区别在于,单一线程池的池大小在newSingleThreadExecutor方法中硬编码,不能再改变 |
newScheduledThreadPool(int corePoolSize) | 能定时执行任务的线程池。该池的核心线程数由参数指定,最大线程数=Integer.MAX_VALUE |
如果Tomcat中默认的最大线程数为:200.也可考虑根据需要在一个最小数量和最大数量间自动增减线程数。
前面章节中的大部分讨论仅涉及代码得行为,即一次执行单个语句或表达式,即通过单个线程来执行。 JAVA虚拟机可以同时支持多个执行线程,若未正确同步线程的行为,线程的行为可能会出现混淆和违反直觉。
本章描述了多线程程序的语义;它包含了,当多个线程修改了共享内存中的值时,应该读取到哪个值得规则。由于这部分规范类似于不同硬件体系结构的内存模型,因此这些语义称为java编程语言内存模型。
这些语言没有规定如何执行多线程程序。相反,它们描述了允许多线程程序的合法行为。
----《Java语言规范》
package com.xbin;
/**
* 线程安全-可见性问题
*/
public class ThreadSafeDemo {
private static boolean flag=false;
private static int i=0;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while (!flag){
i++;
}
System.out.println(i);
}).start();
Thread.sleep(3000L);
flag=true;
System.out.println("shutdown");
}
}
**结论:**不同的环境产生的结果就不同
上述代码运行的内存分析图
上述程序不i输出i的值得原因,虽然和高速缓存没有关系,但高速缓存会在很短的时间内引起可见性的问题,在一些极限的场景需要注意;产生问题的主要原因跟JIT有关。
java编程语言的语义允许java编译器和微处理器进行执行优化,这些优化导致了与其交互的代码不再同步,从而导致看似矛盾的行为。
脚本语言与编译语言的区别
下面分析JIT编译器对上述代码的行为
上述问题是JIT对重复执行的代码进行优化产生的问题,解决上述的问题是在flag添加volatile关键字修饰。
可见性问题:让一个线程对共享变量的修改,能够及时的被其他线程看到。
Java内存模型规定:
对volatile变量v的写入,与所有其他线程后续对v的读同步
要满足这些条件,所以volatiel关键字就有下面两个功能。
volatile变量的访问控制符会加个ACC_VOLATILE
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.2
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6U4g0yFO-1591524544549)(https://upload-images.jianshu.io/upload_images/22932958-9475b32c28d417ce.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]
可以在线程之间共享的内存称为共享内存或堆内存。
所有实例字段,静态字段和数组元素都存储在堆内存中,这些字段和数组都是标题中提到的共享变量。
冲突:如果至少有一个访问是写操作,那么对同一个变量的两次访问是冲突的。
这些能被多个线程访问的共享变量是内存模型规范的对象。
线程间操作有:
read操作(一般读,即非volatile读)
write操作(一般写,即非volatile写)
volatile read
volatile write
Lock(锁monitor),Unlock
线程的第一个和最后一个操作
外部操作
所以线程间操作,都存在可见性问题,JMM需要对其进行规范
package com.xbin;
/**
* 计数工具类
*/
public class Counter {
int i;
public void add(){
i++;
}
}
package com.xbin;
public class CounterDemo {
public static void main(String[] args) throws InterruptedException {
Counter counter=new Counter();
for (int i = 0; i < 10; i++) {
new Thread(()->{
for (int j = 0; j < 10000; j++) {
counter.add();
}
}).start();
}
Thread.sleep(5000L);
System.out.println(counter.i);
}
}
上述代码按照正常逻辑执行的结果应该是:10000 ,但结果确实小于10000
产生上述问题的原因是i++ 不是原子操作,通过javap -v -p 操作把.class 文件解析成字节码操作指令。
从第2步到第7步就是i++操作的字节码指令。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nPvN4eFg-1591524544596)(https://upload-images.jianshu.io/upload_images/22932958-1bf41bf665f968f3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2Tje4El6-1591524544609)(https://upload-images.jianshu.io/upload_images/22932958-99e8cfbeb9354fe9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]
分析完这些操作后,就可以很明显的发现问题。在多个线程访问的情况下,就会有多个线程同时操作,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ra7P5qCE-1591524544626)(https://upload-images.jianshu.io/upload_images/22932958-f80b4305c83cffa3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]
if(owner==null){
owner=currentThread();
}
public class Counter {
int i;
public synchronized void add(){
i++;
}
}
public class Counter {
int i;
Lock lock=new ReentrantLock();
public synchronized void add(){
lock.lock();
try{
i++;
}finally {
lock.unlock();
}
}
}
public class Counter {
AtomicInteger i=new AtomicInteger(0);
public synchronized void add(){
i.getAndIncrement();
}
}
AtomicInteger的底层使用的就是CAS,首先CAS 是CPU指令级的操作,只有一步原子操作,所以非常快;
package com.xbin.atomic;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
/**
* 使用Unsafe 实现i++操作
*/
public class CounterUnsafe {
private static Unsafe unsafe = null;
private static long valueOff;
int i = 0;
static {
//使用反射获取Unsafe对象
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
unsafe = (Unsafe) theUnsafe.get(null);
//计数类属性的偏移量
Field field = CounterUnsafe.class.getDeclaredField("i");
valueOff = unsafe.objectFieldOffset(field);
} catch (Exception e) {
e.printStackTrace();
}
}
public void add() {
try {
while (true) {//自旋
//获取对象属性的值
int intVolatile = unsafe.getIntVolatile(this, valueOff);
boolean b = unsafe.compareAndSwapInt(this, valueOff, intVolatile, intVolatile + 1);
if (b) {
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.xbin.atomic;
public class CounterDemo {
public static void main(String[] args) throws InterruptedException {
CounterUnsafe counter=new CounterUnsafe();
for (int i = 0; i < 10; i++) {
new Thread(()->{
for (int j = 0; j < 10000; j++) {
counter.add();
}
}).start();
}
Thread.sleep(5000L);
System.out.println(counter.i);
}
}
从上述运行结果中可以得到的结论是:使用Unsafe可以实现i++的原子性。
下图是使用CAS对stack进行操作的过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Howzkgbv-1591524544655)(https://upload-images.jianshu.io/upload_images/22932958-23b0a6262c1ab38c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IaIfiWUB-1591524544661)(https://upload-images.jianshu.io/upload_images/22932958-7ee565a1bee2603b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]
我们期望的结果是thread1线程应该是要执行失败的,应该A早就不是当初的A了,对于这种比较不充分情况,可以添加版本号进行比较。
**特性:**可重入,独享,悲观锁
**锁优化:**锁消除(开启锁消除的参数:-XX:+DoescapeAnalysis -XX:+Eliminacatelocks)
锁粗化 JDK做了锁粗化的优化,但我们自己可从代码层面优化
NOTE:synchronized关键字,不仅实现同步,JMM中规定,synchronized要保证可见性(不能够被缓存)。
锁升级过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DoORmt9Q-1591524544688)(https://upload-images.jianshu.io/upload_images/22932958-94ad4b51b6738f80.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]
synchronized是JVM提供的锁的一种方式,JVM对其进行一些优化。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yeggBHRd-1591524544695)(https://upload-images.jianshu.io/upload_images/22932958-83004b047249bc4a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]
synchronized
ReadWriteLock
概念:维护一对关联锁,一个只用于读操作,一个只用于写操作,读锁可以由多个读线程同时持有,写锁是排他的。同一时间,两把锁不能被不同线程持有。
适用场景:适合读取操作多于写入操作的场景,改进互斥锁的性能,比如:集合的并发线程安全性改造,缓存组件。
锁降级:指的是写锁降级成为读锁。持有写锁的同时,再获取读锁,随后释放写锁的过程。写锁是线程独占,读锁是共享,所以写->读是降级。(读->写,是不能实现的)
package com.xbin.juc;
import java.util.concurrent.Callable;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
public class MyFutureTask<T> implements Runnable{
private static final int NEW=0;
private static final int RUNABLE=1;
private static final int END=2;
private int state;
private AtomicReference<Thread> ower=new AtomicReference<>(null);
private LinkedBlockingDeque<Thread> water=new LinkedBlockingDeque<>();
T result;
private final Callable<T> callabll;
public MyFutureTask(Callable<T> callabll) {
this.callabll = callabll;
state=NEW;
}
@Override
public void run() {
while (true){
if(state!=NEW||!ower.compareAndSet(null,Thread.currentThread())) return;
try {
state=RUNABLE;
result= callabll.call();
} catch (Exception e) {
e.printStackTrace();
}finally {
state=END;
ower.set(null);
}
while (true) {
Thread thread = water.poll();
if (thread == null) return;
LockSupport.unpark(thread);
}
}
}
public T get(){
if(state!=END){
//等待
water.add(Thread.currentThread());
LockSupport.park();
}
return result;
}
}
package com.xbin.juc;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class FutureTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableImpl callable=new CallableImpl();
MyFutureTask<String> stringFutureTask = new MyFutureTask<>(callable);
new Thread(stringFutureTask).start();
// new Thread(stringFutureTask).start();
System.out.println(stringFutureTask.get());
System.out.println(stringFutureTask.get());
}
static class CallableImpl implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("执行callable");
return "操作成功";
}
}
}
/**
*
*/
public class SemaphoreDemo {
// Semaphore 信号量 限流 共享锁
private static Semaphore semaphore=new Semaphore(6);
public static void main(String[] args) {
for (int i = 0; i <1000 ; i++) {
new Thread(()->{
try {
semaphore.acquire(1);
System.out.println("线程正在执行===============================");
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release(1);
}
}).start();
}
}
}
手写Semaphore
package cn.tk.myproject.lock;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
public class MySemaphore {
private Sync sync;
public MySemaphore(int permit ) {
this.sync=new Sync(permit);
}
public void acquire(int arg){
sync.acquireShared(arg);
}
public void release(int arg){
sync.releaseShared(arg);
}
class Sync extends AbstractQueuedSynchronizer {
private int permit;
public Sync(int permit) {
this.permit = permit;
}
@Override
protected int tryAcquireShared(int arg) {
for (;;) {
int state = getState();
int next = state + arg;
if (next <= permit) {
//可以获取锁
if (compareAndSetState(state, next)) {
return arg;
}
}
}
}
@Override
protected boolean tryReleaseShared(int arg) {
for (;;){
int state = getState();
if(state==0){
return false;
}
if(compareAndSetState(state,state-arg)){
return true;
}
return false;
}
}
}
}
用法1:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
private static CountDownLatch countDownLatch=new CountDownLatch(4);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 3; i++) {
new Thread(()->{
countDownLatch.countDown();
System.out.println("开始执行代码");
}).start();
}
Thread.sleep(2000L);
countDownLatch.await();
System.err.println("成功执行完毕");
}
}
用法2:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
private static CountDownLatch countDownLatch=new CountDownLatch(1);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 6; i++) {
new Thread(()->{
try {
System.out.println("准备执行!!");
countDownLatch.await();
System.out.println("开始执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
Thread.sleep(3000L);
countDownLatch.countDown();
}
}
手写CountDownLatch
package cn.tk.myproject.lock;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
/**
* 共享锁,从指定值开始下降
*/
public class MyCountDownLatch {
private final Sync sync;
public MyCountDownLatch(int count) {
this.sync=new Sync(count);
}
public void await(){
sync.acquireShared(1);
}
public void countDown(){
sync.releaseShared(1);
}
/**
* CountDownLatch就可以看做是一个共享锁
* 初始状态,这个共享锁被获取了n次,
* 每次countdown,相当于释放一次锁
* 当锁释放完后,其他线程才能再次获得锁
*/
class Sync extends AbstractQueuedSynchronizer{
public Sync(int count) {
setState(count);
}
@Override
protected int tryAcquireShared(int arg) {
return (getState() == 0) ? 1 : -1;
}
@Override
protected boolean tryReleaseShared(int arg) {
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
}
/**
* 需要线程数达到要求才会被执行
*/
public class CyclicBarrierDemo {
private static CyclicBarrier cyclicBarrier=new CyclicBarrier(4);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 100; i++) {
new Thread(()->{
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("正在执行============================");
}).start();
Thread.sleep(300L);
}
}
}
CyclicBarrier源码分析
package cn.tk.myproject.lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class MyCyclecbarrier {
private ReentrantLock reentrantLock=new ReentrantLock();
private Condition condition=reentrantLock.newCondition();
private int count ;
private final int parties;
private Object gentation=new Object();
public MyCyclecbarrier(int parties) {
this.parties = parties;
this.count=this.parties;
}
public void await(){
final ReentrantLock lock =this.reentrantLock;
lock.lock();
try{
final Object g=gentation;
int index=--count;
if(index==0){
//结束等待
//唤醒等待队列
nextGention();
return;
}
//否则进入等待
for (;;){//防止伪唤醒
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
if(g!=gentation){
return;
}
}
}finally {
lock.unlock();
}
}
private void nextGention() {
condition.signalAll();
count=parties;//重置数量
gentation=new Object();
}
}