一,前言
个人总结,带有个人主观,请选择性观看。
二,基础
(一)创建方式
1,实现 **Runable**
2,使用 **Thread**
3,线程池创建 **Executorse**.newCachedThreadPool()
其实哪有那么多创建方式,本质上都是实现了Runable 接口。
(二)线程方法
只列出大部分使用的方法,并未代表所有线程方法,后续会添加实际的例子,以供参考。
1,sleep
睡眠 : 当前线程暂停一段时间,让给别的线程
2,yield
让出一下:返回就绪状态,进入等待队列
3,join
保证线程顺序 : 在自己线程内调用,等待别人结束,然后在接着运行
4,interrupt
给线程设置中断标志:作用于当前线程
5,interrupted
检测中断并清除中断状态:作用于当前线程
6,isInterrupted
只检测中断:作用于此线程
(三)线程周期
(四)线程同步关于synchronized
1,是什么?
是同步锁,用来实现互斥同步。
2,怎么使用?
(1)修饰实例方法
public synchronized void increase() {}
(2) 修饰静态方法
public static synchronized void increase() {}
(3) 修饰代码块
byte[] i = new byte[1];
synchronized(i){}
3,锁的是什么?
锁的是对象(对象头64位的前两位),不是代码。
this XX.class
4,为什么要上锁?
资源不能同时使用。
5,文件load到内存是否单利?
同一个加载器是,不同加载器无法相互访问,所以只要能访问就是单利的。
三,锁
(一)可重入锁
1,什么是可重入锁?
同一个加锁线程自己调用自己不会发生死锁情况;
防止死锁。
2,实现原理
通过为每个锁关联一个请求计数和一个占有它的线程。当计数为 0 时,认为锁是未被占有的。
线程请求一个未被占有的锁时,jvm 将记录锁的占有者,并且将请求计数器置为 1 。
如果同一个线程再次请求这个锁,计数将递增;每次占用线程退出同步块,计数器值将递减。
直到计数器为0,锁被释放。
3,互斥同步的缺点
互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能问题,因此这种同步也被称为阻塞同步。
而且加锁方式属于悲观锁(不管操作是否成功都加锁)。
4,与同类的区别
synchronized 和 ReentrantLock 都是可重入锁。
ReentrantLock 表现为 API 层面的互斥锁(lock() 和 unlock() 方法配合 try/finally 语句块来完成),synchronized 表现为原生语法层面的互斥锁。
(二)什么时候适合用什么锁?
名称 | 线程量 | 执行时间 |
---|---|---|
自旋锁 | 线程少 | 执行时间短 |
OS系统锁 | 线程多 | 执行时间短 |
(三)锁升级
1,偏向锁 -> 自旋锁
如果线程争用,升级为自旋锁。
2,自旋锁 -> 重量级锁
10次以后,升级为重量级锁。