JUC并发编程知识点,你之所以看起来很晦涩,是因为这是写给自己的笔记(二)

1. 读写锁

读可以被多个线程一起读
写只能有一个线程去写
读写锁为了更加细粒度的控制
独占锁(写锁)
共享锁(读锁)

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadAndWrite {

    public static void main(String[] args) {
        Cathe cathe = new Cathe();

        for (int i = 0; i < 9; i++) {
            int temp = i;
            new Thread(()->{
                cathe.put(temp + "",temp + "");
            }).start();
        }

        for (int i = 0; i < 9; i++) {
            int temp = i;
            new Thread(()-> {
                cathe.get(temp + "");
            }).start();
        }
    }

}

//定义一个自己的缓存
class Cathe{
    private Map<String,String> map = new HashMap<>();
    //创建可重入读写锁
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    public void put(String key,String value){
        //加上写锁
        readWriteLock.writeLock().lock();

        try{
            System.out.println(Thread.currentThread().getName() + "正在写");
            map.put(key,value);
            System.out.println(Thread.currentThread().getName() + "写完了");
        }finally {
            //写锁的释放
            readWriteLock.writeLock().unlock();
        }

    }

    public String get(String key){
        //加上读锁
        readWriteLock.readLock().lock();

        try{
            System.out.println(Thread.currentThread().getName() + "正在读");
            System.out.println(Thread.currentThread().getName() + "读完了");

            return map.get(key);
        }finally {
            //读锁释放
            readWriteLock.readLock().unlock();
        }
    }
}

2. 阻塞列队BlockingQuene

在这里插入图片描述

(要知道 Deque(双端队列),AbstractQuene(非阻塞队列))

写入时:队列满了就要阻塞等待
读取时:队列是空的也要阻塞等待

常用的实现类
ArrayBlockingQuene
LinkedBlockingQuene
SynchronousQuene

4组常用API
在这里插入图片描述

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

public class BlockQueue {
    public static void main(String[] args) throws InterruptedException {
        //test01();
        //test02();
        //test03();
        //test04();

        test05();
    }

    public static void test01(){

        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);

        //第一组API add和remove 在队满再添加 或者 列队无再取报异常
        blockingQueue.add("AAA");
        blockingQueue.add("BBB");
        blockingQueue.add("CCC");

        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.element());//拿出队头的元素
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        //System.out.println(blockingQueue.remove());
    }

    public static void test02(){
        //第二组 offer poll 队满再添加返回false 队空再去返回null 不会报错

        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);

        blockingQueue.offer("aaa");
        blockingQueue.offer("bbb");
        blockingQueue.offer("ccc");
        System.out.println(blockingQueue.offer("ddd"));

        System.out.println("======");

        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.peek());//拿出排头的元素
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());

    }

    public static void test03() throws InterruptedException {
        //第三组API 是put 队满的时候再添加会一直阻塞  take 队空的时候再移除会一直阻塞

        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);

        blockingQueue.put("aaa");
        blockingQueue.put("bbb");
        blockingQueue.put("ccc");
        //blockingQueue.put("ddd");


        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());

    }

    public static void test04() throws InterruptedException {
        //又回到了这里, offer 和 poll 可以设置等待时间 满了再添加,等待几秒,不行就掠过 poll也是同理,等完之后取不到就返回null
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);

        blockingQueue.offer("aaa");
        blockingQueue.offer("bbb");
        blockingQueue.offer("ccc");
        blockingQueue.offer("ddd",3, TimeUnit.SECONDS);

        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll(3,TimeUnit.SECONDS));
    }

    public static void test05(){
        //SynchronousQueue同步队列,与其他队列不同,不存储元素,进入一个,就拿出来一个
        BlockingQueue<String> blockingQueue = new SynchronousQueue<>();

        new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName() + "正在进入");
                blockingQueue.put("a");
                System.out.println(Thread.currentThread().getName() + "正在进入");
                blockingQueue.put("b");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();


        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(1);
                System.out.println(Thread.currentThread().getName() + "正在拿出");
                blockingQueue.take();
                TimeUnit.SECONDS.sleep(1);
                System.out.println(Thread.currentThread().getName() + "正在拿出");
                blockingQueue.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

3. 线程池

