关于java:java多线程相关

2次阅读

共计 4928 个字符,预计需要花费 13 分钟才能阅读完成。

一、线程的状态?

1、新建状态:new 一个线程,没还有 start
2、运行状态(就绪和运行):调用线程的.start 办法
    1) 就绪,调用了 start 办法,CPU 没有调配工夫片
    2) 运行,调用了 start 办法,CPU 正在调度
3、阻塞状态:当竞争 synchronized 锁时,没拿到,线程挂起
4、期待状态:join,wait,(LockSupport)park 办法
5、超时期待状态:Thread.sleep(long),wait(long),join(long),parkNanos(……)
6、死亡状态:run 办法完结,或者在 run 办法中抛出异样没有 

二、线程池外围参数

Java 中提供了基于 Executors 构建线程池的形式

间接应用 Executors 构建会造成对线程池的管制力度很粗

必须以手动的形式构建线程池

public ThreadPoolExecutor(int corePoolSize,      // 外围线程、int maximumPoolSize,  // 最大线程有存活工夫
                              long keepAliveTime,  // 生存工夫、TimeUnit unit,  // 单位、BlockingQueue<Runnable> workQueue,  // 工作队列、ThreadFactory threadFactory,  // 线程工厂、为了设置线程的名称,不便前面做调试
                              RejectedExecutionHandler handler) {}  // 回绝策略 

三、线程池的执行流程

提交工作到线程池中,让线程池中的线程去执行工作

1、提交工作到线程池后

  • 如果有闲暇的外围线程,间接执行
  • 如果没有闲暇的外围线程,尝试创立外围线程,去执行工作

2、如果曾经达到了外围线程数配置

将工作扔到工作队列中排队,期待外围线程执行完其余工作再来执行我

3、如果工作队列满了放不下工作了,构建最大线程数

4、如果最大线程也曾经构建满了,执行回绝策略

四、线程池中的 ctl 属性什么用?

ctl 是线程池中一个属性,实质就是 int 类型的数值

高 3 位形容线程池的状态,低 29 位形容工作线程的数量

线程池在执行工作时,须要屡次判断线程池状态,来的确工作是否须要执行(以哪种形式执行)

低 29 用表述线程池中现存的工作线程数量

五、线程池的状态?

// RUNNING- 线程池在失常工作,能够解决提交的工作!!!private static final int RUNNING    = -1 << COUNT_BITS;
    // 调用线程池的 shuwdown() 办法,从 RUNNING -> SHUTDOWN , 不接管新的工作,然而会解决线程池外部现有的工作包含队列
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    // 调用线程池的 shuwdownNow() 办法,从 RUNNING -> STOP , 不接管新的工作,中断正在解决的工作, 不论工作队列工作
    private static final int STOP       =  1 << COUNT_BITS;
    // 过渡状态,会从 SHUTDOWN 和 STOP 转到 TIDYING 状态
    // SHUTDOWN - 工作队列为空,工作线程为空 - TIDYING   
    // STOP - 工作线程为空 - TIDYING
    private static final int TIDYING    =  2 << COUNT_BITS;
    // 当线程池达到了 TIDYING 后,源码中会主动调用 terminated,进入到了 TERMINATED 状态,线程池凉凉
    private static final int TERMINATED =  3 << COUNT_BITS;

六、什么是工作线程?

在 Java 的线程池中,工作线程指的是 Worker 对象

线程池中的工作线程是用 Worker 对象表述的

addWorker(Runnable, true/false)
增加一个 Worker 对象到线程池中,Runnable 具体要执行的工作
true:增加的是外围线程数
false:增加的是最大线程数

Worker 其实就是线程池中的一个外部类,继承了 AQS,实现了 Runnable
private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable{}
线程池执行工作,理论就是调用了 Worker 类中的 run 办法外部的 runWorker 办法
Worker 继承 AQS 的目标是为了增加标识来判断当前工作线程是否能够被打断!

七、工作线程存到在哪个地位?

 存储在了线程池的一个 HashSet 里
private final HashSet<Worker> workers = new HashSet<Worker>();

八、回绝策略

1:Abort:抛异样
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {throw new RejectedExecutionException("Task" + r.toString() +
                                                 "rejected from" +
                                                 e.toString());
        }
2:Discard:扔掉,不抛异样
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
3:DiscardOldest:扔掉排队工夫最久的,行将执行的工作
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {e.getQueue().poll();
                e.execute(r);  // 再次走一遍线程池的执行流程
            }
        }
4:CallerRuns:调用者解决服务,造成调用者性能急剧下降。public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {r.run();
            }
        }

九、如何在线程池执行工作前后做额定解决

try {
    // 前置加强
    beforeExecute(wt, task);
    try {
        // 执行工作
        task.run();} catch (Throwable x) {thrown = x; throw new Error(x);
    } finally {
        // 后置加强
        afterExecute(task, thrown);
    }
}

protected void beforeExecute(Thread t, Runnable r) { }

protected void afterExecute(Runnable r, Throwable t) {}

十、如何正当的调配线程池的大小

 在调配线程池容量大小时,必须要依据你的业务类型来决定
CPU 密集型,IO 密集型,混合型
CPU 密集型:更多的 CPU 在做计算,始终在工作
IO 密集型:更多的时候线程在期待响应
混合型:啥工作都有!1、CPU 密集型:线程数少一点,举荐:CPU 内核数 +1
2、IO 密集型:线程数多一些,举荐:个别 CPU 内核数 * 2,(线程等待时间与线程 CPU 工夫之比 + 1)* CPU 数目
3、混合型:能够将 CPU 密集和 IO 密集的操作分成两个线程池去执行即可!

