JUC(3)

文章基于【狂神说Java】JUC并发编程最新版通俗易懂_哔哩哔哩_bilibili 视频总结而来

13. 四大函数式接口

函数式接口

只有一个方法的接口,比如runnable:

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

其目的是简化编程模型,在新版本的底层框架中大量应用

foreach(消费者类的函数式接口)

函数型接口Function

@FunctionalInterface
public interface Function<T, R> {
    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);

函数型接口有一个传入参数T,有一个返回类型R。

调用打印方法:

package com.myinterface;

import java.util.function.Function;

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName MyInterface.java
 * @Description 函数型接口:
 *                     有一个输入的参数,有一个返回值
 *                     可以用lambda表达式来实现
 * @createTime 2022年05月05日 13:26:00
 */
public class MyInterface {
    public static void main(String[] args) {
        Function<String, String> function = new Function<String,String>() {
            @Override
            public String apply(String s) {
                return s;
            }
        };

        //lambda表达式简化
        Function<String, String>  function1 = (s) -> {return s;};
        System.out.println(function1.apply("hello"));

        String apply = function.apply("123");
        System.out.println(apply);
    }
}

断定型接口 Predicate

@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);

    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * AND of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code false}, then the {@code other}
     * predicate is not evaluated.
     *
     * <p>Any exceptions thrown during evaluation of either predicate are relayed
     * to the caller; if evaluation of this predicate throws an exception, the
     * {@code other} predicate will not be evaluated.
     *
     * @param other a predicate that will be logically-ANDed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * AND of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    /**
     * Returns a predicate that represents the logical negation of this
     * predicate.
     *
     * @return a predicate that represents the logical negation of this
     * predicate
     */
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * OR of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code true}, then the {@code other}
     * predicate is not evaluated.
     *
     * <p>Any exceptions thrown during evaluation of either predicate are relayed
     * to the caller; if evaluation of this predicate throws an exception, the
     * {@code other} predicate will not be evaluated.
     *
     * @param other a predicate that will be logically-ORed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * OR of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    /**
     * Returns a predicate that tests if two arguments are equal according
     * to {@link Objects#equals(Object, Object)}.
     *
     * @param <T> the type of arguments to the predicate
     * @param targetRef the object reference with which to compare for equality,
     *               which may be {@code null}
     * @return a predicate that tests if two arguments are equal according
     * to {@link Objects#equals(Object, Object)}
     */
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

断定型接口只有一个输入参数,返回一个boolean类型的参数,一般用于判断。

判断字符串是否为空:

package com.myinterface;

import java.util.function.Predicate;

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName MyInterface2.java
 * @Description 断定型接口
 * @createTime 2022年05月05日 13:34:00
 */
public class MyInterface2 {
    public static void main(String[] args) {
        // 判断字符串是否为空
        Predicate<String> predicate = new Predicate<String>() {
            @Override
            public boolean test(String o) {
                return o == null || o.length() == 0;
            }
        };

        Predicate<String> predicate2 = (s) -> {
            return s == null || s.length() == 0;
        };

        System.out.println(predicate2.test("111"));

        System.out.println(predicate.test(""));

    }
}

消费型接口 Consumer

/*
 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package java.util.function;

import java.util.Objects;

/**
 * Represents an operation that accepts a single input argument and returns no
 * result. Unlike most other functional interfaces, {@code Consumer} is expected
 * to operate via side-effects.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #accept(Object)}.
 *
 * @param <T> the type of the input to the operation
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

    /**
     * Returns a composed {@code Consumer} that performs, in sequence, this
     * operation followed by the {@code after} operation. If performing either
     * operation throws an exception, it is relayed to the caller of the
     * composed operation.  If performing this operation throws an exception,
     * the {@code after} operation will not be performed.
     *
     * @param after the operation to perform after this operation
     * @return a composed {@code Consumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

消费型接口类似于IO中的消费者,不提供返回值,只接收参数进行消费。

使用传入参数打印:

package com.myinterface;

import java.util.function.Consumer;

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName MyInterface3.java
 * @Description 消费性接口
 * @createTime 2022年05月05日 14:14:00
 */
public class MyInterface3 {
    public static void main(String[] args) {
       Consumer<String> consumer =  new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };

       Consumer<String> consumer2 = (String s) -> {
            System.out.println(s);
       };

       consumer2.accept("hello");

       consumer.accept("hello");
    }
}

供给型接口 Supplier

