Consumer 消费型接口

JUC Day03_可见性

package com.cedric.JUC.function;

import java.util.function.Consumer;

/**
 * Consumer 消费型接口:只有输入,没有返回值
 */
public class Demo03 {
    public static void main(String[] args) {

       /* Consumer<String> consumer = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };*/
        Consumer<String> consumer = (str) -> {System.out.println(str);};
        consumer.accept("aafs");
    }
}

Supplier 供给型接口

JUC Day03_可见性_02

package com.cedric.JUC.function;

import java.util.function.Supplier;

/**
 * Supplier  供给型接口 没有参数,只有返回值
 */
public class Demo04 {
    public static void main(String[] args) {
      /*  Supplier<String> supplier = new Supplier<String>() {
            @Override
            public String get() {
                System.out.println("get()");
                return "Cedric";
            }
        };*/
        Supplier<String> supplier = () -> {return "Cedric";};
        System.out.println(supplier.get());
    }
}

Stream流式计算

什么是Stream流式计算

JUC Day03_i++_03

package com.cedric.JUC.function;

import java.util.Arrays;
import java.util.List;

/**
 * 题目要求:一分钟内完成此题,只能用一行代码实现
 * 现在有五个用户,筛选:
 * 1.ID必须是偶数
 * 2.年龄必须大于23岁
 * 3.用户名转为大写字母
 * 4.用户名字母倒着排序
 * 5.只输出一个用户
 */
public class Demo05 {
    public static void main(String[] args) {
        User u1 =  new User(1,"a",21);
        User u2 =  new User(2,"b",22);
        User u3 =  new User(3,"c",23);
        User u4 =  new User(4,"d",24);
        User u5 =  new User(6,"e",25);
        // 集合就是存储
        List<User> list = Arrays.asList(u1,u2,u3,u4,u5);

        // 计算交给Stream流
        list.stream()
                .filter(user -> {return user.getId() % 2 == 0;})
                .filter(user -> {return user.getAge() > 23;})
                .map(user -> {return user.getName().toUpperCase();})
                .sorted((uu1,uu2) -> {return uu2.compareTo(uu1);})
                .limit(1)
                .forEach(System.out :: println);
    }
}

ForkJoin

什么是ForkJoin

ForkJoin在JDK1.7,并行执行任务,提高效率 大数据量 把大任务拆分成小任务
JUC Day03_java内存模型_04

ForkJoin特点:工作窃取

这个里面维护的都是双端队列
JUC Day03_java内存模型_05

ForkJoin

JUC Day03_java_06

JUC Day03_java_07

package com.cedric.JUC.forkjoin;

import java.util.concurrent.RecursiveTask;

/**
 * 求和计算的任务
 *
 * 如何使用ForkJoin
 * 1.ForkJoinPool  通过他来执行
 * 2.计算任务 ForkJoinPool.execute(ForkJoinTask task)
 * 3.计算类要继承 ForkJoinTask
 */
public class ForkJoinDemo extends RecursiveTask<Long> {

    private Long start;
    private Long end;

    // 临界值
    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();
        }
    }
}

测试:

package com.cedric.JUC.forkjoin;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //test1(); // 6655
        //test2(); // 7616
        test3(); // 168
    }

    // 普通方式
    public static void test1(){
        Long start = System.currentTimeMillis();
        Long sum = 0L;
        for (Long i = 1L; i <= 10_0000_0000L; 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));
    }

    // Steam并行流
    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=" + sum + "时间:" + (end - start));
    }
}

异步回调

Future 设计的初衷:对将来的某个事件的结果进行建模
JUC Day03_赋值_08

package com.cedric.JUC.future;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * 并步执行
 * 成功回调
 * 失败回调
 */
public class Demo01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
      /*  // 没有返回值的runAsync 异步回调
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(2); // 延时两秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "runAsync -> Void");
        });
        System.out.println("11111");

        completableFuture.get(); // 获取阻塞执行结果
*/

        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "supplyAsync -> Integer");
            int i = 10 / 0;
            return 1024;
        });

        System.out.println(completableFuture.whenComplete((t,u) -> {
            System.out.println("t -> " + t);  // 正常的返回结果
            System.out.println("u -> " + u);  // 错误信息:java.util.concurrent.CompletionException: java.lang.ArithmeticException:
        }).exceptionally((e) -> {
            System.out.println(e.getMessage());
            return 233; // 可以获取到错误的返回结果
        }).get());
    }
}