十一、如果存在临界(共享)资源,如何保障线程的安全性

1、互斥锁:synchronized、Lock(ReentrantLock,ReadWriteLock)2、非阻塞锁:CAS(真的无锁嘛,底层 lock cmpexchg 有锁,Java 层面没锁)3、不采纳任何锁:ThreadLocal,适当的状况采纳 volatile 也成!ThreadLocal:让多个线程将共享资源 copy 到本地,没有多线程操作共享资源的问题
    volatile:只有不蕴含并发对共享数据进行运算,根本没问题。

十二、ThreadLocal 到底是什么?

ThreadLocal 的实质就是一个 Map。
ThreadLocal 能够将一个数据和本地线程绑定在一起。

十三、ThreadLocal 的内存透露问题?

Java 中四种援用

强援用:OOM 也不革除

软援用:内存不足革除

弱援用:只有 GC 就革除

虚援用:拿不到援用,构建进去就凉凉~~

想要解决这个问题,就在应用 TheadLocal 结束后,进行 remove 操作

十四、volatile

可见性和禁止指令重排,无奈保障原子性!

为什么 CPU 会指令重排?

CPU 会在保障 happens-before 的前提下,对指令进行从新排序,从而提高效率

为了实现禁止指令重排,JVM 虚拟机提出了标准,内存屏障

  • LoadLoad
  • StoreLoad
  • LoadStore
  • StroeStore

hotSpot 虚拟机实现的很简略,在两条指令两头,追加一个 lock 指定实现 volatile 的成果

CPU 级别中多线程解决共享数据时,加锁。

lock 指令会让 CPU 内存中的数据操作完同步到主内存。

十五、伪共享(缓存行共享)问题

CPU 外部分为 L1,L2,L3 内存,CPU 外部内存,效率比去主内存中找数据快的多!

个别的 64 的 CPU,外部会有缓存行存储数据,一个缓存行是 64byte

个别的解决形式,就是让一个业务的数据填满整个缓存行。

long l = 真正的数据。

long l1,l2,l3,l4,l5,l6,l7;

在 JDK1.8 中,个别采纳 @Contended 注解即可实现

十六、CAS

compare And Swap

CAS 存在的问题:

ABA 问题: 追加版本号解决

如果失败次数过多,占用 CPU 资源: 不同场景有不同的解决计划

synchronized:解决计划是自适应自旋锁,如果自旋次数过多,就挂起线程

LongAdder:自增时,如果失败,将失败的信息增加到 Cell[] 中

只能保障一个数据的平安: 无奈像 synchronized 一样锁住一段代码,ReentrantLock 外部就是基于 CAS 的形式实现了锁的成果

synchronized-ReentrantLock:看马老师和黄老师的视频,外面会有零碎解说

十七、ConcurrenthashMap

只说 JDK1.8 的……

ConcurrenthashMap 在没有 Hash 抵触时,以 CAS 的形式尝试插入到数组中

如果有 Hash 抵触,这个时候回将以后数组索引地位锁住,以 synchronized 的模式挂到链表上面

如果数组长度达到了最开始的长度的 0.75 时,就要将数组长度扩充二倍,素来防止链表过长造成查问效率较低

十八、ConcurrenHashMap 在并发扩容时,如何保障平安?

在计算 Node 中 key 的 hash 值时,会特意的将 hash 值失常状况的数值定义为负数

正数有非凡的含意,如果 hash 值为 -1,代表以后节点正在扩容

ConcurrenthashMap 会在扩容时,每次将老数组中的数据 table.size – 1 ~ table.size – 16 索引的地位挪动,而后再迁徙其余索引地位的数据,如果有线程在插入数据时,发现正在扩容,找还没有被迁徙数据的索引地位,帮忙最开始扩容的线程进行扩容,

最开始扩容 A:31~16

线程 B 插入数据,发现正在扩容,帮你迁徙数据,15~0 索引地位

每一个迁徙结束的数据,都会加上标识,代表扩容结束,放上一个 ForwardingNode 节点,代表扩容结束,而且再扩容是,不会利用 ConcurrentHashMap 的遍历,查问和增加(发现扩容,会帮忙~)

十九、线程扩容时,会应用 sizeCtl 记录当初扩容时的线程数量,那么为什么 1 个线程扩容时,低位数值为 2,2 个线程扩容为?

如果 sizeCtl 为 -1,代表 ConcurrentHashMap 正在初始化,- N 代表正在扩容

所以不得已,要将 1 个线程正在扩容的标识这是为 -2,- 2 代表有 1 个线程扩容

- 3 代表有 2 个线程扩容。

二十、AQS

AQS 是啥?

AQS 是 JUC 包下的一个并发基类,很多内容都基于 AQS 实现,如罕用的
ReentrantLock/Semaphore/CountDownLatch/ 线程池。

AQS 构造?

CLH(双向队列)+state(int 类型的变量)

基于双向队列和 CAS 的形式操作 state,实现了各个 JUC 下罕用的并发内容

偏心锁:AQS 队列有 Node,就间接排队,不竞争锁资源

非偏心锁:啥也不论,上来间接先竞争锁资源,而后再走下面套路

本文由博客一文多发平台 OpenWrite 公布!

正文完
 0