Java锁的实现

匿名 (未验证) 提交于 2019-12-02 21:52:03

volatile属性:可见性、保证有序性、不保证原子性。

  Java的内存中所有的对象都存在主内存中,每个线程都有自己的栈和程序计数器,多个线程对同一个对象的变量读取时,会将对象的变量从主内存中拷贝到自己的栈帧里(操作数栈),线程之间也无法直接访问对方的操作数栈,只能通过主内存传递变量的值;

  可见性:如果对声明了volatile变量进行写操作时,JVM会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写会到系统内存。 这一步确保了如果有其他线程对声明了volatile变量进行修改,则立即更新主内存中数据。

  有序性:但这时候其他处理器的缓存还是旧的,所以在多处理器环境下,为了保证各个处理器缓存一致,每个处理会通过嗅探在总线上传播的数据来检查 自己的缓存是否过期,当处理器发现自己缓存行对应的内存地址被修改了,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作时,会强制重新从系统内存把数据读到处理器缓存里。 这一步确保了其他线程获得的声明了volatile变量都是从主内存中获取最新的。

使用场景:状态标记,后续会再写另一个使用场景,单例模式下的volatile确保单例对象的返回的正确性。

原子操作:可以是一个步骤,也可以是多个步骤,但是其顺序不可以被打断,也不可以被切面只执行其中的一部分(不可中断性)。

并发示例:

public class Counter {     volatile int i=0;     public void add(){         //非原子操作 可以加锁synchronized或者ReentrantLock等保证原子性         i++;     } }  public class Main {     public static void main(String[] args) throws Exception {         final Counter counter=new Counter();         for(int i=0;i<5;i++){             new Thread(new Runnable(){                 @Override                 public void run() {                   for(int j=0;j<10000;j++){                       counter.add();                   }                   System.out.println("Done......");                 }             }).start();         }         //主线程进行等待         Thread.sleep(5000);         System.out.println(counter.i);     } }

  其中上述add()方法对应的汇编指令,其中i++被编译成下面四条指令,因此无法实现原子性:

public void add();     flags: ACC_PUBLIC     Code:       stack=3, locals=1, args_size=1          0: aload_0          1: dup          2: getfield      #2      // Field i:I   【从主内存中加载到操作数栈】          5: iconst_1                 【将1赋值给变量】          6: iadd          7: putfield      #2    // Field i:I   【将计算结果放回到主内存】         10: return       LineNumberTable:         line 11: 0         line 12: 10

 

CAS(Compare and swap)

Compare and swap 比较和交换,属于硬件同步原语,处理器提供了基本内存操作的原子性保证。

CAS操作需要输入两个数值,一个旧值A和一个新值B,在操作期间对旧值进行比较,若没有发生变化,才交换成新值,发生了变化则不交换。

Java中的sun.misc.Unsafe类,提供了compareAndSwapInt()和compareAndSwapLong()等几个方法实现CAS。

JDK中很多工具类底层都是用到了CAS机制

原子操作类:AtomicInteger、AtomicIntegerArray、AutomicIntegerFieldUpdater

并发操作类:ReentrantLock、ReentrantReadWriteLock、AQS、Semphore、CountDownLatch

Map:ConcurrentHashMap、ConcurrentSkipListMap

List:CopyOnWriteArrayList

Set:CopyOnWriteArraySet、ConcurrentSkipListSet

线程池:ThreadPoolExecutor

 

使用CAS实现锁:

public class CounterUnsafe {     volatile int i=0;     private static Unsafe unsafe=null;     private static long valueOffset;     static{         try {             //反射出unsafe对象             Field field=Unsafe.class.getDeclaredField("theUnsafe");             field.setAccessible(true);             unsafe=(Unsafe)field.get(null);             //获取Counter类中的字段i的偏移量             Field ifield=Counter.class.getDeclaredField("i");             valueOffset=unsafe.objectFieldOffset(ifield);         }catch (NoSuchFieldException |IllegalAccessException e){             e.printStackTrace();         }     }      public void add(){         for(;;){             //拿到旧的值             int current=unsafe.getIntVolatile(this,valueOffset);             //进行+1操作             int newValue=current+1;             //如果写会到堆内存成功             if(unsafe.compareAndSwapInt(this,valueOffset,current,newValue)){                 break;             }         }     } }

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!