/*
 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package java.util.function;

/**
 * Represents a supplier of results.
 *
 * <p>There is no requirement that a new or distinct result be returned each
 * time the supplier is invoked.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #get()}.
 *
 * @param <T> the type of results supplied by this supplier
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

供给型接口没有传入参数,只有返回值

package com.myinterface;

import java.util.function.Supplier;

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName MyInterface4.java
 * @Description 供给型
 * @createTime 2022年05月05日 14:20:00
 */
public class MyInterface4 {
    public static void main(String[] args) {
        Supplier<String> s = new Supplier<String>() {
            @Override
            public String get() {
                return "sdad";
            }
        };

        System.out.println(s.get());


        Supplier<Integer> supplier = ()->{
            return 123;
        };

        System.out.println(supplier.get());
    }
}

14.Stream流计算

package com.Stream;

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

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName StreamTest.java
 * @Description     Stream流的使用
 *              一行代码实现Stream流的使用
 *              现在有5个用户,筛选:
 *                  1. ID必须是偶数
 *                  2. 年龄必须大于23
 *                  3. 用户名转成大写字母
 *                  4. 用户名字母倒序
 *                  5. 只输出第一个用户的名字
 * @createTime 2022年05月05日 14:37:00
 */
public class StreamTest {
    public static void main(String[] args) {
        User user1 = new User("zhangsan", 20,1);
        User user2 = new User("lisi", 30,2);
        User user3 = new User("wangwu", 40,3);
        User user4 = new User("zhaoliu", 50,4);
        List<User> list = Arrays.asList(user1, user2, user3, user4);
        
    }
}

我们使用Stream流进行链式编程,降低代码的复杂度。

package com.Stream;

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

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName StreamTest.java
 * @Description     Stream流的使用
 *              一行代码实现Stream流的使用
 *              现在有5个用户,筛选:
 *                  1. ID必须是偶数
 *                  2. 年龄必须大于23
 *                  3. 用户名转成大写字母
 *                  4. 用户名字母倒序
 *                  5. 只输出第一个用户的名字
 * @createTime 2022年05月05日 14:37:00
 */
public class StreamTest {
    public static void main(String[] args) {
        User user1 = new User("zhangsan", 20,1);
        User user2 = new User("lisi", 30,2);
        User user3 = new User("wangwu", 40,3);
        User user4 = new User("zhaoliu", 50,4);
        List<User> list = Arrays.asList(user1, user2, user3, user4);

        //集合就是存储
        //计算交给Stream流处理,链式编程
        list.stream().filter(u->{return u.getId()%2==0;})
                .filter(u->{return u.getAge()>23;})
                .map(u->{return u.getName().toUpperCase();})
                .sorted((u1,u2)->{return u2.compareTo(u1);})
                .limit(1).forEach(System.out::println);
    }
}

15. ForkJoin

分支合并

jdk 1.7 并行执行任务(大任务拆成小任务) 工作窃取

不让线程等待,维护的都是双端队列,上面下面同时取用,线程2完成后可以去窃取线程1未执行完的任务。

对于求和计算的任务:

package com.forkjion;
import java.util.concurrent.RecursiveTask;

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName MyFork.java
 * @Description 求和计算
 * @createTime 2022年05月05日 15:06:00
 */
public class MyFork extends RecursiveTask<Long> {

    private final Long start;
    private final Long end;

    /**
     * @Description 构造方法
     * @param start 开始
     * @param end   结束
     */
    public MyFork(Long start, Long end) {
        this.start = start;
        this.end = end;
    }

    /**
     * @Description 求和计算
     * @return Long 返回值
     */
    @Override
    protected Long compute() {
        long temp = 1000000000L;
        if((end-start)< temp){
            long sum = 0L;
            for (Long i = start; i <= end; i++) {
                sum += i;
            }
            return sum;
        }else {
            //通过mid计算出中间值
            long mid = (start + end) / 2;
            //创建子任务1
            MyFork left = new MyFork(start, mid);
            // 任务压入线程队列
            left.fork();
            //创建子任务2
            MyFork right = new MyFork(mid+1, end);
            // 任务压入线程队列
            right.fork();
            // 合并结果
            return left.join() + right.join();
        }
    }
}

除此之外,还有使用Stream流的解决方案:

    public static void test3(Long start,Long end){
        long startTime = System.currentTimeMillis();
        long reduce = LongStream.rangeClosed(start, end).parallel().reduce(0L, Long::sum);
        long endTime = System.currentTimeMillis();
        System.out.println("Stream 耗时:"+(endTime-startTime));
        System.out.println(reduce);
    }

.rangeClosed表示一个(start,end], 也就是一个闭区间,不包含end的函数

.parallel() 的意思是使用并行函数

.reduce的意思是合并流的元素并产生单个值