JMM

请谈谈对Volatile的理解

Volatile是JVM提供轻量级的同步机制

1.保证可见性

2.不保证原子性

3.禁止指定重拍

什么是JMM

JMM:java内存模型,不存在的东西(概念,约定)

关于JMM的一些同步的约定:

1.线程解锁前,必须把共享变量立刻刷回主存

2.线程加锁前,必须读取主存中的最新值到工作内存中

3.加锁和解锁是同一把锁

线程 工作内存、主内存

八种操作:
JUC Day03_可见性_09

JUC Day03_可见性_10

关于主内存与工作内存之间的具体交互协议,即一个变量如何从主内存拷贝到工作内存、如何从工作内存同步到主内存之间的实现细节,Java内存模型定义了以下八种操作来完成:

  • lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
  • unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
  • read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
  • load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
  • use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
  • assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  • store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
  • write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。

Java内存模型还规定了在执行上述八种基本操作时,必须满足如下规则:

  • 如果要把一个变量从主内存中复制到工作内存,就需要按顺寻地执行read和load操作, 如果把变量从工作内存中同步回主内存中,就要按顺序地执行store和write操作。但Java内存模型只要求上述操作必须按顺序执行,而没有保证必须是连续执行。
  • 不允许read和load、store和write操作之一单独出现
  • 不允许一个线程丢弃它的最近assign的操作,即变量在工作内存中改变了之后必须同步到主内存中。
  • 不允许一个线程无原因地(没有发生过任何assign操作)把数据从工作内存同步回主内存中。
  • 一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作。
  • 一个变量在同一时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。lock和unlock必须成对出现
  • 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值
  • 如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作;也不允许去unlock一个被其他线程锁定的变量。
  • 对一个变量执行unlock操作之前,必须先把此变量同步到主内存中(执行store和write操作)。

问题:程序不知道主内存的值已经被修改过了
JUC Day03_赋值_11

Volatile


1.保证可见性

package com.cedric.JUC.tvolatile;

import java.util.concurrent.TimeUnit;

public class JMMDemo {
    // 不加 volatile 程序就会死循环
    // 加 volatile 可以保证可见性
    private volatile static int num = 0;

