java 并发 详解
1 普通线程和 守护线程的区别。
守护线程会跟随主线程的结束而结束,普通线程不会。
2 线程的 stop 和 interrupted 的区别。 stop 会停止线程,但是不会释放锁之类的资源? interrupt 会让线程抛出异常。
测试:stop 和 interrupt 关于锁释放的问题。
3 关于线程 和 Thread 的 关系。 其实 实现线程的 方法只有一种,就是 new Thread 的对象 。 Thread 里面 的run 方法 默认执行方式会去 执行 Runnable target 的 run方法。 这也就是 我们说的 实现Runnaable 方法。当时这时候其实五门也创建了一个Thread 对象。
Lambda 表达式也是被翻译了一个 Runnabel 的 子类对象。
一般认为的实现线程的方法
1 继承Thread 类
2 实现Runnable 接口
3 匿名内部类直接new一个 Thread 或者 直接new 一个Runnable。
4 使用Runnable 带返回值的封装, FutureTask 和 Callable
5使用定时 使用 Timer 和 TimerTask
6 线程池
7 lambda 表达式
@Override
public void run() {
if (target != null) {
target.run();
}
}
4 如果一个线程参数里面传入了 Runnable ,然后又重写了 run 方法。这时候生效的是重写的run方法,因为 子类已经重写了 run方法,父类的 run方法不会别执行(多态)。
5 FutureTask 封装了 Runnable 和 Future 。让线程的执行可以拿到 一个返回值,其实它是 吧 Callable 的返回值 放在 一个成员上面,然后通过get 接口去拿,并且这个get 接口会等待 run 线程结束。
run 方法 执行的 Callable 的 call 方法,然后把 call 的返回值放在 成员变量上面,然后等待 Future 的 get 方法来取 这个返回值,并且这个 get 方法会等待线程执行完毕取到返回值。
6 Timer 和 t.schedule(task, time); 可以实现延时任务。 并且它会另外启动一个线程。
7 单利的写法
饿汉式:(没有线程安全问题)
/**
* 饿汉式单利写法(线程安全)
* @author ZHANGYUKUN
*
*/
public class Single {
private static Single single = new Single(); private Single (){} public static Single getInstance(){
return single;
} }
懒汉式:(有线程安全问题)
/**
* 懒汉式单利写法(有线程安全问题)
* @author ZHANGYUKUN
*
*/
public class Single2 {
private static Single2 single; private Single2 (){} public static Single2 getInstance(){
if( single == null ){
single = new Single2();
}
return single;
} }
懒汉式,双if 写法(基本不会出线程安全,但是会出指令重排序)
/**
* 懒汉式单利双id写法(基本无线程安全问题,但是有指令重排序问题)
* @author ZHANGYUKUN
*
*/
public class Single3 {
private static Single3 single; private Single3 (){} public static Single3 getInstance(){
if( single == null ){
synchronized (Single3.class) {
if( single == null ){
single = new Single3();//这里的 new 和 赋值 可能被重排序
}
}
}
return single;
} }
解释:new 一个对象,然后赋值分成3 部。
1 分配内存空间
2 初始这个空间
3 吧这个对象引用指向这个空间
指令重排序如果 让3 ,2 颠倒,那么这时候 这个 空间还没初始化,但是已经不为null了。另一个线程 获取单利对象就会 获取到这个获取到这个没有初始化的对象( 在 3 执行了,2 还没执行的 这一小段时间 会出这个问题 )。
懒汉式最终版:
/**
* 懒汉式单利双id写法(无线程安全问题)
* @author ZHANGYUKUN
*
*/
public class Single4 {
private static volatile Single4 single; private Single4 (){} public static Single4 getInstance(){
if( single == null ){
synchronized (Single4.class) {
if( single == null ){
single = new Single4();//volatile 修饰 禁止重排序
}
}
}
return single;
} }
8 自旋 是消耗资源的 wait 是不消耗资源的。
9 重入锁 。在同一线程中,允许多次进入这个锁,不需要再次获抢锁,而只是在计数器上+1。
synchronized 是重入锁。
synchronized 是在 对象头里面写了一个线程id。标志这个锁被这个线程拿到,如果 一个线程调用了这个线程的两个方法 a,和 b ,他们 都需要获取这个对象改的锁,并且 a 里面调用了 b,因为是重入锁 所以不会 死锁。
10 synchronized 在等待锁的过程中会自旋。
11 Thread.activeCount() 可以看到还有几个线程活着。
12 volatile 两个作用
1 禁止指令重排序
2 多线程 可见性
多线程环境中,一个变量被修改是发生在cpu 缓存中的。一般情况下这个修改操作不一定会里面刷新到,计算机内存中( 对象的堆内存 )。
如果是valatile 修饰的变量的改动会立刻 更新到内存中,并且,会是别的cpu 缓存中的 这个变量的 值失效。从而保证多线程可见性。
volatile 的缺点:
1 禁用重排序,会降低效率
2 volatitle 会使 cpu 缓存失效,降低性能。
13 synchornized 级能保证多线程可见性,有能保证 原子性,因为它是同步的。
volatile 只能保证保证 可见性,不能保证 原子性,如果是 非原子的操作并不能保证线程安全。
但是 synchornized 效率比 volatile 低。
14 java java.util.concurrent.atomic 包 下面有支持原子操作的辅助类。
15 lock 和 synchronize 的 比较
lock 需要显示的声明和释放,比较麻烦。但是 比synchronize 灵活,并且能实现公平锁性。
synchronize 使用简单。
lock 的灵活体现在
1 非阻塞的获取锁
2 能中断获取锁
3 给获取锁设定最大等待时间
16 ReentrantReadWriteLock 锁的降级, 在写锁还没释放的时候,就获取读锁,这样可以保证,写的的那份数据后读到的就是就是最新的,并且不阻塞别的读线程,会被被别人修改。
锁的升级,和 锁的降级正好相反,ReentrantReadWriteLock 不支持。它是为了保证 我读到的是最新的,并且用这个读到的数据直接做修改。不会出现这个 读锁释放,到写锁获取这个 时间差 数据被修改。
备注: 读锁 是共享的,写锁的排他的,正常来说 ,加了 写锁,读锁就加不上,但是 锁升级和降级是对 当前线程 读写锁 重入的一种特殊处理。
17 公平锁
获取锁的过程不是通过抢锁,而是通过排队的锁叫做公平锁。
18 读锁和写锁的目的
读锁:禁止别的线程写数据,但是别的线程可以读数据。这样的目的是我 读到的数据在锁的周期内是最新的,并且不会被改变(不让写)。
写锁: 禁止一切锁, 在写锁的范围内,数据一定是最新的,不会被别人改变(不让写),并且别人不会读到,修改数据时中间状态的数据(不让读)。
19 synchronize 在1.6 以前是个重量级锁
1.6 以后变得轻量了,
20 锁的 关系
偏向锁:在执行过程中,假如该锁没有被其他线程所获取,没有其他线程来竞争该锁,那么持有偏向锁的线程将永远不需要进行同步操作也就是说:在此线程之后的执行过程中,
如果再次进入或者退出同一段同步块代码,并不再需要去进行加锁或者解锁操作
重入锁:如果当前线程已经获取了这个锁,那么再次获取这个锁的时候,只是数量加+。并不会因为再去抢锁。
轻量级锁: 一般指的自旋锁,和 自适应自旋锁 ,在 偏向锁,没有偏向自己的时候,偏向锁 膨胀成为 轻量级锁。自旋锁一般需要指定自旋次数,自适应自旋锁会自动选择自旋次数,刚才回去过锁的,线程自旋可能多一些,获取锁概率 小的线程可能自旋次数少一些,自旋锁消耗CPU资源。
重量级锁:轻量级锁膨胀之后,就升级为重量级锁了。重量级锁是依赖对象内部的monitor锁来实现的,而monitor又依赖操作系统的MutexLock(互斥锁)来实现的,所以重量级锁也被成为互斥锁.重量级锁 阻塞的时候是 wait ,不消耗 cpu 资源,但是阻塞或者唤醒一个线程时,都需要操作系统来帮忙,这就需要从用户态转换到内核态,而转换状态是需要消耗很多时间的,有可能比用户执行代码的时间还要长。
公平锁和非公平锁: 获取锁的过程是按照顺序来的就是公平锁,抢 锁的就是非公平锁。
21 synchronized 是 重量级锁吗?
不是,synchronized 活根据 锁的 场景自动切换 锁的类型, 同线程铜锁方法切换偏向锁-->偏向锁被别人抢了变成自旋轻量级---> 自旋10次,自动升级成重量级锁。
22 线程安全的方法
1 synchronized
2 volatile
3 Aotmic 包下面的类
4 Lock 接口的 的实现类。
23 TimeUnit.SECONDS.sleep(1); 可以是 让当前线程睡一会 。
public void sleep(long timeout) throws InterruptedException {
if (timeout > 0) {
long ms = toMillis(timeout);
int ns = excessNanos(timeout, ms);
Thread.sleep(ms, ns);
}
}
24 线程等锁阻塞(block),和 等待(wait) 有什么区别?
等锁 block 是被动的等待,在么抢到锁的情况下,被动的wait,不会一直 消耗cpu 资源。并且会在 下一次锁释放的时候自动的去抢锁。
wait 是主动的 等待,必须要拿到监视器 才能 主动的 wait。wait 会主动释放 锁,wait 也是不会一直消耗cpu 资源的。 wait 的线程不会因为别的线程释放锁,而被唤醒。 需要主动被被 notify 才能 被唤醒。 wait 状态被唤醒以后,如果抢到锁进入 运行状态,如果抢不到 进入 block 状态。
25 线程状态 图
26 生产者消费者问题中 ,synchronized 的 方法让生产者消费者是用的同一把锁,也就是说生产的同时 没法消费,消费的同时没法生产? 怎么解决?
27 ReentrantLock 的 的 wait 和 notify 都是通过 Condition 来实现的, condition 相当于一个组,在这个组上await 的线程被加入这个组,下次叫醒的时候 ,也就叫醒的这个组。
ReentrantLock lock = new ReentrantLock();
lock.lock(); Condition condition = lock.newCondition();
condition.await();
condition.signal();
condition.signalAll(); lock.unlock();
28 终结 java的 容器
1 有哪些支持并发
2 有哪些 List
3 有哪些map
4 有有哪些set
5 有哪些Hash
6 有哪些tree
29 总结 树 的 类型
1 二叉树
2 红黑树
3 b-tree
4 b+tree
30 线程 join 是,让当前线程wait ,直到 join的 线程执行完成。
线程执行完成会叫醒所有监事这个线程对象的线程。
主线程 join 一个 子 线程 --> 主线程 获取了 子线程对象的监视器 -->主线线程 wait --》 子线程执行完毕--->子线程触发 叫醒所有在在这个join线程对象上 wait 的线程。
31 ThreadLocal 线程局部变量。
ThreadLocal 不真实的存储数据,在向 ThreadLocal 里面放东西的时候,它会获取 当前线程的 一个 成员 变量 map,然后 用 ThreadLocal 作为这个 map 的 key ,用你 想存到这个 ThreadLocal 的 值作为 值。 这样你 不同的线程 范围这个 ThreadLocal 对应的数据的时候,其实读的是自己 map成员变量里面 的值。
但是 ThreadLocalMap 并不是 map 的子类,但是同时 map 类似的结构。
32 线程通信类 CountDownLatch( 数量下降锁 ) ,指定一个数量,这个数量 每次减一,当这个数量变成0 的时候,await 的线程 被唤醒。
只要当 await 的 线程阻塞,没countdown 一次, state -1 ,直到 state 为0 ,await 的线程被唤醒 。
33 线程通信类 CyclicBarrier (循环屏障) ,所有线程走到 屏障点,都会阻塞,直到左后一个线程走到屏障点,然后全部前程一起呗唤醒,一起向前走。
34 线程通信类 Semaphore(信号量)Semaphore 的作用在与限流 , 指定一个 允许 的令牌数,然后 代码 执行的时候 获取一个令牌才能执行,执行完了释放令牌,保证同一时间 只有 令牌数量个线程在执行。超出的线程被拒绝杂外面
35 线程通信类 Exchanger (交换者), 两个线程执行,其中一个线程执行到指定位置 阻塞,然后等待对方也执行到 某个位置 。然后交换数据以后,继续向下执行。
36 forkjoin 框架,类似于 MapReduce ,先分开计算,然后汇众 一个结果, 目的是充分的里用 多核 cpu 资源。
37 关于支持线程安全的容器。
常见的 有Vector, Hashtable,当是 Vector ,Hashtable 处理线安全的方式是 同步方法。 效率并不高。
除了 Vector 意外 还有 Collections.sync开头的方法,可以 传入一个容器,然后 返回 一个 线程 安全的 代理类。 但是这些容器 读写 都是 同步的,读的时候不能写,写的时候不能读。在大量的读的情况下,可能效率比较低。
上面这些 线程安全的容器,都是通过 同步 来处理 线程安全的的,上面你的 Vector, Hashtable 还有 Collections.sync____ 方法的代理容器都叫做 同步容器。 同步容器都 几乎都出现 在 jdk 1.2
38 和同步容器对应 的 并发容器。 几乎都出现在 jdk 1.5
ConcurrentHashMap 1通过控制锁的粒度,以前是 锁 整个容器,现在把一个容器分成多分 ,每次只锁定一小段容器
ConcurrentLinkedQueue 使用的乐观锁,通过cas 修改数据,如果数据被人修改了就从做。没有被修改就万事大吉。 所以 去size的时候是去算的,而不是存起来的一个 数字。
CopyOnWriteArrayList ,CopyOnWriteArraySet 这类容器是在写的时候复制 一个新的副本,写完以后在吧 内部容器指向新的 副本。 因为是复制,读的那个数字 总是不加锁(类似 innodb 的非锁定读 ),所以读的效率非常高。 写的时候 效率比较低,适合 写少读多。
39 关闭 并发的时候为什么 加锁问题?
1 读写并发 一个线程在写数据,加了一个,但是这时候 size 还没增加,另一个读线程在读数据, 读到元素有 11 个 ,但是 size 是10 ,明显数据不一致了。类似数据库的脏读(读到别人没做完,或者说没提交的数据 )。 所以 读写要 互斥。
2 写写 并发。 如果没有锁 ,原来容器是 10 个,现在两个线程都在加 数据,变成12 个, 但是 他们会做的事情是吧 11 设置为 长度, 并且 他们写入的位置可能是一样的 ,都是11 这个位置。这样有一个就被 冲掉了(有点像数据库的丢失更新 )。 所以写写 要互斥。
3 读读 ,都是读 ,不会出 并发问题。
40 web 服务器,是通过 tcp 协议实现的。 只是 在堆请求和响应的格式有要求。用 SocketServer 可以实现一个简单的 web 服务器。
41 jdk 8 出的 LongAdder 和DoubleAdder 的 原理,相对于 AtomicLong 来说, 比如一个 值是10 ,以前在这个 10 上面 做并发操作,现在把10 分成几份,比如 5,3,2,0,0 多线程环境下竞争 的就不是 一把锁,而是多吧锁。这样 几个值加起来就是 最终的的值。
上面的 5 份 叫做 cell , 总值叫做base。
42 java 8 的 StampedLock ,对 ReentranReadWriterLock 的增强。
StampedLock 的 高效的 原因, ReentranReadWriterLock 读写是互斥的,StampedLock 可以读写并行。从而提高效率,原理在读的时候,如果被别的线程修改了数据,那么在读一次,取到最新的值。
StampedLock 和以前 ReentranReadWriterLock 类似 的 悲观的锁,读写互斥,也有 乐观锁的支持,读写,并行。
43 java 伪共享
伪共享的非标准定义为:缓存系统中是以缓存行(cache line)为单位存储的,当多线程修改互相独立的变量时,如果这些变量共享同一个缓存行,就会无意中影响彼此的性能,这就是伪共享。
java 并发 详解的更多相关文章
- Java虚拟机详解----JVM常见问题总结
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
- Java面向对象详解
Java面向对象详解 前言:接触项目开发也有很长一段时间了,最近开始萌发出想回过头来写写以前学 过的基础知识的想法.一是原来刚开始学习接触编程,一个人跌跌撞撞摸索着往前走,初学的时候很多东西理解的也懵 ...
- Java synchronized 详解
Java synchronized 详解 Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 1.当两个并发线程访问同一个对象object ...
- Java集合详解3:Iterator,fail-fast机制与比较器
Java集合详解3:Iterator,fail-fast机制与比较器 今天我们来探索一下LIterator,fail-fast机制与比较器的源码. 具体代码在我的GitHub中可以找到 https:/ ...
- Java集合详解4:一文读懂HashMap和HashTable的区别以及常见面试题
<Java集合详解系列>是我在完成夯实Java基础篇的系列博客后准备开始写的新系列. 这些文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查 ...
- Java集合详解3:一文读懂Iterator,fail-fast机制与比较器
<Java集合详解系列>是我在完成夯实Java基础篇的系列博客后准备开始写的新系列. 这些文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查 ...
- Java 集合详解 | 一篇文章解决Java 三大集合
更好阅读体验:Java 集合详解 | 一篇文章搞定Java 三大集合 好看的皮囊像是一个个容器,有趣的灵魂像是容器里的数据.接下来讲解Java集合数据容器. 文章篇幅有点长,还请耐心阅读.如只是为了解 ...
- Java内部类详解
Java内部类详解 说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就 ...
- 黑马----JAVA迭代器详解
JAVA迭代器详解 1.Interable.Iterator和ListIterator 1)迭代器生成接口Interable,用于生成一个具体迭代器 public interface Iterable ...
随机推荐
- Hadoop yarn任务调度策略介绍
二.Capacity Scheduler(容器调度器)的配置 2.1 容器调度介绍 Capacity 调度器允许多个组织共享整个集群,每个组织可以获得集群的一部分计算能力.通过为每个组织分配专门的队列 ...
- 从零学React Native之13 持久化存储
数据持久化就是指应用程序将某些数据存储在手机存储空间中. 借助native存储 这种方式不言而喻,就是把内容传递给native层,通过原生API存储,详见从零学React Native之05混合开发 ...
- Python第一课-Python的下载与安装
官网 https://www.python.org/ 我们安装的是windows 系统 Python3和Python2版本不兼容,我们下载最新的Python3.7.4 下载executatable版本 ...
- <爬虫>黑板爬虫闯关01
import requests from lxml import etree import time ''' 黑板爬虫闯关 网址:http://www.heibanke.com/lesson/craw ...
- LVS/Nginx/HAProxy负载均衡器的对比分析
转自:http://www.blogjava.net/ivanwan/archive/2013/12/25/408014.html LVS的特点是: 抗负载能力强.是工作在网络4层之上仅作分发之用,没 ...
- vue-cli3使用yarn run build打包找不到路径
vue-cli3使用yarn run build打包项目部署到服务器上面,运行空白 解决办法非常方便,直接创建vue.config.js 在vue.config.js中添加即可 再打包项目即成功
- 关于join的一些补充
1, 为什么join是string的method而不是list的method http://effbot.org/pyfaq/why-is-join-a-string-method-instead-o ...
- VMware的下载安装
在学习使用LINNX系统之前,先在自己的电脑上安装一个虚拟机,流行的虚拟机软件有VMware(VMWare ACE).Virtual Box和Virtual PC,它们都能在Windows系统上虚拟出 ...
- android Serializable 和 Parcelable 区别
android 中自定义的对象序列化的问题有两个选择一个是Parcelable,另外一个是Serializable. 一 序列化原因: 1.永久性保存对象,保存对象的字节序列到本地文件中:2.通过 ...
- .Net StackFrame
StackFrame指的是一个.net运行的时候堆栈上的一个帧(Frame),每次进入一个方法的时候就会有一个新的方法帧压入线程执行堆栈,可以通过StackFrame获取相关的信息,比如当前代码所在文 ...