签到5!
如何使用多线程
实现Runable接口或者Callable接口、继承Thread类,调用ExcutorService,Future实现带返回结果的线程
- Thread的状态
New、Runabled、Blocked、Time_waiting、Terminated
jps显示java进程的id
jstack打印进程的堆栈跟踪信息
如何终止一个线程:并不是stop,造成不可预知的结果,有些资源没有释放,如何优雅的关闭,interrupt
通过thread.interrupt()方法修改该线程Thread.currentThread.isInterrupted()改为true,从而通过标志位来合理的有机会处理资源修改问题;
在线程中使用sleep等阻塞方法进行阻塞的时候,调用Thread.interrupted()方法会抛出异常会让线程的中断位复位,改为false,这种机制可以使阻塞的线程有一种感知状态,这里就使用interrupt来让它感知到,从而在阻塞方法中修改相应的处理,并且interrupted又会复位,这就达成了某种默契
- 多线程引用引发的问题 共享数据的安全性问题
synchronized:优化后:偏向锁、轻量级锁、重量级锁
- volatile
原理就是主存和每个线程的私有工作内存数据不一致导致的问题
MESI:修改状态、独占状态、共享状态、不可用状态
私有线程只能读取MES中的数据,读取I的状态只能从主存中
私有线程只能写MS的数据,如果是S状态,需要先将其他线程的S状态改为I状态再进行写
但是也存在问题,如果告知其他线程的S为I,那么就存在一个问题,告知和受到回应都会阻塞该线程
为了防止阻塞那只能缓存了,缓存啥?store buffer
然后再异步就可以处理了不需要进行阻塞了
这个又存在问题了,指令的发送接收不确定了,现在又得先去store buffer中取没有再去缓存中取数据;由于这个情况,造成了传说中的指令乱序 或者指令重排,重排的结果导致硬件工程师崩溃
硬件工程师提供了内存屏障让软件自己去调用
写屏障:写屏障之后的所有读或者写可见,写屏障让store buffer中的数据同步到主存中,刷新store buffer
读屏障:读屏障之后的读都对于读可见
全屏障:结合读写屏障
那么,通过屏障可以有效的防止指令乱序的问题
Volatile其实就是在汇编语言上加了lock,实现了屏障的功能
保证了可见性、有序性禁止重排
java的内存模型:主内存和工作内存
主内存是线程共享的,引用对象、数组、静态变量等存储在堆中的变量
工作内存:线程私有的工作内存对变量的改变都是线程内的,不能直接改变主内存中的变量进行读写,线程直接的变量传递都是通过主内存的共享变量进行的
内存屏障就是可以刷新缓存,限制指令重排
那么Volatile就是解决可见性和有序性的
JUC
在juc包中包含了线程池、计时器、并发集合、同步器、阻塞队列等
lock
-
ReentrantLock 可重入锁,唯一实现了lock接口的类,可重入锁的意思是获取锁的线程下次再获取该锁时不需要阻塞而是锁的计数加一
-
ReentrantReadWriteLock 重入读写锁,读多写少的情况下解决线程安全问题
-
StampedLock 可以理解为读写锁的升级版,由于虽然读写分离了,但是只适合于读多写少,读线程太多之后,造成写锁饥饿,现在的StampedLock中的是一种乐观锁完全不阻塞,这样性能就提高了