1、ForkJoin
将大任务分层若干个小任务,并行执行,结果继续合并。
每个任务维护一个双端队列,执行速度快的分任务可以偷窃其他分任务的任务,叫做
工作窃取
- 任务类
任务类继承
RecursiveTask<Long>
,泛型为返回结果类型
public class ForkJoinDemo extends RecursiveTask<Long> {
private Long start; // 1
private Long end; // 1990900000
// 临界值
private Long temp = 10000L;// 临界值
public ForkJoinDemo(Long start, Long end) {
this.start = start;
this.end = end;
}
// 计算方法
@Override
protected Long compute() {
if ((end-start)<temp){
Long sum = 0L;
for (long i = start; i <= end; i++) {
sum+=i;
}
return sum;
}else { // forkjoin 递归
long middle = (start + end) / 2; // 中间值
ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
task1.fork(); // 拆分任务,把任务压入线程队列
ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end);
task2.fork(); // 拆分任务,把任务压入线程队列
return task1.join() + task2.join();//收集结果
}
}
}
- 调用任务方法
public static void main(String[] args) throws ExecutionException,InterruptedException {
test1(); // 24203
test2(); // 1953
test3(); // 1187
}
// 普通计算
public static void test1(){
Long sum = 0L;
long start = System.currentTimeMillis();
for (Long i = 1L; i <= 10_0000_0000; i++) {
sum += i;
}
long end = System.currentTimeMillis();
System.out.println("sum="+sum+" 时间:"+(end-start));
}
// ForkJoin计算
public static void test2() throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinDemo(0L, 10_0000_0000L);
ForkJoinTask<Long> submit = forkJoinPool.submit(task);// 提交任务
Long sum = submit.get();
long end = System.currentTimeMillis();
System.out.println("sum="+sum+" 时间:"+(end-start));
}
// Stream并行流
public static void test3(){
long start = System.currentTimeMillis();
long sum = LongStream.rangeClosed(0L,10_0000_0000L).parallel().reduce(0, Long::sum);
long end = System.currentTimeMillis();
System.out.println("sum="+"时间:"+(end-start));
}
2、异步回调
与AJAX类似,异步执行任务。回调返回和失败
- 异步回调,无返回值
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(runAsync.get());//回调阻塞,无返回null
System.out.println("=====main====");
}
- 异步回调,有返回值
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName());
int i=10/0;
return 1024;
});//异步任务
Integer integer = supplyAsync.whenComplete((t, u) -> {
System.out.println("u=>" + u);//异步任务返回异常信息
System.out.println("t=>" + t);//无异常正常返回,有异常为null
}).exceptionally((e) -> {//处理异常
System.out.println("e=>" + e);//异常信息
return 2048;//异常统一返回
}).get();
System.out.println(integer);
}
3、JMM
Java内存模型
- 线程解锁前,必须把
共享变量立刻刷回主存
。- 线程加锁前,必须
读取主存
中的最新值到工作内存
中!- 加锁和解锁是
同一把锁
内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的
4、Volatile
- 保证可见性
- 不保证原子性
- 禁止指令重排
- 保证可见性
主存中的数据被某一条线程改变时,其他线程会接到通知
private static int num=0;//主存数值
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while(num==0){//线程的num为0
//业务
}
}).start();
TimeUnit.SECONDS.sleep(2);
num=1;//主线程修改num对线程不可见
System.out.println("====main====");//线程死循环
}
private static volatile int num=0;//volatile关键字使得num改变时线程可知
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while(num==0){//线程的num为0
//业务
}
}).start();
TimeUnit.SECONDS.sleep(2);
num=1;
System.out.println("====main====");
}
- 不保证原子性
原子性 : 不可分割
线程A在执行任务的时候,不能被打扰的,也不能被分割。要么同时成功,要么同时失败。
private volatile static int num = 0;
public static void sum(){
num++;
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 20; i++) {//20条线程
new Thread(()->{//线程内加1000次
for (int j = 0; j < 1000; j++) {
sum();
}
}).start();
}
while (Thread.activeCount()>2){//等待线程执行完毕
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+"num=>"+num);//19196
}
#字节码
public static void sum();#sum方法 在操作时不是一个整体,在执行时其他线程可干预
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
0: getstatic #2
3: iconst_1 #取到值
4: iadd #加一
5: putstatic #2
8: return
java.util.concurrent.atomic
保证操作原子性
private volatile static AtomicInteger num = new AtomicInteger();//原子操作数
public static void sum(){
num.getAndIncrement();//原子操作加1
}
#字节码
public static void sum();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: getstatic #2
3: invokevirtual #原子操作加1
6: pop
7: return
- 禁止指令重排
指令重排:源代码–>编译器优化的重排–> 指令并行也可能会重排–> 内存系统也会重排—> 执行
处理器在进行指令重排的时候考虑:数据之间的依赖性
!
int x = 1; // 1
int y = 2; // 2
x = x + 5; // 3
y = x * x; // 4
//1234 2134 1324 执行顺序都可以
//多线程时指令顺序可能破坏数据的依赖关系
volatile可以避免指令重排:内存屏障(CPU指令)。
作用:
- 保证特定的操作的执行顺序!
- 可以保证某些变量的内存可见性
5、单例模式
- 饿汉式
在调用之前就创造对象,浪费内存
public class SigleHug {
private SigleHug() { }//构造器私有
public static final SigleHug SIGLE_HUG = new SigleHug();//全局唯一
public static SigleHug getInstance(){
return SIGLE_HUG;//返回对象
}
}
//静态内部类方式
public class SingleInner {
private SingleInner() { }
public static SingleInner getInstance(){
return InnerClass.SINGLE_INNER;
}
private static class InnerClass{
public static final SingleInner SINGLE_INNER = new SingleInner();
}
}
- 懒汉式
在调用时创建对象
public class SingleLazy {
private SingleLazy() { }//构造器私有
public volatile static SingleLazy singleLazy;
public static SingleLazy getInstance(){
//1. 分配内存空间
//2、执行构造方法,初始化对象
//3、把这个对象指向这个空间
//不是原子操作,volatile修饰
if (singleLazy==null){//线程重复创建
singleLazy=new SingleLazy();
}
return singleLazy;
}
}
问题:多线程情况下不安全
public static void main(String[] args) {
new Thread(()-> System.out.println(SingleLazy.getInstance())).start();//@4c999645
new Thread(()-> System.out.println(SingleLazy.getInstance())).start();//@75e24497
}
//DCL懒汉式
public class SingleLazy {
private SingleLazy() { }
public volatile static SingleLazy singleLazy;
public static SingleLazy getInstance(){
if (singleLazy==null) {//避免重复创建,创建实例后之间返回
synchronized (SingleLazy.class){//线程同步
if (singleLazy==null){//避免下一条线程重复创建
singleLazy=new SingleLazy();
}
}
}
return singleLazy;
}
}
问题:反射可使类构造器私有属性改变,破坏单例
public static void main(String[] args) throws Exception {
System.out.println(SingleLazy.getInstance());//原始对象,@14ae5a5
Constructor<SingleLazy> constructor = SingleLazy.class.getDeclaredConstructor(null);
//获取无参构造器
constructor.setAccessible(true);//修改访问权限
SingleLazy singleLazy = constructor.newInstance();
System.out.println(singleLazy);//反射对象,@7f31245a
}
//解决:隐藏标志位,创建对象成功后改变标志位
public class SingleLazy {
public volatile static SingleLazy singleLazy;
private static boolean flag = false;//标志位,创建实例改为true
private SingleLazy() {
synchronized (SingleLazy.class){
if (!flag){
flag = true;
}else {//反射时抛出异常,反射进行第一次创建也能成功
throw new RuntimeException("不要用反射创建对象");
}
}
}
public static SingleLazy getInstance(){
if (singleLazy==null) {
synchronized (SingleLazy.class){
if (singleLazy==null){
singleLazy=new SingleLazy();//不是原子操作,volatile修饰
}
}
}
return singleLazy;
}
}
//若获得了标志位信息,也可反射成功
public static void main(String[] args) throws Exception {
Constructor<SingleLazy> constructor = SingleLazy.class.getDeclaredConstructor(null);
Field flag = SingleLazy.class.getDeclaredField("flag");//获得标志位
flag.setAccessible(true);//改变标志位的访问权限
constructor.setAccessible(true);
SingleLazy singleLazy1 = constructor.newInstance();
flag.set(singleLazy1,false);//改变标志位
SingleLazy singleLazy2 = constructor.newInstance();
System.out.println(singleLazy1);//@6d6f6e28
System.out.println(singleLazy2);//@135fbaa4
}
枚举类可使反射失效
//jdk反射源码
if ((clazz.getModifiers() & Modifier.ENUM) != 0)//如果是一个枚举类
throw new IllegalArgumentException("Cannot reflectively create enum objects");
//抛出不能通过反射创建枚举
//枚举单例
public enum EnumSingle {
INSTANCE;
public static EnumSingle getInstance(){
return INSTANCE;
}
}
#字节码
javap -p EnumSingle.class
public final class com.wdd.lock.EnumSingle extends
java.lang.Enum<com.wdd.lock.EnumSingle> {
public static final com.wdd.lock.EnumSingle INSTANCE;#INSTANCE属性
private static final com.wdd.lock.EnumSingle[] $VALUES;
public static com.wdd.lock.EnumSingle[] values();
public static com.wdd.lock.EnumSingle valueOf(java.lang.String);
private com.wdd.lock.EnumSingle();#无参构造
public static com.wdd.lock.EnumSingle getInstance();
static {};
}
//无参构造反射破环枚举
public static void main(String[] args) throws Exception {
Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
//NoSuchMethodException: com.wdd.lock.EnumSingle.<init>()
//异常表明EnumSingle类没有无参构造,不是JDK源码中写的无法反射枚举
EnumSingle enumSingle1 = constructor.newInstance();
System.out.println(enumSingle1);
}
//专业反编译软件 jad.exe jad -sjava EnumSingle.class
private EnumSingle(String s, int i)
{
super(s, i);//实际运行时EnumSingle枚举类只有一个有参构造器
}
//有参构造反射破环枚举
public static void main(String[] args) throws Exception {
Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);//有参构造
constructor.setAccessible(true);
//IllegalArgumentException: Cannot reflectively create enum objects
//异常表明反射无法破环枚举类的单例,枚举类运行时确实只有有参构造
EnumSingle enumSingle1 = constructor.newInstance();
System.out.println(enumSingle1);
}
6、CAS
CAS
:比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环!
public static void main(String[] args) {
AtomicInteger integer = new AtomicInteger(2000);//原子数
boolean compareAndSet = integer.compareAndSet(2000, 3000);
System.out.println(compareAndSet);//true
System.out.println(integer.get());//3000
integer.getAndIncrement();//原子数加1
boolean compareAndSet1 = integer.compareAndSet(2000, 3000);
System.out.println(compareAndSet1);//false
System.out.println(integer.get());//3001
}
compareAndSet()
:源码
//AtomicInteger类
//本地内存地址valueOffset
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
===============================
// Unsafe类
//本地CAS方法
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
getAndIncrement()
源码
//AtomicInteger类
//本地内存地址valueOffset
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
// Unsafe类
//(对象,地址,增加的值)
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
//var地址中取值
var5 = this.getIntVolatile(var1, var2);
//自旋锁,内存中完成后退出循环
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
缺点
:
1、 循环会耗时
2、一次性只能保证一个共享变量的原子性
3、ABA问题
7、ABA问题
compareAndSet()
:其他线程值A修改为B又改回A,本线程不知道解决方法:
原子引用
=====>乐观锁机制
- ABA问题
public static void main(String[] args) {
AtomicInteger integer = new AtomicInteger(2000);
System.out.println("=========捣乱的线程=========");
System.out.println(integer.compareAndSet(2000, 3000));//true
System.out.println(integer.get());//3000
System.out.println(integer.compareAndSet(3000, 2000));//true
System.out.println(integer.get());//2000
System.out.println("=========正常的线程=========");
System.out.println(integer.compareAndSet(2000, 4000));//true
System.out.println(integer.get());//4000
}
- 原子引用
public static void main(String[] args) {
AtomicStampedReference<Integer> reference = new AtomicStampedReference<>(1,2000);
System.out.println("=========捣乱的线程=========");
System.out.println(reference.compareAndSet(1, 2,2000,3000));//true,版本号加1
System.out.println(reference.getReference());//2,获取版本号
System.out.println(reference.getStamp());//3000,获取值
System.out.println(reference.compareAndSet(2, 3,3000,2000));//true,版本号加1
System.out.println(reference.getReference());//3
System.out.println(reference.getStamp());//2000
System.out.println("=========正常的线程=========");
System.out.println(reference.compareAndSet(1,2,2000, 4000));//false,版本号不一致
System.out.println(reference.getReference());//3
System.out.println(reference.getStamp());//2000
}