池化技术
优化资源的使用,实现线程的复用,能控制最大的并发数量
用一个池子准备好资源,谁来谁拿,用完放回来。
1.降低资源的消耗
2.提高响应速度
3.管理方便

三大方法
Executors工具类有三个创建线程池的方法
newSingleThreadExecutor()单线程池
newFixedThreadExecutor()固定大小的线程池
newCathedThreadExecutor()最大数无限大(21亿)
线程池用完要 shutdown()
但是不采用这种方式创建线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPool {

    public static void main(String[] args) {
        //ExecutorService pool = Executors.newSingleThreadExecutor(); //创建的单一的线程池
        //ExecutorService pool = Executors.newFixedThreadPool(3); //创建固定大小的线程池
        ExecutorService pool = Executors.newCachedThreadPool(); // 创建可伸缩的线程池

        try {
            for (int i = 0; i < 33; i++) {
                pool.execute(()->{
                    System.out.println(Thread.currentThread().getName());
                });
            }
        }finally {
            pool.shutdown();
        }
    }
}

七大参数
用ThreadPoolExecutor()方法创建
最大线程的承载是 最大线程数+阻塞队列大小

源码分析
    public ThreadPoolExecutor(int corePoolSize, //核心大小,在一般情况下,准备出来的线程数
                              int maximumPoolSize,//最大线程数,在线程数量需求变大时,会逐渐的开启线程,直到最大线程的大小
                              long keepAliveTime,//存活时间,emm,就是最大线程数在多久没有任务执行的时候会自动释放
                              TimeUnit unit,//时间单位
                              BlockingQueue<Runnable> workQueue,//阻塞列队,用于达到最大线程数时,再需要线程则进入阻塞队列
                              ThreadFactory threadFactory,//线程的创建工厂,一般默认
                              RejectedExecutionHandler handler//拒绝策略) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

这图是真他娘的形象

四种拒绝策略

public class ThreadPoolExecutorDemo {
    public static void main(String[] args) {
        //拒绝策略ThreadPoolExecutor.AbortPolicy() 在达到最大承载的时候,再来人就会报错,并把这个新来的线程丢弃
        //       CallerRunsPolicy() 在达到最大承载的时候,再来线程会让他原路返回,回到创建这个线程的线程,就像是回到main方法,由main方法执行
        //       DiscardOldestPolicy() 在达到最大承载的时候,再来线程会尝试让这个线程与最早来的线程竞争,抢过了就执行,抢不过,就丢弃
        //       DiscardPolicy() 在达到最大承载的时候,直接让新来的线程丢弃

        //最大线程数量的定义策略(调优)
        //1.CPU密集型
        //让他的核数是几 就让它最大的线程数量是几
        //        Runtime.getRuntime().availableProcessors();
        //使用如上方法,来获取CPU的最大核数,不能将最大线程数用常数写死
        //2.IO密集型
        //根据IO程序最大的线程设置,大于这个线程数

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 5,
                3, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardPolicy());

        try {
            for (int i = 0; i < 10; i++) {
                threadPoolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName());
                });
            }

        }finally {
        	//用完关闭线程池
            threadPoolExecutor.shutdown();
        }
    }
}

4. 四大函数式接口

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class FourFunctions {
    //四大函数式接口
    public static void main(String[] args) {

/*        Function<String, String> function = new Function<String, String>() {
            @Override
            public String apply(String o) {
                return "进来一个东西出一个东西";
            }
        };

        System.out.println(function.apply("o"));*/

        Function<String,String> function1 = (a)->{ return a; };

        System.out.println(function1.apply("Lambda"));


        //直接给你消费掉
/*        Consumer<String> consumer = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println("进来啥,消费啥");
            }
        };

        consumer.accept("???");*/

        Consumer<String> consumer = (a)->{
            System.out.println("爷,进来进出不去了拉,来玩呀");
        };
        consumer.accept("!!!");

        //慈善家,供给你
        Supplier<String> supplier = ()->{
            return "感受supplier的爱吧!";
        };
        System.out.println(supplier.get());

        //断定型,直接给你审判了
        Predicate<String> predicate = (a)->{
            System.out.println("迎接审判吧!!!");
            return true;
        };
        System.out.println(predicate.test("!!!"));
    }
}