long reduce1 = LongStream.rangeClosed(start,end).parallel().reduce(0L,(a,b)->{return a+b;});

reduce中的函数可以换成Long::sum

16.异步回调

Future :对将来的某个时间进行建模

package com.future;

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

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName FutureTest.java
 * @Description 异步调用测试
 *              异步执行
 *              成功回调
 *              失败回调
 * @createTime 2022年05月05日 16:22:00
 */
public class FutureTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //发起一个请求
        CompletableFuture<Void> future= CompletableFuture.runAsync(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("异步执行");
        });

        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            return 200;
        });

        completableFuture.whenComplete((a, b) -> {
            System.out.println("成功回调");
            //正常的返回结果
            System.out.println("a:"+a);
            //错误的信息
            System.out.println("b:"+b);
        }).exceptionally(e -> {
            e.printStackTrace();
            System.out.println("失败回调");
            return 500;
        });

        System.out.println("11111");
        future.get();
        completableFuture.get();
    }
}

 

17.JMM

Volatile 是java虚拟机提供的轻量级同步机制

  1. 保证可见性

  2. 不保证原子性

  3. 禁止指令重排

JMM:

java内存模型,不存在的东西,概念,约定

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

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

  3. 加锁解锁是同一把锁

线程:工作内存,主内存

        线程对于内存中的数据操作并不是直接操作主内存中的数据,而是把主内存中的数据复制一份到自己的工作内存,执行引擎工作结束之后再同步到主内存中

八种操作:

  1. read操作(从主存中读取变量的值)

  2. load操作(加载主存中的值到线程的工作内存)

  3. use操作(使用执行引擎)

  4. assign操作(执行引擎返回值到工作内存)

  5. write操作(工作内存中的值写入主存)

  6. store操作 (主存存储值)

  7. lock操作(线程加锁)

  8. unlock操作(线程解锁)

 

内存可见性:

        我们现在有两个线程,一个main线程,一个是main中启动的线程,main中的线程无限循环,main线程把值改成1之后,理应停止循环,但实际没有。

package com.violate;

import java.util.concurrent.TimeUnit;

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName JMMDemo.java
 * @Description 内存可见性测试
 * @createTime 2022年05月05日 16:51:00
 */
public class JMMDemo {
    public static int num = 0;

    public static void main(String[] args) {
        new Thread(()->{
            while (num==0){

            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        num = 1;
        System.out.println("num = "+num);
    }

}

        简单来讲就是main中的新线程不知道main线程已经修改了主存中的值,还在用自己之前复制到工作内存的值不停地循环。 (没有及时同步)

package com.violate;

import java.util.concurrent.TimeUnit;

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName JMMDemo.java
 * @Description volatile关键字实现内存可见
 * @createTime 2022年05月05日 16:51:00
 */
public class JMMDemo {
    public static int num = 0;
    volatile static int num2 = 0;

    public static void main(String[] args) {
        new Thread(()->{
            while (num2==0){

            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        num2 = 1;
        System.out.println("num = "+num2);
    }
}

使用 volatile 关键字可以解决,保证可见性

不保证原子性:

原子性:不可分割

线程A执行任务的时候不能被打扰,不能被分割,要么同时成功,要么同时失败

package com.violate;

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName JMMDemo2.java
 * @Description TODO
 * @createTime 2022年05月05日 17:01:00
 */
public class JMMDemo2 {
    volatile static int num = 0;

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

    public static void main(String[] args) {
        for (int i = 0; i <20 ; i++) {
            new Thread(()->{
                for (int j = 0; j <1000 ; j++) {
                    add();
                }
            }).start();
        }

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

        System.out.println(num);
    }
}

可以发现每次执行的结果都不一样,表示了不保证原子性

假设我们不用锁,怎么实现原子性?

使用原子类解决:

package com.violate;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName JMMDemo2.java
 * @Description TODO
 * @createTime 2022年05月05日 17:01:00
 */
public class JMMDemo2 {
    volatile static AtomicInteger num = new AtomicInteger(0);

    public static void add(){
        // +1 使用了CAS
        num.incrementAndGet();
    }


    public static void main(String[] args) {
        for (int i = 0; i <20 ; i++) {
            new Thread(()->{
                for (int j = 0; j <1000 ; j++) {
                    add();
                }
            }).start();
        }

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

        System.out.println(num);
    }
}

原子类的底层调用了Unsafe类,直接在内存中修改值 。

禁止指令重排:

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

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

系统处理器在执行指令重排的时候,会考虑数据之间的依赖性。但是可能会因为指令重排导致问题。

volatile可以避免指令重排

内存屏障,CPU指令

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PigeonEssence

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值