狂神JUC并发编程笔记

视频来自: https://www.bilibili.com/video/BV1B7411L7tE?p=5&spm_id_from=pageDriver

看到一个新东西不会,狂神会先看源码

JUC = java.util.conrrunent

创建线程的三种方式
extends thread, implement runnable, implement callable接口

常规的实现并发的方式,用synchronized,以及

new Thread(new Runnable() {
 @Override
 public void run() {
   String a = "run 1";
 }
}).start();

上面代码在java 8以后可用lambda表达式:

new Thread(() -> {
   String a = "run 1";
 }
}).start();

锁的实现方式,Lock三部曲

1、 new ReentrantLock();

2. lock.lock(); //加锁

3. try { xxx} catch {} finaly { lock.unlock()}//解锁

池化技术: 事先准备好资源,有人要用,就来我这里拿,用完之后还给我

默认大小: 2
最小值、最大值

线程池的好处:

1. 降低资源消耗

2.提高响应的速度

3.(因为放到池子里了)方便管理

线程复用,控制最大并发数,管理线程

线程池: 三大方法,7种参数,4种拒绝策略

线程池三大方法

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

线程池七大参数

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {

按参数顺序依次对应:

1.核心线程数 线程池里面最小的活跃线程数
2.最大线程数 线程池里面最大的活跃线程数

3.保活时间 线程不工作的最大时间,超时了没有人调用就会释放

4.时间单位 超时用的时间单位
5.工作队列: 用来储存要执行,但是未执行的线程(也即为阻塞的线程)

6.线程工厂 用来创建线程的,一般不用动

7.拒绝策略 

线程池的执行流程,任务来了先放入核心线程池里面,其次放入非核心线程,否则进入工作队列

Volitale 不保证原子性

public class VolatileNotAtomicTest {
    private volatile static int num = 0;
    public static void add() {
        ++num;// ++num和num++都是非原子操作,一行对应3-4行汇编指令
    }
    public static void main(String[] args) {

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

        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(num); // num 一般小于 20000,因为没有原子性
    }
}

 保证有序性的原理是指令重排序,保证可见性的原理是MESE一致性缓存协议,以及CPU嗅探机制

深入理解CAS

Unsafe类: 因为java无法操作内存,而c++可以,Unsafe是Java的后门,通过这个类,调用native方法,来操作内存

public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

public final int getAndAddInt(Object this, long valueOffset, int increment) {
        int previousValue ;
        do {
            previousValue = this.getIntVolatile(this, valueOffset);
        } while(!this.compareAndSwapInt(this, valueOffset, previousValue, previousValue + increment));
// 如果当前对象内存的值(this + valueOffset) 还是和我期望的值 previousValue相等
// 那么将该对象内存值改为 previousValue + increment
// compareAndSwapInt 是CPU原语言,操作很快

        return var5;
    }

 上述自旋操作,

池的最大线程数到底该如何定义

CPU 密集型: 几核,就是几, 可以保持CPI的效率最高,如下图,CPU核 = 逻辑处理器数量,即12个

如何获取核数:

System.out.println(Runtime.getRuntime().availableProcessors());

 IO 密集型: 判断你程序中十分耗IO的线程,有多少个,比如有15个任务比较耗费线程,那么就设置最大线程数为两倍,30个

四大函数式接口

标明了@FunctionalInterface的接口

"lambda表达式就是为了优化匿名内部类而生"

Consumer, Function, Predicate, Supplier

只要是函数式接口,就能用lambda表达式简化

BlockingQueue四组API

add, remove element(返回队首)会抛出异常

offer,poll, peek(返回队首) 不会抛出异常

put,take  一直阻塞,string.sout

offer("element", 2, Timeunit.SECOND),poll(2, Timeunit.SECOND)  超时(2S)退出

ArrayBlockingQueue 基于数组结构的队列

LinkedBlockingQueue 基于链表结构的队列


版权声明:本文为newbaby2012原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。