    public static void main(String[] args) {

        new Thread(()->{    // 线程1对主内存的变化不知道
            while(num == 0){

            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num = 1;
        System.out.println(num);
    }
}

不保证原子性

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

package com.cedric.JUC.tvolatile;

// 不保证原子性
public class vDemo02 {

    // volatile 不保证原子性
    public volatile static int num = 0;

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

    public static void main(String[] args) {

        // 理论上num的值应该是两万
        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }

        while(Thread.activeCount() > 2){ // main  gc
            Thread.yield();
        }

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

如果不加lock和synchronized,怎样保证原子性


JUC Day03_赋值_12


使用原子类,解决原子性问题

JUC Day03_java内存模型_13

package com.cedric.JUC.tvolatile;

import java.util.concurrent.atomic.AtomicInteger;

// 不保证原子性
public class vDemo02 {

    // volatile 不保证原子性
    // 原子类的Integer
    public volatile static AtomicInteger num =  new AtomicInteger(0);

    public static void add(){
        // num++; // 不是一个原子性操作
        num.getAndIncrement(); // AtomicInteger + 1 方法,CAS
    }

    public static void main(String[] args) {

        // 理论上num的值应该是两万
        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }

        while(Thread.activeCount() > 2){ // main  gc
            Thread.yield();
        }

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

这些类的底层都直接和操作系统挂钩,在内存中修改值

指令重排

什么是指令重排:你写的程序,计算机并不是按照你写的那样去执行的

源代码-->编译器优化的重排-->指令并行也可能重排-->内存系统也会重排-->执行

处理器在进行指令重排的时候,考虑:数据之间的依赖性

volatile可以避免指令重排:

内存屏障,CPU指令,作用:

1.保证特定操作的执行顺序

2.可以保证某些变量的内存可见性(利用这些特性volatile实现了可见性)

JUC Day03_java_14

Volatile是可以保持可见性,不能保证原子性,由于内存屏障,可以避免指令重排的现象产生

玩转单例模式

饿汉式 DCL懒汉式

饿汉式

package com.cedric.JUC.single;

// 饿汉式单例
public class Hungary {

    // 可能会浪费空间
    private byte[] data1 = new byte[1024 * 1024];
    private byte[] data2 = new byte[1024 * 1024];
    private byte[] data3 = new byte[1024 * 1024];
    private byte[] data4 = new byte[1024 * 1024];

    private Hungary(){

    }

    private final static Hungary HUNGARY = new Hungary();

    public static Hungary getInstance(){
        return HUNGARY;
    }
}

DCL懒汉式

package com.cedric.JUC.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

// 懒汉式单例模式
public class LazyMan {

    private static boolean cedric = false;

    private  LazyMan(){
        synchronized(LazyMan.class){
            if(cedric == false){
                cedric = true;
            }else{
                throw new RuntimeException("不要试图使用反射破坏异常");
            }

        }
    }

    private volatile static LazyMan lazyMan;

    // 双重检测锁模式的  懒汉式多例  DCL懒汉式
    public static LazyMan getInstance(){
        if(lazyMan == null){
            synchronized (LazyMan.class){
                if(lazyMan == null){
                    lazyMan = new LazyMan(); // 不是一个原子性操作

                }
            }
        }
        return lazyMan;
    }
    /**
     * 1.分配内存空间
     * 2.执行构造方法
     * 3.把这个对象指向这个空间
     */

    // 多线程并发
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        //LazyMan lazyMan = LazyMan.getInstance();

        Field cedric = LazyMan.class.getDeclaredField("cedric");
        cedric.setAccessible(true);

        Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);
        constructor.setAccessible(true);
        LazyMan lazyMan2 = constructor.newInstance();

        cedric.set(lazyMan2,false);

        LazyMan lazyMan3 = constructor.newInstance();

        //System.out.println(lazyMan);
        System.out.println(lazyMan2);
        System.out.println(lazyMan3);


    }
}

静态内部类

package com.cedric.JUC.single;

// 静态内部类
public class Holder {
    private Holder(){

    }
    public  static Holder getInstance(){
        return InnerClass.HOLDER;
    }
    public static  class InnerClass{
        private static final Holder HOLDER = new Holder();
    }
}

单例不安全,因为有反射

package com.cedric.JUC.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

// 懒汉式单例模式
public class LazyMan {

    private static boolean cedric = false;

    private  LazyMan(){
        synchronized(LazyMan.class){
            if(cedric == false){
                cedric = true;
            }else{
                throw new RuntimeException("不要试图使用反射破坏异常");
            }

        }
    }

    private volatile static LazyMan lazyMan;

    // 双重检测锁模式的  懒汉式多例  DCL懒汉式
    public static LazyMan getInstance(){
        if(lazyMan == null){
            synchronized (LazyMan.class){
                if(lazyMan == null){
                    lazyMan = new LazyMan(); // 不是一个原子性操作

                }
            }
        }
        return lazyMan;
    }
    /**
     * 1.分配内存空间
     * 2.执行构造方法
     * 3.把这个对象指向这个空间
     */

    // 多线程并发
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        //LazyMan lazyMan = LazyMan.getInstance();

        Field cedric = LazyMan.class.getDeclaredField("cedric");
        cedric.setAccessible(true);

        Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);
        constructor.setAccessible(true);
        LazyMan lazyMan2 = constructor.newInstance();

        cedric.set(lazyMan2,false);

        LazyMan lazyMan3 = constructor.newInstance();

        //System.out.println(lazyMan);
        System.out.println(lazyMan2);
        System.out.println(lazyMan3);

    }
}

枚举

package com.cedric.JUC.single;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public enum EnumSingle {

    INSTANCE;

    public  EnumSingle getInstance(){
        return  INSTANCE;
    }
}
class Test{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingle instance1 = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        EnumSingle intance2 = declaredConstructor.newInstance();

    }
}

JUC Day03_java_15