1.线程创建的3种方式
2.线程的状态切换步骤
3.线程的5中状态
Java中的线程的生命周期大体可分为5种状态。
1. 新建(NEW):新创建了一个线程对象。
2. 可运行(RUNNABLE):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。
3. 运行(RUNNING):可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。
4. 阻塞(BLOCKED):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种:
(一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
(二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
(三). 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。
5. 死亡(DEAD,称为Terminted更好):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。
线程状态图
4.synchronized的底层实现
锁的种类(偏向锁,自旋锁,系统锁),锁的升级
偏向锁,记录这个线程的id,如果线程争用,升级为自旋锁,自旋10次后(默认10次)升级为重量级锁,向系统申请资源.
锁只能升级不能降级
执行时间短(加锁代码),线程数少,用自旋锁
执行时间长,线程数多,用系统锁
5.volatile
6.CAS(jdk1.8是Compare And Swap, jdk11.0是Compare And Set,无锁优化 自旋)
CPU源语支持,中间不能被打断
7.Unsafe
这是jdk1.8的
这是jdk11.0的
increment: sync,atomicXXX,LongAdder
8.CAS新类型锁——Reentrantlock
reentrantlock用于替代synchronized
由于m1锁定this,只有m1执行完毕的时候,m2才能执行
这里是复习synchronized最原始的语义
使用reentrantlock可以完成同样的功能
需要注意的是,必须要必须要必须要手动释放锁(重要的事说三遍)
使用syn锁定的话如果syn代码块执行完或者遇到异常,jvm会自动释放,但是lock必须手动释放锁
使用reentrantlock可以进行尝试锁定trylock,这样无法锁定,或者在指定时间内无法锁定就是放弃锁定
8.2公平锁和非公平锁
通过分析ReentrantLock中的公平锁和非公平锁的实现,其中tryAcquire是公平锁和非公平锁实现的区别,下面的两种类型的锁的tryAcquire的实现,从中我们可以看出在公平锁中,每一次的tryAcquire都会检查CLH队列中是否仍有前驱的 先是检查并设置锁的状态,这种方式会出现即使队列中有等待的线程,但是新的线程仍然会与排队线程中的对头线程竞争(但是排队的线程是先来先服务的),所以新的线程可能会抢占已经在排队的线程的锁,这样就无法保证先来先服务,但是已经等待的线程们是仍然保证先来先服务的,所以总结一下公平锁和非公平锁的区别:
1、公平锁能保证:老的线程排队使用锁,新线程仍然排队使用锁。
2、非公平锁保证:老的线程排队使用锁;但是无法保证新线程抢占已经在排队的线程的锁。
8.3ReentrantLock和Synchronized的区别
cas和sync的区别
trylock可以尝试获取锁,获取不到就算了
lockinterruptibly 可以实现锁打断
ReentrantLock可以实现公平和非公平锁,Synchronized只有非公平的锁
9.CountDownLatch
10.CyclicBarrier可循环屏障
11.Phaser同步屏障
onAdvance是在栅栏推倒的时候自动调用
12.ReadWriteLock读写锁
读锁就是共享锁,写锁就是排他锁
zookeeper和redis(两种方法)实现分布式锁
13.Semaphore(起限流作用)
Semaphore直接翻译是信号灯的意思,字面理解是信号灯亮的时候才能执行,信号灯不亮的时候不能执行.
Semaphore s = new Semaphore(2); //如果为1 则表示只允许一个线程同时执行,2则表示允许两个线程同时执行new Thread(()->{s.acquire(); //每当有一个线程执行acquire()方法后,就会减少1,当为0时,就不能执行了,所以最开始new对象的参数为多少就允许多少个线程同时执行s.release();})
14.Exchanger(用于线程之间交换数据用的,只能用于两个线程之间交换数据)
15.LockSupport
将当前线程阻塞,阻塞的方法LockSupport.park(),解除阻塞LockSupport.unpark()
16.多线程,高并发面试题
(1)实现一个容器,提供两个方法,add,size
写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束.
(2)写一个固定容量同步容器,拥有put和get方法,以及getCount方法,能够支持2个生产者线程以及10个消费者线程的阻塞调用.
17.源码阅读原则