- 并发/并行
- 并发:多任务交替执行,任务之间可能是串行,也可能是并行;多任务在一个cpu上轮流执行
- 并行:多任务同时执行。
- 互斥/同步
- 互斥:共享的进程资源,单个线程访问的时候具有排他性;
- 同步:线程之前的按照顺序执行
- 并发编程三要素
- 原子性;
- 可见性;(syn/volatile)缓存带来的可见性问题;
- 有序性;编译优化;
- 形成死锁的四个必要条件
- 互斥:一个资源只能被一个线程持有;
- 不可剥夺;
- 请求-保持;
- 循环等待;
- runnable/callable
- 都是接口,用start()启动线程
- 但是runnable接口的run方法无返回参数,且不能捕获ex处理;callable接口返回值是一个泛型,可以和future/futuretask配合异步获得执行结果,用future.get()方法获得,get方法是阻塞的;
- 线程的start() / run()
- start():会启动一个线程,并进入就绪状态,等待cpu时间片,分到时间片以后会自动之前run方法;方法只能执行一次;
- run():普通方法,调用后在主线程执行;
- Java中的线程调度算法
-
计算机通常只有一个 CPU,在任意时刻只能执行一条机器指令,每个线程只有获得CPU 的使用权才能执行指令。所谓多线程的并发运行,其实是指从宏观上看,各个线程轮流获得 CPU 的使用权,分别执行各自的任务。在运行池中,会有多个处于就绪状态的线程在等待 CPU,JAVA 虚拟机的一项任务就是负责线程的调度,线程调度是指按照特定机制为多个线程分配 CPU 的使用权。
有两种调度模型:分时调度模型和抢占式调度模型。
分时调度模型是指让所有的线程轮流获得 cpu 的使用权,并且平均分配每个线程占用的 CPU 的时间片这个也比较好理解。
Java虚拟机采用抢占式调度模型,是指优先让可运行池中优先级高的线程占用CPU,如果可运行池中的线程优先级相同,那么就随机选择一个线程,使其占用CPU。处于运行状态的线程会一直运行,直至它不得不放弃 CPU
-
-
锁池/等待队列 ???
-
锁池(同步队列):如果对象的锁已经被某个线程持有,此时有其他线程要进入同步代码块,那么需要获取对象锁的拥有权,但是此时锁又已经被别的线程占有了,那么就会进入该对象的锁池。
-
等待队列:如果持有锁的线程,调用了对象的wait方法,那么就会进入等待队列。直到调用notify/notifyall,会唤醒1个或者全部的线程进入锁池,尝试去竞争锁。如果一次竞争失败,还会在锁池中,下一次再竞争。
-
使用wait/notify
synchronized (monitor) { // 判断条件谓词是否得到满足 while(!locked) { // 等待唤醒 monitor.wait(); } // 处理其他的业务逻辑 }
-
持有对象锁,syn(obj);
-
while(flag):wait();
-
执行业务代码
-
-
线程类的构造方法和静态块调用
-
线程类的构造方法和静态块是被new这个线程类所在的线程所调用的,只有run方法是这个线程本身调用的;
-
-
java线程数过多
-
线程的生命周期开销非常高;
-
消耗过多的cpu:
-
降低jvm稳定性;
-
-
重排序
-
为了提高性能,处理器和编译器常常会对指令进行重排序;
-
在单线程环境下不能改变程序运行的结果;
-
存在数据依赖关系的不允许重排序;
-
-
重排序不会破坏单线程环境的执行结果,但是会破坏多线程的执行语义。
-
-
synchronized
-
用来控制线程同步的,多线程环境下,控制syn代码段不会被多个线程同时执行;
-
synchronized 关键字加到 static 静态方法和 synchronized(class)代码块上都是是给 Class 类上锁。synchronized 关键字加到实例方法上是给对象实例上锁。
-
尽量不要使用 synchronized(String a) 因为JVM中,字符串常量池具有缓存功能!
-
对于synchronized同步代码块或者同步方法,当出现异常的时候,虚拟机会自动释放当前线程占用的锁,因此不会出现由于异常导致出现死锁现象。
-
-
synchronized原理
-
syn需要持有对象的监视器锁monitor,持有的时候会将进入数设置为1;在此进入+1;退出-1.为0时,其他线程可尝试持有;
-
syn是可重入锁,底层维护了一个计数器;可重入锁是为了防止死锁;
-
syn会自旋,非公平锁。
-
-
锁的状态
-
无锁/偏向锁/轻量级锁/重量级锁
-
-
锁升级
-
偏向锁:因为大多数时候,其实是不存在竞争的,所以引入了偏向锁,在线程a访问同步代码块的时候,会在锁对象的对象头中的锁标志位设置为01,记录线程a的threadid。偏向锁是不会主动放弃锁的,那么如果线程a再次尝试获取锁,那么比较threadid是否相同,如果相同,那么不用cas加锁,解锁;如果不相同,是线程b来尝试获取,那么需要查看锁对象头中记录的线程a是否存活,如果不,那么锁对象重置为无锁状态,线程b竞争并设置为偏向锁;如果存活,那么查看线程a的栈帧信息,是否还持有,如果还需要持有,那么暂停线程a,升级为轻量级锁;如果不再持有,那么设置为无锁。
-
轻量级锁:轻量级锁主要用于竞争锁对象的线程不是很多,同时线程持有锁的时间也不是很长的情况,因为阻塞线程需要cpu从用户态转为 内核态,如果自旋一会锁就被释放了,那么无疑得不偿失。线程a获取轻量级锁的时候,会把锁对象头的markworld复制到栈帧中用于存储锁记录的空间,然后使用cas把对象头的内容替换成线程a存储的锁记录的地址。如果线程b也尝试获取锁失败的时候,会先尝试自旋。如果自旋次数到了/自旋的时候有线程c竞争,那么会升级为重量级锁,把除了持有锁以外的线程全部阻塞。(因为线程的调度是在内核态运行的,而线程中的代码是在用户态运行)
-
重量级锁:除了持有锁以外的线程全部阻塞。
-
锁可以升级不可以降级,但是偏向锁状态可以被重置为无锁状态。
-
-
synchronized、volatile、CAS 比较
-
synchronized 是悲观锁,属于抢占式,会引起其他线程阻塞。
-
volatile 提供多线程共享变量可见性和禁止指令重排序优化。
-
CAS 是基于冲突检测的乐观锁(非阻塞)
-
-
synchronized 和 Lock 的区别
- 首先synchronized是Java内置关键字,在JVM层面,Lock是个Java类;
- synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。
- synchronized 不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁;而 lock 需要自己加锁和释放锁,如果使用不当没有 unLock()去释放锁就会造成死锁。
- 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。
- ReentrantLock 使用起来比较灵活,但是必须有释放锁的配合动作;
- ReentrantLock 必须手动获取与释放锁,而 synchronized 不需要手动释放和开启锁;
- 二者的锁机制其实也是不一样的。ReentrantLock 底层调用的是 Unsafe 的park 方法加锁,synchronized 操作的应该是对象头中 mark word
- volatile
- 加入volatile关键字以后,会多出一个lock前缀指令,汇编代码。实际上相当于一个内存屏障,cpu指令。
这个内存屏障,确保重排序的时候内存屏障之前的代码不会跑到屏障后面;同时强制将缓存的修改操作立即写入主存;如果是写操作,导致其它工作内存中对应的缓存行无效。 - volatile 常用于多线程环境下的单次操作(单次读或者单次写)。
- 加入volatile关键字以后,会多出一个lock前缀指令,汇编代码。实际上相当于一个内存屏障,cpu指令。
-
synchronized 和 volatile 的区别
-
volatile 是变量修饰符;synchronized 可以修饰类、方法、变量
-
volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性。
-
volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。
-
volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。
-
volatile关键字是线程同步的轻量级实现,所以volatile性能肯定比synchronized关键字要好。但是volatile关键字只能用于变量而synchronized关键字可以修饰方法以及代码块。synchronized关键字在JavaSE1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁以及其它各种优化之后执行效率有了显著提升,实际开发中使用 synchronized 关键字的场景还是更多一些
-
-
final不可变对象
-
对象一旦被创建它的状态(对象的数据,也即对象属性值)就不能改变,反之即为可变对象(Mutable Objects)。
-
不可变对象保证了对象的内存可见性,对不可变对象的读取不需要进行额外的同步手段,提升了代码执行效率。
-
-
lock相对同步优势
-
可以使线程在等待锁的时候响应中断
-
可以实现公平锁;
-
可以实现读写锁
-
-
cas的问题
-
aba
-
自旋开销
-
只能保证一个变量的原子操作
-
参考
1. https://blog.csdn.net/tongdanping/article/details/79647337
2. https://thinkwon.blog.csdn.net/article/details/104863992