JUC
1.进程,线程,管程
#进程和线程区别
根本区别:进程是`系统进行资源分配(如地址和文件等)和调度的基本单位`;线程是`CPU调度和分派的基本单位`。
切换:上下文切换包含了寄存器的存储和程序计数器存储的指令内容。进程切换与线程切换的一个最主要区别就在于进程切换涉及到虚拟地址空间的切换而线程切换则不会
#区别
进程 :是操作系统进行资源分配和调度的一个基本单位。
线程 :是CPU调度和分派的基本单位,一个进程可以拥有多个线程。
协程 :是一种用户态的轻量级线程,一个线程也可以拥有多个协程。
管程 :是一种程序结构,结构内的多个子程序(对象或模块)形成的多个工作线程互斥访问共享资源。
1.1守护线程
import java.util.concurrent.TimeUnit;
/**
* 守护线程
*/
public class DaemonDemo {
public static void main(String[] args)//一切方法运行的入口
{
Thread t1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t 开始运行, " +
(Thread.currentThread().isDaemon() ? "守护线程" : "用户线程"));
while (true) {
}
}, "t1");
t1.start();
t1.setDaemon(true);//t1是守护线程
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t ----end 主线程");
}
}
2.CompletableFuture
FutureTask基础使用
import java.util.concurrent.*;
/**
* CompletableFuture 异步多线程带返回值
*/
public class CompletableFuture
{
public static void main(String[] args) throws ExecutionException, InterruptedException
{
//异步
FutureTask<String> futureTask = new FutureTask<>(new MyThread());
Thread t1 = new Thread(futureTask,"t1");
t1.start();
//获取返回值
System.out.println(futureTask.get());
}
}
class MyThread implements Callable<String>
{
@Override
public String call() throws Exception
{
System.out.println("-----come in call() " );
return "hello Callable";
}
}
FutureTask 结合线程池
/**
* FutureTask 结合线程池
*/
public class FutureThreadPool {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService threadPool = Executors.newFixedThreadPool(3);
FutureTask<String> futureTask1 = new FutureTask<String>(() -> {
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "task1 over";
});
threadPool.submit(futureTask1);
System.out.println(futureTask1.get());
threadPool.shutdown();
}
}
FutureTask->get方法阻塞线程
//get容易导致阻塞,一般建议放在程序后面,
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* get容易导致阻塞
*/
public class FutureAPIDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
FutureTask<String> futureTask = new FutureTask<String>(() -> {
System.out.println(Thread.currentThread().getName() + "\t -----come in");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "task over";
});
Thread t1 = new Thread(futureTask, "t1");
t1.start();
System.out.println(Thread.currentThread().getName() + "\t ----忙其它任务了");
//get方法要放在主线程后面,否则会阻塞主线程
//System.out.println(futureTask.get());
//System.out.println(futureTask.get(3,TimeUnit.SECONDS));//时间到没完成会抛异常
//实际用法
while (true) {
if (futureTask.isDone()) {
System.out.println(futureTask.get());
break;
} else {
//轮询获取结果
//暂停毫秒
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("正在处理中,不要再催了,越催越慢 ,再催熄火");
}
}
}
}
CompletableFuture异步有返回值/无返回值
import java.util.concurrent.*;
/**
* completableFuture runAsync异步无返回值
* completableFuture supplyAsync异步有返回值
*/
public class CompletableFutureBuildDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService threadPool = Executors.newFixedThreadPool(3);
/*CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName());
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
},threadPool);
System.out.println(completableFuture.get());*/
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName());
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello supplyAsync";
}, threadPool);//自定义线程池
System.out.println(completableFuture.get());
//join和get 一样,只是不会抛出异常
System.out.println(completableFuture.join());
threadPool.shutdown();
}
}
CompletableFuture 异步线程池
import java.util.concurrent.*;
/**
*CompletableFuture 异步线程池
*/
public class CompletableFutureUseDemo{
public static void main(String[] args) throws ExecutionException, InterruptedException{
ExecutorService threadPool = Executors.newFixedThreadPool(3);
try{
CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "----come in");
int result = ThreadLocalRandom.current().nextInt(10);
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("-----1秒钟后出结果:" + result);
if(result > 2)
{
int i=10/0;
}
return result;
// threadPool 这里使用自定义线程池,如果不传参使用默认线程池 。一定要使用自定义线程池,否则whenComplete可能会没有结果
//v: 上一步的结果 e: 异常信息
},threadPool).whenComplete((v,e) -> { //没有异常执行这一段
if (e == null) {
System.out.println("-----计算完成,更新系统UpdateValue:"+v);
}
}).exceptionally(e -> { //有异常执行这一段
e.printStackTrace();
System.out.println("异常情况:"+e.getCause()+"\t"+e.getMessage());
return null;
});
System.out.println(Thread.currentThread().getName()+"线程先去忙其它任务");
}catch (Exception e){
e.printStackTrace();
}finally {
threadPool.shutdown();
}
}
}
案例
import lombok.*;
import lombok.experimental.Accessors;
import java.awt.print.Book;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
*
* 案例说明:电商比价需求,模拟如下情况:
*
* 1需求:
* 1.1 同一款产品,同时搜索出同款产品在各大电商平台的售价;
* 1.2 同一款产品,同时搜索出本产品在同一个电商平台下,各个入驻卖家售价是多少
*
* 2输出:出来结果希望是同款产品的在不同地方的价格清单列表,返回一个List<String>
* 《mysql》 in jd price is 88.05
* 《mysql》 in dangdang price is 86.11
* 《mysql》 in taobao price is 90.43
*
* 3 技术要求
* 3.1 函数式编程
* 3.2 链式编程
* 3.3 Stream流式计算
*/
public class CompletableFutureMallDemo
{
static List<NetMall> list = Arrays.asList(
new NetMall("jd"),
new NetMall("dangdang"),
new NetMall("taobao"),
new NetMall("pdd"),
new NetMall("tmall")
);
/**
* step by step 一家家搜查
* List<NetMall> ----->map------> List<String>
* @param list
* @param productName
* @return
*/
public static List<String> getPrice(List<NetMall> list,String productName)
{
//《mysql》 in taobao price is 90.43
return list
.stream()
.map(netMall ->
String.format(productName + " in %s price is %.2f",
netMall.getNetMallName(),
netMall.calcPrice(productName)))
.collect(Collectors.toList());
}
/**
* List<NetMall> ----->List<CompletableFuture<String>>------> List<String>
* @param list
* @param productName
* @return
*/
public static List<String> getPriceByCompletableFuture(List<NetMall> list,String productName)
{
return list.stream().map(netMall ->
CompletableFuture.supplyAsync(() -> String.format(productName + " in %s price is %.2f",
netMall.getNetMallName(),
netMall.calcPrice(productName))))
.collect(Collectors.toList())
.stream()
.map(s -> s.join())
.collect(Collectors.toList());
}
public static void main(String[] args)
{
long startTime = System.currentTimeMillis();
List<String> list1 = getPrice(list, "mysql");
for (String element : list1) {
System.out.println(element);
}
long endTime = System.currentTimeMillis();
System.out.println("----costTime: "+(endTime - startTime) +" 毫秒");
System.out.println("--------------------");
long startTime2 = System.currentTimeMillis();
List<String> list2 = getPriceByCompletableFuture(list, "mysql");
for (String element : list2) {
System.out.println(element);
}
long endTime2 = System.currentTimeMillis();
System.out.println("----costTime: "+(endTime2 - startTime2) +" 毫秒");
}
}
class NetMall
{
@Getter
private String netMallName;
public NetMall(String netMallName)
{
this.netMallName = netMallName;
}
public double calcPrice(String productName)
{
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
return ThreadLocalRandom.current().nextDouble() * 2 + productName.charAt(0);
}
}
常用API(获取结果)
package com.bilibili.juc.cf;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* CompletableFuture 返回值 API
*/
public class CompletableFutureAPIDemo
{
public static void main(String[] args) throws ExecutionException, InterruptedException
{
}
/**
* 获得结果和触发计算
* @throws InterruptedException
* @throws ExecutionException
*/
private static void group1() throws InterruptedException, ExecutionException
{
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "abc";
});
//join不会抛出异常 , get会
//System.out.println(completableFuture.get());
//System.out.println(completableFuture.get(2L,TimeUnit.SECONDS));
//System.out.println(completableFuture.join());
//暂停几秒钟线程
//try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
//如果没有计算完成,则返回xxx这个值
//System.out.println(completableFuture.getNow("xxx"));
//.complete(打断计算) 如果完成计算则返回计算值,返回true 如果没有计算完成则返回false,返回 completeValue 这个值
System.out.println(completableFuture.complete("completeValue")+"\t"+completableFuture.get());
}
}
常用API(对结果进行续处理、thenApply、handle)
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* thenApply 有异常会中断续传
* handle 有异常也会往下续传
*/
public class CompletableFutureAPI2Demo
{
public static void main(String[] args)
{
ExecutorService threadPool = Executors.newFixedThreadPool(3);
CompletableFuture.supplyAsync(() ->{
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("111");
return 1;
},threadPool).handle((f,e) -> { //thenApply 有异常会中断续传
int i=10/0;
System.out.println("222");
return f + 2;
}).handle((f,e) -> { //handle 有异常也会往下续传
System.out.println("333");
return f + 3;
}).whenComplete((v,e) -> {
if (e == null) {
System.out.println("----计算结果: "+v);
}
}).exceptionally(e -> {
e.printStackTrace();
System.out.println(e.getMessage());
return null;
});
System.out.println(Thread.currentThread().getName()+"----主线程先去忙其它任务");
threadPool.shutdown();
}
}
常用API(对结果消费thenRun/thenAccept/thenApply)
theRUN : 任务A执行完执行B,并且`B不需要A的结果`
theAccept : 任务A执行完执行B,`B需要A的结果,但是任务B无返回值`
theApply : 任务A执行完成执行B,`B需要A的结果,同时任务B有返回值`
import java.util.concurrent.CompletableFuture;
/**
* thenRun/thenAccept/thenApply
*/
public class CompletableFutureAPI3Demo
{
public static void main(String[] args)
{
/*CompletableFuture.supplyAsync(() -> {
return 1;
}).thenApply(f ->{
return f + 2;
}).thenApply(f ->{
return f + 3;
}).thenAccept(System.out::println);*/
//resultA
System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenRun(() -> {}).join());
//null
System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenAccept(r -> System.out.println(r)).join());
//resultAresultB
System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenApply(r -> r + "resultB").join());
}
}
CompletableFuture和线程池
package com.bilibili.juc.cf;
import java.util.concurrent.*;
/**
* CompletableFuture和线程池
* //传入自定义线程池,不要用thenRunAsync,否则后面用的都是默认线程池,只有使用thenRun,后面用的才是自定义线程池
*/
public class CompletableFutureWithThreadPoolDemo
{
public static void main(String[] args)
{
ExecutorService threadPool = Executors.newFixedThreadPool(5);
try
{
CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
try { TimeUnit.MILLISECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("1号任务" + "\t" + Thread.currentThread().getName());
return "abcd";
//传入自定义线程池,不要用thenRunAsync,否则后面用的都是默认线程池,只有使用thenRun,后面用的才是自定义线程池
},threadPool).thenRun(() -> {
try { TimeUnit.MILLISECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("2号任务" + "\t" + Thread.currentThread().getName());
}).thenRun(() -> {
try { TimeUnit.MILLISECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("3号任务" + "\t" + Thread.currentThread().getName());
}).thenRun(() -> {
try { TimeUnit.MILLISECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("4号任务" + "\t" + Thread.currentThread().getName());
});
System.out.println(completableFuture.get(2L, TimeUnit.SECONDS));
}catch (Exception e){
e.printStackTrace();
}finally {
threadPool.shutdown();
}
}
}
CompletableFuture_计算速度选用
package com.bilibili.juc.cf;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
/**
* playA.applyToEither(playB
* 比较那个快
*/
public class CompletableFutureFastDemo
{
public static void main(String[] args)
{
CompletableFuture<String> playA = CompletableFuture.supplyAsync(() -> {
System.out.println("A come in");
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
return "playA";
});
CompletableFuture<String> playB = CompletableFuture.supplyAsync(() -> {
System.out.println("B come in");
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
return "playB";
});
CompletableFuture<String> result = playA.applyToEither(playB, f -> {
return f + " is winer";
});
System.out.println(Thread.currentThread().getName()+"\t"+"-----: "+result.join());
}
}
CompletableFuture之对计算结果合并
package com.bilibili.juc.cf;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
/**
* 异步线程结果合并
*completableFuture1.thenCombine(completableFuture2,
*/
public class CompletableFutureCombineDemo
{
public static void main(String[] args)
{
CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "\t ---启动");
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 10;
});
CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "\t ---启动");
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 20;
});
//(x, y) 代表两个异步线程return 的结果
CompletableFuture<Integer> result = completableFuture1.thenCombine(completableFuture2, (x, y) -> {
System.out.println("-----开始两个结果合并");
return x + y;
});
System.out.println(result.join());
}
}
3.锁
synchronized
class Phone //资源类{
public static synchronized void sendEmail(){
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("-----sendEmail");
}
public synchronized void sendSMS(){
System.out.println("-----sendSMS");
}
public void hello(){
System.out.println("-------hello");
}
}
#笔记总结: 视频32
1-2
一个对象里面如果有多个synchronized方法,`某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,其它的线程都只能等待`,
`这些synchronized方法锁的是当前对象this`,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
3-4
加个普通方法后发现和同步锁无关 # phone.hello();
换成两个对象后,不是同一把锁了,情况立刻变化。# Phone phone = new Phone(); Phone phone2 = new Phone();
5-6
都换成静态同步方法后,情况又变化 # (public static synchronized void sendEmail())
三种 synchronized 锁的内容有一些差别:
对于`普通同步方法,锁的是当前实例对象(new出来的)`,通常指this,所有的普通同步方法用的都是同一把锁—>实例对象本身
对于`静态同步方法,锁的是当前类的Class对象(类)`,如Phone.class唯一的一个模板 # (static synchronized )
对于`同步方法块,锁的是 synchronized 括号内的对象`
7-8
当一个线程试图访问同步代码时它首先必须得到锁,正常退出或抛出异常时必须释放锁。
`所有的普通同步方法用的都是同一把锁——实例对象本身`,就是new出来的具体实例对象本身,本类this,也就是说如果一个实例对象的普通同步方法获取锁后,该实例对象的其他普通同步方法必须等待获取锁的方法释放锁后才能获取锁。
所有的静态同步方法用的也是同一把锁——类对象本身,就是我们说过的唯一模板Class,具体实例对象this和唯一模板Class,这两把锁是两个不同的对象,所以静态同步方法与普通同步方法之间是不会有竞态条件的
但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁。
synchronized字节码分析
//synchronized静态代码块
通过javac -p xxx.class 编译字节码文件
synchronized 一般情况是monitorenter 和monitorexit指令
一般情况是一个tenter对应两个exit
//synchronized普通同步方法.
javac -v xxx.class
会生成ACC_synchronized
//静态同步方法
会生成ACC_synchronized 和ACC_STATIC
公平锁和非公平锁
为什么默认是非公平锁?
//Lock lock = new ReentrantLock();
1.非公平锁可以更充分的利用cpu的时间片,尽量减少cpu的空闲状态时间
2.多线程时减少线程开销
可重入锁
synchronized 和 ReentrantLock都是可重入锁
ReentrantLock 调用的时候必须lock对应unlock,加锁几次,就需要释放锁几次
死锁及排查
jps -l 查出进程号
jstack 进程号 //查看死锁原因(栈信息)
或者使用jconsole 图形化工具
中断机制
1.isInterrupted/interrupt
package com.bilibili.juc.interrupt;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* isInterrupted() 该方法就是直接调用当前线程的isInterrupted(true)方法。
* t1.interrupt() 仅仅设置一个线程中断位
* t1.interrupt()调用了这个的方法就会设置isInterrupted为true
*
*/
public class InterruptDemo
{
static volatile boolean isStop = false;
static AtomicBoolean atomicBoolean = new AtomicBoolean(false);
public static void main(String[] args)
{
Thread t1 = new Thread(() -> {
while (true)
{
if(Thread.currentThread().isInterrupted()) //判断是否是种植当前线程
{
System.out.println(Thread.currentThread().getName()+"\t isInterrupted()被修改为true,程序停止");
break;
}
System.out.println("t1 -----hello interrupt api");
}
}, "t1");
t1.start();
System.out.println("-----t1的默认中断标志位:"+t1.isInterrupted());
//暂停毫秒
try { TimeUnit.MILLISECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }
//t2向t1发出协商,将t1的中断标志位设为true希望t1停下来
new Thread(() -> {
t1.interrupt();
},"t2").start();
//t1.interrupt();
}
private static void m2_atomicBoolean()
{
new Thread(() -> {
while (true)
{
if(atomicBoolean.get())
{
System.out.println(Thread.currentThread().getName()+"\t atomicBoolean被修改为true,程序停止");
break;
}
System.out.println("t1 -----hello atomicBoolean");
}
},"t1").start();
//暂停毫秒
try { TimeUnit.MILLISECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(() -> {
atomicBoolean.set(true);
},"t2").start();
}
private static void m1_volatile()
{
new Thread(() -> {
while (true)
{
if(isStop)
{
System.out.println(Thread.currentThread().getName()+"\t isStop被修改为true,程序停止");
break;
}
System.out.println("t1 -----hello volatile");
}
},"t1").start();
//暂停毫秒
try { TimeUnit.MILLISECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(() -> {
isStop = true;
},"t2").start();
}
}
2.interrupt异常处理
package com.bilibili.juc.interrupt;
import java.util.concurrent.TimeUnit;
/**
* 调用 interrupt 方法 有sleep wait等方法的时候,程序不停止,需要在catch里面修改interrupt状态
*/
public class InterruptDemo3
{
public static void main(String[] args)
{
Thread t1 = new Thread(() -> {
while (true)
{
if(Thread.currentThread().isInterrupted())
{
System.out.println(Thread.currentThread().getName()+"\t " +
"中断标志位:"+Thread.currentThread().isInterrupted()+" 程序停止");
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();//为什么要在异常处,再调用一次??
e.printStackTrace();
}
System.out.println("-----hello InterruptDemo3");
}
}, "t1");
t1.start();
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(() -> t1.interrupt(),"t2").start();
}
}
/**
* 1 中断标志位,默认false
* 2 t2 ----> t1发出了中断协商,t2调用t1.interrupt(),中断标志位true
* 3 中断标志位true,正常情况,程序停止,^_^
* 4 中断标志位true,异常情况,InterruptedException,将会把中断状态将被清除,并且将收到InterruptedException 。中断标志位false
* 导致无限循环
*
* 5 在catch块中,需要再次给中断标志位设置为true,2次调用停止程序才OK
*/
interrupted 和 isInterrupted区别
这两个方法有两个主要区别:
静态方法interrupted 会清除中断状态(传入的参数ClearInterrupted 为true)
实例方法isInterrupter不会(传入的参数ClearInterrupted为false)
LockSupport
线程阻塞的工具类 park()/unpark()
LockSupport之(wait/notify)(await/signal)实现等待和唤醒实现等待和唤醒
package com.bilibili.juc.LockSupport;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
/**
* LockSupport
* 1.wait和 notify必须放在synchronized 里面
* 2.Condition 中的线程等待和唤醒方法 需要先获取锁 , 先 await后signal
* 3.LockSupport.park()/unpark() 支持先唤醒后等待, 可不用同步代码块
*/
@SuppressWarnings("all")
public class LockSupportDemo
{
static int x = 0;
static int y = 0;
public static void main(String[] args)
{
Thread t1 = new Thread(() -> {
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + "\t ----come in"+System.currentTimeMillis());
LockSupport.park(); //只能有一个通行证,不能使用多个,许可证不会累积
System.out.println(Thread.currentThread().getName() + "\t ----被唤醒"+System.currentTimeMillis());
}, "t1");
t1.start();
//暂停几秒钟线程
//try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(() -> {
LockSupport.unpark(t1);//唤醒哪个线程
System.out.println(Thread.currentThread().getName()+"\t ----发出通知");
},"t2").start();
}
private static void lockAwaitSignal()
{
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
new Thread(() -> {
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
lock.lock();
try
{
System.out.println(Thread.currentThread().getName()+"\t ----come in");
condition.await();
System.out.println(Thread.currentThread().getName()+"\t ----被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
},"t1").start();
//暂停几秒钟线程
//try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(() -> {
lock.lock();
try
{
condition.signal();
System.out.println(Thread.currentThread().getName()+"\t ----发出通知");
}finally {
lock.unlock();
}
},"t2").start();
}
private static void syncWaitNotify()
{
Object objectLock = new Object();
new Thread(() -> {
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
synchronized (objectLock){
System.out.println(Thread.currentThread().getName()+"\t ----come in");
try {
objectLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"\t ----被唤醒");
}
},"t1").start();
//暂停几秒钟线程
//try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(() -> {
synchronized (objectLock){
objectLock.notify();
System.out.println(Thread.currentThread().getName()+"\t ----发出通知");
}
},"t2").start();
}
}
Volatile
可见性,有序性
内存屏障
可见性
package com.bilibili.juc.volatiles;
import java.util.concurrent.TimeUnit;
/**
* volatile 可见性
*/
public class VolatileSeeDemo
{
//static boolean flag = true;
static volatile boolean flag = true;
public static void main(String[] args)
{
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"\t -----come in");
while(flag)
{
}
System.out.println(Thread.currentThread().getName()+"\t -----flag被设置为false,程序停止");
},"t1").start();
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
flag = false;
System.out.println(Thread.currentThread().getName()+"\t 修改完成flag: "+flag);
}
}
没有原子性
package com.bilibili.juc.volatiles;
import java.util.concurrent.TimeUnit;
/**
* public synchronized void addPlusPlus()
* volatile 不具有原子性
*/
class MyNumber
{
volatile int number;
public void addPlusPlus()
{
number++;
}
}
/**
* @auther zzyy
* @create 2022-02-23 16:54
*/
public class VolatileNoAtomicDemo
{
public static void main(String[] args)
{
MyNumber myNumber = new MyNumber();
for (int i = 1; i <=10; i++) {
new Thread(() -> {
for (int j = 1; j <=1000; j++) {
myNumber.addPlusPlus();
}
},String.valueOf(i)).start();
}
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(myNumber.number);
}
}
指令禁重排
DCL双端检锁
//通过volatile 对单例模式加锁来保证对象不被线程突然初始化
@Data
public class Singleton {
private static Singleton singleton;
/**
* 一
*/
private String one;
/**
* 二
*/
private String two;
/**
* 三
*/
private String three;
/**
* 四
*/
private String four;
private Singleton() {
this.one = "三万里河东入海";
this.two = "五千仞岳上摩天";
this.three = "遗民泪尽胡尘里";
this.four = "南望王师又一年";
}
/**
* 获取实例
*
* @return 单例类对象
*/
public static Singleton getInstance() {
// 一次检查(非同步)。
if (Objects.isNull(singleton)) {
synchronized (Singleton.class) {
// 二次检查(同步)。
if (Objects.isNull(singleton)) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
CAS
CAS代码
package com.bilibili.juc.cas;
import java.util.concurrent.atomic.AtomicInteger;
/**
* cas 比较交换
*/
public class CASDemo
{
public static void main(String[] args)
{
AtomicInteger atomicInteger = new AtomicInteger(5);
System.out.println(atomicInteger.compareAndSet(5, 2022)+"\t"+atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(5, 2022)+"\t"+atomicInteger.get());
atomicInteger.getAndIncrement();
}
}
AtomicReference自定义
package com.bilibili.juc.cas;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
import java.util.concurrent.atomic.AtomicReference;
@Getter
@ToString
@AllArgsConstructor
class User
{
String userName;
int age;
}
/**
* AtomicReference 自定义Atomic类
*/
public class AtomicReferenceDemo
{
public static void main(String[] args)
{
AtomicReference<User> atomicReference = new AtomicReference<>();
User z3 = new User("z3",22);
User li4 = new User("li4",28);
atomicReference.set(z3);
System.out.println(atomicReference.compareAndSet(z3, li4)+"\t"+atomicReference.get().toString());
System.out.println(atomicReference.compareAndSet(z3, li4)+"\t"+atomicReference.get().toString());
}
}
CAS自己实现自旋锁
package com.bilibili.juc.cas;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* 题目:实现一个自旋锁,复习CAS思想
* 自旋锁好处:循环比较获取没有类似wait的阻塞。
*
* 通过CAS操作完成自旋锁,A线程先进来调用myLock方法自己持有锁5秒钟,B随后进来后发现
* 当前有线程持有锁,所以只能通过自旋等待,直到A释放锁后B随后抢到。
*/
public class SpinLockDemo
{
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void lock()
{
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"\t"+"----come in");
while (!atomicReference.compareAndSet(null, thread)) {
}
}
public void unLock()
{
Thread thread = Thread.currentThread();
atomicReference.compareAndSet(thread,null);
System.out.println(Thread.currentThread().getName()+"\t"+"----task over,unLock...");
}
public static void main(String[] args)
{
SpinLockDemo spinLockDemo = new SpinLockDemo();
new Thread(() -> {
spinLockDemo.lock();
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
spinLockDemo.unLock();
},"A").start();
//暂停500毫秒,线程A先于B启动
try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(() -> {
spinLockDemo.lock();
spinLockDemo.unLock();
},"B").start();
}
}
CAS缺点
1.如果没有成功,会一直自旋,增加cpu负担
2.CAS ABA 增加版本号解决
CAS-> ABA问题解决
package com.bilibili.juc.cas;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* CAS ABA问题 使用AtomicStampedReference 解决
*/
@SuppressWarnings("all")
public class ABADemo
{
static AtomicInteger atomicInteger = new AtomicInteger(100);
//初始值 版本号
static AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(100,1);
public static void main(String[] args)
{
new Thread(() -> {
int stamp = stampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"\t"+"首次版本号:"+stamp);
//暂停500毫秒,保证后面的t4线程初始化拿到的版本号和我一样
try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }
stampedReference.compareAndSet(100,101,stampedReference.getStamp(),stampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+"\t"+"2次流水号:"+stampedReference.getStamp());
stampedReference.compareAndSet(101,100,stampedReference.getStamp(),stampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+"\t"+"3次流水号:"+stampedReference.getStamp());
},"t3").start();
new Thread(() -> {
int stamp = stampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"\t"+"首次版本号:"+stamp);
//暂停1秒钟线程,等待上面的t3线程,发生了ABA问题
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
boolean b = stampedReference.compareAndSet(100, 2022, stamp, stamp + 1);
System.out.println(b+"\t"+stampedReference.getReference()+"\t"+stampedReference.getStamp());
},"t4").start();
}
private static void abaHappen()
{
new Thread(() -> {
atomicInteger.compareAndSet(100,101);
try { TimeUnit.MILLISECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
atomicInteger.compareAndSet(101,100);
},"t1").start();
new Thread(() -> {
try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(atomicInteger.compareAndSet(100, 2022)+"\t"+atomicInteger.get());
},"t2").start();
}
}
原子操作类
AtomicInteger
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
class MyNumber
{
AtomicInteger atomicInteger = new AtomicInteger();
public void addPlusPlus()
{
atomicInteger.getAndIncrement();
}
}
/**
* @auther zzyy
* @create 2022-02-25 21:59
*/
public class AtomicIntegerDemo
{
public static final int SIZE = 50; //50个线程
public static void main(String[] args) throws InterruptedException
{
MyNumber myNumber = new MyNumber();
CountDownLatch countDownLatch = new CountDownLatch(SIZE);
for (int i = 1; i <=SIZE; i++) {
new Thread(() -> {
try {
for (int j = 1; j <=1000; j++) {
myNumber.addPlusPlus();
}
} finally {
countDownLatch.countDown();
}
},String.valueOf(i)).start();
}
//等待上面50个线程全部计算完成后,再去获得最终值
countDownLatch.await();
System.out.println(Thread.currentThread().getName()+"\t"+"result: "+myNumber.atomicInteger.get());
}
}
AtomicIntegerArray
package com.bilibili.juc.atomics;
import java.util.concurrent.atomic.AtomicIntegerArray;
/**
* AtomicIntegerArray
*/
public class AtomicIntegerArrayDemo
{
public static void main(String[] args)
{
AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[5]);
//AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(5);
//AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[]{1,2,3,4,5});
for (int i = 0; i <atomicIntegerArray.length(); i++) {
System.out.println(atomicIntegerArray.get(i));
}
System.out.println();
int tmpInt = 0;
//0号索引设置 为1122
tmpInt = atomicIntegerArray.getAndSet(0,1122);
System.out.println(tmpInt+"\t"+atomicIntegerArray.get(0));
//获取0好索引的值 并 +1
tmpInt = atomicIntegerArray.getAndIncrement(0);
System.out.println(tmpInt+"\t"+atomicIntegerArray.get(0));
}
}
AtomicMarkableReference
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicMarkableReference;
/**
* AtomicMarkableReference 使用ture false 解决cas aba问题
*/
public class AtomicMarkableReferenceDemo
{
//初始值100,没人修改过 false
static AtomicMarkableReference markableReference = new AtomicMarkableReference(100,false);
public static void main(String[] args)
{
new Thread(() -> {
//目前修改标识位
boolean marked = markableReference.isMarked();
System.out.println(Thread.currentThread().getName()+"\t"+"默认标识:"+marked);
//暂停1秒钟线程,等待后面的T2线程和我拿到一样的模式flag标识,都是false
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
markableReference.compareAndSet(100,1000,marked,!marked);
},"t1").start();
new Thread(() -> {
boolean marked = markableReference.isMarked();
System.out.println(Thread.currentThread().getName()+"\t"+"默认标识:"+marked);
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
boolean b = markableReference.compareAndSet(100, 2000, marked, !marked);
//T1已经修改过了, t2的结果为false,
System.out.println(Thread.currentThread().getName()+"\t"+"t2线程CASresult: "+b);
System.out.println(Thread.currentThread().getName()+"\t"+markableReference.isMarked());
System.out.println(Thread.currentThread().getName()+"\t"+markableReference.getReference());
},"t2").start();
}
}
/**
* CAS----Unsafe----do while+ABA---AtomicStampedReference,AtomicMarkableReference
*
* AtomicStampedReference,version号,+1;
*
* AtomicMarkableReference,一次,false,true
*
*/
AtomicIntegerFieldUpdater
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
* AtomicIntegerFieldUpdater 针对 一个对象的一个字段进行元子类增加
*/
class BankAccount
{
//更新的对象属性必须使用 public volatile 修饰符。
public volatile int money = 0;//钱数
public void add()
{
money++;
}
//因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须
// 使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。
AtomicIntegerFieldUpdater<BankAccount> fieldUpdater = //哪个对象的 哪个字段
AtomicIntegerFieldUpdater.newUpdater(BankAccount.class,"money");
//不加synchronized,保证高性能原子性,局部微创小手术
public void transMoney(BankAccount bankAccount)
{
fieldUpdater.getAndIncrement(bankAccount);
}
}
AtomicReferenceFieldUpdater字段级别原子更新
package com.bilibili.juc.atomics;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
/**
* 字段级别的原子更新
* AtomicReferenceFieldUpdater
* 通过引用类型的 updater 不通过synchournized更新
*
**/
class MyVar //资源类
{
public volatile Boolean isInit = Boolean.FALSE;
AtomicReferenceFieldUpdater<MyVar,Boolean> referenceFieldUpdater =
AtomicReferenceFieldUpdater.newUpdater(MyVar.class,Boolean.class,"isInit");//字段名
public void init(MyVar myVar)
{
if (referenceFieldUpdater.compareAndSet(myVar,Boolean.FALSE,Boolean.TRUE))
{
System.out.println(Thread.currentThread().getName()+"\t"+"----- start init,need 2 seconds");
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"\t"+"----- over init");
}else{
System.out.println(Thread.currentThread().getName()+"\t"+"----- 已经有线程在进行初始化工作。。。。。");
}
}
}
/**
* 需求:
* 多线程并发调用一个类的初始化方法,如果未被初始化过,将执行初始化工作,
* 要求只能被初始化一次,只有一个线程操作成功
*/
public class AtomicReferenceFieldUpdaterDemo
{
public static void main(String[] args)
{
MyVar myVar = new MyVar();
for (int i = 1; i <=5; i++) {
new Thread(() -> {
myVar.init(myVar);
},String.valueOf(i)).start();
}
}
}
LongAccumulator
package com.bilibili.juc.atomics;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.LongBinaryOperator;
/**
* longAdder 只能算加法,只能从0开始
*LongAccumulator 自定义参数
*/
public class LongAdderAPIDemo
{
public static void main(String[] args)
{
LongAdder longAdder = new LongAdder();
longAdder.increment();
longAdder.increment();
longAdder.increment();
System.out.println(longAdder.sum());
LongAccumulator longAccumulator = new LongAccumulator(new LongBinaryOperator()
{
@Override
public long applyAsLong(long left, long right)
{
return left + right;
}
},0);//初始值从0开始
longAccumulator.accumulate(1);//1
longAccumulator.accumulate(3);//4
System.out.println(longAccumulator.get());
}
}
LongAddr性能最高
package com.bilibili.juc.atomics;
import javax.lang.model.element.VariableElement;
import java.util.HashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
class ClickNumber //资源类
{
int number = 0;
public synchronized void clickBySynchronized()
{
number++;
}
AtomicLong atomicLong = new AtomicLong(0);
public void clickByAtomicLong()
{
atomicLong.getAndIncrement();
}
LongAdder longAdder = new LongAdder();
public void clickByLongAdder()
{
longAdder.increment();
}
LongAccumulator longAccumulator = new LongAccumulator((x,y) -> x + y,0);
public void clickByLongAccumulator()
{
longAccumulator.accumulate(1);
}
}
/**
*
* 需求: 50个线程,每个线程100W次,总点赞数出来
*/
public class AccumulatorCompareDemo
{
public static final int _1W = 10000;
public static final int threadNumber = 50;
public static void main(String[] args) throws InterruptedException
{
ClickNumber clickNumber = new ClickNumber();
long startTime;
long endTime;
CountDownLatch countDownLatch1 = new CountDownLatch(threadNumber);
CountDownLatch countDownLatch2 = new CountDownLatch(threadNumber);
CountDownLatch countDownLatch3 = new CountDownLatch(threadNumber);
CountDownLatch countDownLatch4 = new CountDownLatch(threadNumber);
startTime = System.currentTimeMillis();
for (int i = 1; i <=threadNumber; i++) {
new Thread(() -> {
try {
for (int j = 1; j <=100 * _1W; j++) {
clickNumber.clickBySynchronized();
}
} finally {
countDownLatch1.countDown(); // 计数器-1操作,一般都在finally里操作
}
},String.valueOf(i)).start();
}
countDownLatch1.await();//等待线程计算结束获取值
endTime = System.currentTimeMillis();
System.out.println("----costTime: "+(endTime - startTime) +" 毫秒"+"\t clickBySynchronized: "+clickNumber.number);
startTime = System.currentTimeMillis();
for (int i = 1; i <=threadNumber; i++) {
new Thread(() -> {
try {
for (int j = 1; j <=100 * _1W; j++) {
clickNumber.clickByAtomicLong();
}
} finally {
countDownLatch2.countDown();
}
},String.valueOf(i)).start();
}
countDownLatch2.await();//等待线程计算结束获取值
endTime = System.currentTimeMillis();
System.out.println("----costTime: "+(endTime - startTime) +" 毫秒"+"\t clickByAtomicLong: "+clickNumber.atomicLong.get());
startTime = System.currentTimeMillis();
for (int i = 1; i <=threadNumber; i++) {
new Thread(() -> {
try {
for (int j = 1; j <=100 * _1W; j++) {
clickNumber.clickByLongAdder();
}
} finally {
countDownLatch3.countDown();
}
},String.valueOf(i)).start();
}
countDownLatch3.await();
endTime = System.currentTimeMillis();
System.out.println("----costTime: "+(endTime - startTime) +" 毫秒"+"\t clickByLongAdder: "+clickNumber.longAdder.sum());
startTime = System.currentTimeMillis();
for (int i = 1; i <=threadNumber; i++) {
new Thread(() -> {
try {
for (int j = 1; j <=100 * _1W; j++) {
clickNumber.clickByLongAccumulator();
}
} finally {
countDownLatch4.countDown();
}
},String.valueOf(i)).start();
}
countDownLatch4.await();
endTime = System.currentTimeMillis();
System.out.println("----costTime: "+(endTime - startTime) +" 毫秒"+"\t clickByLongAccumulator: "+clickNumber.longAccumulator.get());
}
}
ThreadLoacal
package com.bilibili.juc.tl;
import lombok.Getter;
import sun.font.FontRunIterator;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
* ThreadLocal 案例
*/
class House //资源类
{
int saleCount = 0;
//创建初始化为0 的threadlocal
ThreadLocal<Integer> saleVolume = ThreadLocal.withInitial(() -> 0);
public void saleVolumeByThreadLocal()
{
saleVolume.set(1+saleVolume.get());//saleVolume.get() 得到上一次的数字再 +1
}
}
/**
*
* 需求1: 5个销售卖房子,集团高层只关心销售总量的准确统计数。
* 需求2: 5个销售卖完随机数房子,各自独立销售额度,自己业绩按提成走,分灶吃饭,各个销售自己动手,丰衣足食
*
*
*/
public class ThreadLocalDemo
{
public static void main(String[] args) throws InterruptedException
{
House house = new House();
for (int i = 1; i <=5; i++) {
new Thread(() -> {
int size = new Random().nextInt(5)+1;
try {
for (int j = 1; j <=size; j++) {
//house.saleHouse();
house.saleVolumeByThreadLocal();
}
System.out.println(Thread.currentThread().getName()+"\t"+"号销售卖出:"+house.saleVolume.get());
} finally {
house.saleVolume.remove(); //不清空容易内存泄漏
}
},String.valueOf(i)).start();
};
//暂停毫秒
try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"\t"+"共计卖出多少套: "+house.saleCount);
}
}
ThreadLoacal源码分析
thread threadLocal threadLocalmap区别
ThreadLocal弱引用
对象内存布局
对象头 类元信息 对象标记
synchronized锁升级
无锁,偏向锁,轻锁,重锁
AQS
// CLH AQS双向队列+state(int类型)
ReentrantReadWriteLock 可重入读写锁
// 解决锁饥饿和锁降级的问题
// 写锁能够降级成为读锁
package com.bilibili.juc.rwlock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 锁降级:遵循获取写锁→再获取读锁→再释放写锁的次序,写锁能够降级成为读锁。
*
* 如果一个线程占有了写锁,在不释放写锁的情况下,它还能占有读锁,即写锁降级为读锁。
*
* 读没有完成时候写锁无法获得锁,必须要等着读锁读完后才有机会写
*/
public class LockDownGradingDemo
{
public static void main(String[] args)
{ //读的时候不允许写
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
//正常 A B两个线程
// A
/*readLock.lock();
System.out.println("----读取");
readLock.unlock();
// B
writeLock.lock();
System.out.println("----写入");
writeLock.unlock();*/
readLock.lock();
System.out.println("----读取");
readLock.unlock();
writeLock.lock();
System.out.println("----写入");
writeLock.unlock();
}
}
可重入读写锁
package com.bilibili.juc.rwlock;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/** 读写互斥,读读共享 属于悲观锁
*
* 读写锁 : 写的时候 读锁不能打断
* 读锁没有完成,写锁无法获得
*/
class MyResource //资源类,模拟一个简单的缓存
{
Map<String,String> map = new HashMap<>();
//=====ReentrantLock 等价于 =====synchronized,之前讲解过
Lock lock = new ReentrantLock();
//=====ReentrantReadWriteLock 一体两面,读写互斥,读读共享
ReadWriteLock rwLock = new ReentrantReadWriteLock();
public void write(String key ,String value)
{
rwLock.writeLock().lock();
try
{
System.out.println(Thread.currentThread().getName()+"\t"+"正在写入");
map.put(key,value);
//暂停毫秒
try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"\t"+"完成写入");
}finally {
rwLock.writeLock().unlock();
}
}
public void read(String key)
{
rwLock.readLock().lock();
try
{
System.out.println(Thread.currentThread().getName()+"\t"+"正在读取");
String result = map.get(key);
//暂停200毫秒
//try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }
//暂停2000毫秒,演示读锁没有完成之前,写锁无法获得
try { TimeUnit.MILLISECONDS.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"\t"+"完成读取"+"\t"+result);
}finally {
rwLock.readLock().unlock();
}
}
}
public class ReentrantReadWriteLockDemo
{
public static void main(String[] args)
{
MyResource myResource = new MyResource();
for (int i = 1; i <=10; i++) {
int finalI = i;
new Thread(() -> {
myResource.write(finalI +"", finalI +"");
},String.valueOf(i)).start();
}
for (int i = 1; i <=10; i++) {
int finalI = i;
new Thread(() -> {
myResource.read(finalI +"");
},String.valueOf(i)).start();
}
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
for (int i = 1; i <=3; i++) {
int finalI = i;
new Thread(() -> {
myResource.write(finalI +"", finalI +"");
},"新写锁线程->"+String.valueOf(i)).start();
}
}
}
可重入邮戳锁StampedLock
//比可重入锁更快 锁饥饿问题 属于乐观锁 读的过程中也允许写锁接入
package com.bilibili.juc.rwlock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;
/**
* StampedLock = ReentrantReadWriteLock + 读的过程中也允许获取写锁介入
*/
public class StampedLockDemo
{
static int number = 37;
static StampedLock stampedLock = new StampedLock();
public void write()
{
long stamp = stampedLock.writeLock();
System.out.println(Thread.currentThread().getName()+"\t"+"写线程准备修改");
try
{
number = number + 13;
}finally {
stampedLock.unlockWrite(stamp);//解锁获取 写锁的 戳记
}
System.out.println(Thread.currentThread().getName()+"\t"+"写线程结束修改");
}
//悲观读,读没有完成时候写锁无法获得锁
public void read()
{
long stamp = stampedLock.readLock();
System.out.println(Thread.currentThread().getName()+"\t"+" come in readlock code block,4 seconds continue...");
for (int i = 0; i < 4; i++) {
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"\t"+" 正在读取中......");
}
try
{
int result = number;
System.out.println(Thread.currentThread().getName()+"\t"+" 获得成员变量值result:"+result);
System.out.println("写线程没有修改成功,读锁时候写锁无法介入,传统的读写互斥");
}finally {
stampedLock.unlockRead(stamp);
}
}
//乐观读,读的过程中也允许获取写锁介入
public void tryOptimisticRead()
{
long stamp = stampedLock.tryOptimisticRead();
int result = number;
//故意间隔4秒钟,很乐观认为读取中没有其它线程修改过number值,具体靠判断
System.out.println("4秒前stampedLock.validate方法值(true无修改,false有修改)"+"\t"+stampedLock.validate(stamp));
for (int i = 0; i < 4; i++) {
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"\t"+"正在读取... "+i+" 秒" +
"后stampedLock.validate方法