JUC——Day03

JUC Day03

文章总结自B站狂神说JAVA

1.ForkJoin

分支合并:并行执行任务来提高效率。把大任务划分成小任务来分来执行,将每个小任务得到的结果整合为最终结果。
特点:

  1. 工作窃取:不让执行完毕任务的线程等待,去帮助其它的线程执行任务。

demo:


/**
 * @date 2020/7/28 7:54
 *
 * 求和计算任务
 */
public class ForkJoinDemo01  extends RecursiveTask<Long> {

    private long start;
    private long end;

//    临界值 超过分为两个任务
    private long temp = 10000L;

    public ForkJoinDemo01(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 {
//           获取中间值
            long middle = (start + end) / 2;
//            根据中间值,将计算任务分为 开始到中间和中间到结束。
            ForkJoinDemo01 task1 = new ForkJoinDemo01(start, middle);
            task1.fork(); // 拆分任务,把任务压入线程队列。
            ForkJoinDemo01 task2 = new ForkJoinDemo01(middle+1, end);
            task2.fork();
            long result = task1.join() + task2.join();
            return result;
        }
    }
}

测试类:

/**
 * @date 2020/7/28 8:09
 *
 * 测试不同计算方法在数据较大时执行所需要的时间。
 */
public class Test01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

//        test1(); // 295
//        test2(); // 227
//        test3(); // 189

    }

//    for 循环计算
    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));
    }

//    fork join
    public static void test2() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();

        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinDemo01(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));
    }


//    steam并行流
    public static void test3(){
        long start = System.currentTimeMillis();
        long reduce = LongStream.range(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
        long end = System.currentTimeMillis();
        System.out.println("sum="+reduce+"执行时间:"+(end-start));
    }
}

2. 异步回调

类似于AJAX,就是在Java的线程中使用异步调用。

  1. 无返回值:
/**
 * @date 2020/7/28 9:23
 *  异步异步回调
 */
public class Demo01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"void -> runAsync");
        });

        System.out.println("11");

//        get 会阻塞(等待线程执行完毕)执行结果
        Void aVoid = completableFuture.get();
    }
}
  1. 有返回值
/**
 * @date 2020/7/28 9:36
 * 有返回值的异步回调
 */
public class Demo02 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
            CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
//            int i  = 10/0;
            System.out.println("supplyAsync -> run");
            return 1;
        });

        System.out.println(completableFuture.whenComplete((t, u) -> {
            System.out.println("t->" + t); // 正常的返回结果
            System.out.println("u-|>" + u); // 错误的返回结果
        }).exceptionally((e) -> {
//            发生错误的时候会执行。
            e.getMessage();
            return 1010;
        }).get());

    }
}

3. JMM

JMM:Java内存模型,是一种约定。
JMM同步的约定:

  1. 线程解锁前,必须把共享变量立刻刷会主内存。
  2. 线程加锁前,必须读取线程主内存中的最新值传到工作内存中。
  3. 必须保证家锁和减锁是同一把锁。

主内存与线程的工作内存之间有八种操作:

  1. read:读取主内存中变量。
  2. load:将读取的变量加载到工作内存中。
  3. use:执行引擎读取工作内存中的变量。
  4. assign:执行引擎将读取的变量执行操作之后返回到工作内存中。
  5. write:写入返回的变量。
  6. store:将写入的变量存到主内存中。
  7. lock:加锁。
  8. unlock:释放锁。

4. Volatile

是Java虚拟机提供的轻量级的同步机制。

4.1 保证可见性

如果工作内存无法及时的获得主内存中已经修改了的值,那么就不会及时的做出决定。

/**
 * @date 2020/7/28 10:00
 * JMM Demo
 */
public class Demo01 {
//    主线程

    private static int num = 0;
    public static void main(String[] args) {


//        线程A
        new Thread(()->{
            while (num == 0){
            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        num = 1;
        System.out.println(num);

    }
}

如上方代码,即使等到main线程休眠结束后num变为1,线程A也不会结束,会一直死循环。

解决方案:使用volatile关键字标识变量num

private volatile static int num = 0;

4.2 不保证原子性

原子性:不可分割。线程A在执行任务时,不能被打扰,要么同时成功要么同时失败。

/**
 * @date 2020/7/28 10:16
 * 不保证原子性
 */
public class Demo02 {
    private volatile static int num = 0;
    public static void main(String[] args) {

        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                for (int i1 = 0; i1 < 1000; i1++) {
                    add();
                }
            }).start();
        }

//        如果线程大于2线程礼让
        while (Thread.activeCount()>2){
            Thread.yield();
        }

        System.out.println(Thread.currentThread().getName()+"--------->"+num);

    }
    public static void add(){
        num++;
    }
}

如何保证原子性:

  1. lock&synchornized' 加锁可以保证原子性,但是消耗的资源比较大。
  2. 使用原子类->atomic

4.3 禁止指令重排

编译器优化会重排代码执行,指令并行也可能会重排,内存系统也会重排。处理器在执行指令重排的时候会考虑数据之间的依赖性。但是在多线程共同执行时,程序就不一定会出现应有的数值。添加volatile可以避免指令重排。
避免原理:内存屏障:

  1. 保证特定的操作的执行顺序。
  2. 可以保证某些变量的内存可见性。
    在单例模式使用的最多。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值