5. 面试会问到的流式计算

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

/**
 * 题目要求:一分钟一行代码完成此题
 * 1.ID必须是偶数
 * 2.年龄大于23岁
 * 3.用户名转变为大写字母
 * 4.用户倒叙排序
 * 5.只输出一个用户
 */
public class Stream {
    public static void main(String[] args) {
        User user1 = new User(1,"a",21);
        User user2 = new User(2,"b",22);
        User user3 = new User(3,"c",23);
        User user4 = new User(4,"d",24);
        User user5 = new User(5,"e",25);
        User user6 = new User(6,"f",26);

        List<User> users = Arrays.asList(user1, user2, user3, user4, user5, user6);


        users.stream().filter(user -> {return user.getId()%2 == 0;})
                .filter(user -> {return user.getAge() > 23;})
                .map(user -> {return user.getName().toUpperCase();})
                .sorted((u1,u2)->{return -1;})
                .limit(1L)
                .forEach(System.out::println);
    }
}
class User{
    private int id;
    private String name;
    private int age;

    public User(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

6. ForkJoin

JDK1.7中引入,并行执行任务,提高效率,用于大数据的情况下。
把大任务化成小任务,最后汇成结果(递归)
特点:工作窃取,某个线程把任务执行完,还会取窃取其他线程没有执行完的任务(维护的是双端队列)

ForkJoin的执行过程
创建ForkJoinPool(用来执行任务)
ForkJoinTask的两个类型 RecurisiveTask有返回值 RecursiveAction无返回值
创建自己的ForkJoinTask要继承这两个类
fork()把方法压入
join()获取结果

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

public class ForkJoinDemo extends RecursiveTask<Long> {

    private Long start;
    private Long end;

    private Long middle = 10000L;

    public ForkJoinDemo(Long start, Long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        if((end - start) < middle){
            Long sum = 0L;
            for(Long i = start;i <= end;i++){
                sum += i;
            }
            return sum;
        }else{
            long mid = (start + end) / 2;
            ForkJoinDemo task1 = new ForkJoinDemo(start,mid);
            task1.fork();

            ForkJoinDemo task2 = new ForkJoinDemo(mid + 1,end);
            task2.fork();

            return task1.join() + task2.join();

        }
    }
}

class test{

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //test01(); //939
        //test02(); //459
        test03(); //97
    }
    //普通程序员
    public static void test01(){
        Long sum = 0L;
        long start = System.currentTimeMillis();

        for (Long i = 1L;i <= 100000000;i++){
            sum += i;
        }
        long end = System.currentTimeMillis();

        System.out.println("和为" + sum + "时间为" + (end-start));
    }

    //ForkJoin
    public static void test02() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();

        ForkJoinPool forkJoinPool = new ForkJoinPool();


        ForkJoinDemo task = new ForkJoinDemo(0L, 100000000L);
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);
        Long sum = submit.get();

        long end = System.currentTimeMillis();

        System.out.println("和为" + sum + "时间为" + (end-start));
    }

    //Stream并行流
    public static void test03(){
        long start = System.currentTimeMillis();

        long sum = LongStream.rangeClosed(0L, 100000000L).parallel().reduce(0,Long::sum);

        long end = System.currentTimeMillis();

        System.out.println("和为" + sum + "时间为" + (end-start));
    }
}

7. 异步回调

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

public class Asyn {
    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("无返回值的延迟异步回调");
        });

        System.out.println("Main函数");

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

        //有返回值的异步回调
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{

            System.out.println(Thread.currentThread().getName());
            int i = 1/0;
            return 200;
        });

        //completableFuture.whenComplete 首先若是正常执行的化,则进行返回200的语句
        //若上方代码有错误,那么则会走exceptionally的代码,返回404
        CompletableFuture<Integer> future = completableFuture.whenComplete((t, v) -> {
            System.out.println("t:" + t);
            System.out.println("v:" + v);
        }).exceptionally(e -> {
            System.out.println(e.getMessage());
            return 404;
        });

        //get的值是返回值
        System.out.println(future.get());
    }
}
原创文章 34 获赞 8 访问量 1154

猜你喜欢

转载自blog.csdn.net/qq_46225886/article/details/105785781