线程三大特性
多线程有三大特性,原子性、可见性、有序性。
原子性: 即单次或者单组操作要么全部执行(执行的过程不会被任何因素打断)要么就都不执行。
可见性: 即当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
有序性: 程序执行的顺序按照代码的先后顺序执行。
简单来说,原子性保证数据的一致性,是线程安全的一部分,可见性保证多线程之间共享变量的变化能够及时被其它线程发现,有序性是指在一般情况下处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但它保证程序最终执行结果和代码顺序执行的结果是一致的。
重排序对单线程并不会产生任何影响,但在多线程情况下需要考虑。
Volatile
Volatile关键字的作用是修饰变量使其在多个线程之间可见,Volatile保证可见性,但是Volatile不具备原子性。
简单来说,使用Volatile关键字可以解决线程之间共享变量可见性问题, 强制线程每次读取该值的时候都去“主内存”中取值。
volatile与synchronized区别?
- volatile 只能修饰变量,synchronized 可修饰代码块/函数/变量,前者是轻量级的。
- volatile 只能保证可见性,不具备原子性,不能用来保证线程同步安全,因为多线程并发访问volatile修饰的变量并不会阻塞。
- synchronized 同步关键字,具备可见性原子性,只有当获取到了锁,才能执行目标代码,当多个线程执行时,只能有一个线程获取锁并执行,会出现阻塞。
总结: 简单来说线程安全,无非是“可见性”和“原子性”的问题。在并发编程中根据实际需求情况采用synchronized或lock又或是volatile+(JUC包下的类)原子类实现线程安全都是可行的方案。
ThreadLocal
ThreadLocal直译“本地线程”,ThreadLocal是作为线程的局部变量使用的,线程使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
ThreadLocal类接口很简单,只有4个方法:
- void set(Object value)设置当前线程的线程局部变量的值。
- public Object get()返回当前线程所对应的线程局部变量。
- public void remove()将当前线程局部变量的值删除,目的是减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
- protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。
ThreadLocal实现原理其实是通过map集合实现,想了解更多可以查看源码。