https://segmentfault.com/blog/ressmix_multithread?page=2 Volatile Actomic CAS ThreadLocal Unsafe类 同步类容器 并发类容器 并发无阻塞式队列 并发阻塞式队列
Volatile关键字
volatile关键字的作用式保证变量在多线程之间的可见性,如果一个字段被声明为volatile,Java线程内存模型确保所有线程看到这个变量的值是一致的。与Synchronized不同。volatile变量不会引起线程上下文的切换和调度,在适合场景下拥有更低的执行成本和更高的执行效率。
volatile实现原理有两点:
- 修改volatile变量时会强制将修改后的值刷新的主内存中。
- 修改volatile变量后导致其他线程工作内存中对应的变量值失效。因此,再读取该变量值的时候就需要重新读取主内存中的值。
Unsafe类

Unsafe类,来源于
sun.misc包。该类封装了许多类似指针的操作,可以直接进行内存管理、操作对象、阻塞/唤醒线程等操作。Java本身不需要指针的操作,所以这也是该类命名为unsafe的原因之一
JUC中许多CAS方法底层实现都是基于Unsafe类在操作。
比如AtomicBoolean的compareAndSet方法:
unsafe.compareAndSwapInt方法是个native方法。(如果对象中的字段值与期望值相等,则将字段值修改为x,然后返回true;否则返回false):
Unsafe类中CAS方法都是native方法,需要通过CAS原子指令完成。在讲AQS时,里面有许多涉及CLH队列的操作,其实就是通过Unsafe类完成的指针操作。
Unsafe对象的创建
Unsafe是一个final类,不能被继承,也没有公共的构造器,只能通过工厂方法getUnsafe获得Unsafe的单例。
但是getUnsafe方法限制了调用该方法的类的类加载器必须为Bootstrap ClassLoader
@CallerSensitivepublic static Unsafe getUnsafe() {Class<?> caller = Reflection.getCallerClass();if (!VM.isSystemDomainLoader(caller.getClassLoader()))throw new SecurityException("Unsafe");return theUnsafe;}
| 类加载器名称 | 作用 |
|---|---|
| Bootstrap类加载器(Bootstrap ClassLoader) | 主要加载的是JVM自身需要的类,这个类加载使用C++语言实现的,是JVM自身的一部分,它负责将 【JDK的安装目录】/lib路径下的核心类库,如rt.jar |
| 扩展类加载器(Extension ClassLoader) | 该加载器负责加载【JDK的安装目录】jrelibext目录中的类库,开发者可以直接使用该加载器 |
| 系统类加载器(Application ClassLoader) | 负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,也是默认的类加载器 |
所以在用户代码中直接调用getUnsafe方法,会抛出异常。因为用户自定义的类一般都是由系统类加载器加载的。
/*** Unsafe类的安全限制*/public class UnsafeDemo0 {private int age;public int getAge() {return age;}public static void main(String[] args) {UnsafeDemo0 demo0 = new UnsafeDemo0();// 获取Unsafe类实例Unsafe unsafe = Unsafe.getUnsafe();try {//获取age属性的内存偏移地址long ageOffset = unsafe.objectFieldOffset(UnsafeDemo0.class.getDeclaredField("age"));//设置age的值为11unsafe.putInt(demo0,ageOffset,11);//输出结果System.out.println(demo0.getAge());} catch (NoSuchFieldException e) {e.printStackTrace();}}}
//运行结果Exception in thread "main" java.lang.SecurityException: Unsafeat jdk.unsupported/sun.misc.Unsafe.getUnsafe(Unsafe.java:97)at com.mkevin.demo3.UnsafeDemo0.main(UnsafeDemo0.java:20)
但是,是否就真的没有办法获取到Unsafe实例了呢?当然不是,要获取Unsafe对象的方法很多,这里给出一种通过反射的方法:
/*** 突破Unsafe类的安全限制*/public class UnsafeDemo1 {private int age;public int getAge() {return age;}public static void main(String[] args) {UnsafeDemo1 demo0 = new UnsafeDemo1();try {//通过反射获取Unsafe的静态成员变量theUnsafeField field = Unsafe.class.getDeclaredField("theUnsafe");//设置此字段为可存取field.setAccessible(true);//获取变量的值,强转为Unsafe类对象Unsafe unsafe = (Unsafe) field.get(null);//获取age属性的内存偏移地址long ageOffset = unsafe.objectFieldOffset(UnsafeDemo1.class.getDeclaredField("age"));//设置age的值为11unsafe.putInt(demo0,ageOffset,11);System.out.println("KEVIN-1>>"+demo0.getAge());//获取ageint age = unsafe.getInt(demo0,ageOffset);System.out.println("KEVIN-1-getInt>>"+age);//验证CAS方法boolean result = unsafe.compareAndSwapInt(demo0,ageOffset,10,50);System.out.println("KEVIN-2>>"+demo0.getAge()+","+result);//验证CAS方法result = unsafe.compareAndSwapInt(demo0,ageOffset,11,100);System.out.println("KEVIN-3>>"+demo0.getAge()+","+result);//获取并设置age = unsafe.getAndSetInt(demo0,ageOffset,99);System.out.println("KEVIN-4-getAndSetInt>>"+age+","+demo0.getAge());//获取并增加age = unsafe.getAndAddInt(demo0,ageOffset,100);System.out.println("KEVIN-5-getAndAddInt>>"+age+","+demo0.getAge());} catch (NoSuchFieldException | IllegalAccessException e) {e.printStackTrace();}}}
但是,除非对Unsafe的实现非常清楚,否则应尽量避免直接使用Unsafe来进行操作。
同步类容器
即线程安全类容器,由synchronized修饰的集合
- Vector、HashTable等古老的并发容器,都是使用Collections.synchronizedXXX等工厂方法创建的,并发状态下只能有一个线程访问容器对象,性能很低。
- 原理是在整个容器上加一把锁(synchronized),保证每次只能由一个线程访问该对象。
并发类容器
ConcurrentMap
- ConcurrentHashMap替代HashMap、HashTable
- ConcurrentSkipListMap替代TreeMap
- 底层原理:不是使用synchronized,ConcurrentHashMap将hash表分为16个segment,每个segment单独进行所控制,从而减小了锁的粒度(相当于最高支持16个线程同时访问hash表),提升了性能。
COW类并发容器
- copy on write容器,简称COW;写时复制容器,想容器中添加元素时,先将容器进行Copy出一个新的容器,然后将元素添加到新容器中,再将原容器的引用指向新容器。并发读的时候不需要锁定容器,因为原容器没有变化,使用的是一种读写分离的思想。由于每次更新都会复制新容器,所以如果数据量较大,并且更新操作频繁则对内存消耗很高,建议在高并发读的场景下使用。
- CopyOnWriteArraySet基于CopyOnWriteArrayList实现,其唯一的不同是在add时调用的是CopyOnWriteArrayList的addIfAbsebt方法,adIfAbsent方法同样采用锁保护,并创建一个新的大小+1的Object数组。遍历当前Object数组,如果Object数组已经有了当前元素,则直接返回,如果没有则放入Object数组的尾部,并返回。从以上分析可见,CopyOnWriteArraySet在add时每次都要进行数组遍历,因此其性能会低于CopyOnWriteArrayList。
COW迭代器的弱一致性问题
- 使用COW容器的iterator方法实际返回的是COWIterator实例,遍历数据为快照数据,其他线程对于容器元素增加、删除、修改不对快照产生影响。
对java.concurrent.CopyOnWriteArrayList、Java.util.comcurrent.CopyOnWriteArraySet均适用。
/*** CopyOnWriteArrayList 的迭代器也为弱一致性迭代器*/public class COWDemo0 {public static void main(String[] args) throws InterruptedException {CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();list.add(1);list.add(2);list.add(3);Iterator<Integer> iterator = list.iterator();Thread td = new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}list.add(4);list.add(5);}});td.start();td.join();while(iterator.hasNext()){System.out.println(iterator.next());}System.out.println("------------------");for(int i=0;i<list.size();i++){System.out.println(list.get(i));}}}
并发阻塞队列-SynchronizedQueue

