Java锁的实现

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;
            }
        }
    }
}

 Counter类进行加锁实现:

//对变量进行加锁实现
public class Counter {
    volatile int i=0;
    Lock lock=new MyReentrantLock(); //自定义锁
    public void add(){
        lock.lock();
        i++;
        lock.unlock();
    }
}


//自定义锁
public class MyReentrantLock implements Lock{
    //标记锁的持有线程
    AtomicReference<Thread> owner=new AtomicReference<>();
    //等待的线程队列  BlockQueue [ 如果当前没有可用的空间  add 抛异常,offer 返回false,put阻塞][remove,poll,take]
    private LinkedBlockingQueue<Thread> waiters=new LinkedBlockingQueue<>();

    @Override
    public void lock(){
        //如果未获取到锁
        if(!tryLock()){
            waiters.offer(Thread.currentThread());
            //1、线程挂起的几种方式 [condition] [Suspend.resume()  容易造成死锁,已经被弃用] [wait nofity 必须与synchronized 关键字一起使用]
           for(;;){
               //判断是否是线程的头部,如果不是继续循环
               Thread peek = waiters.peek(); //peek拿到元素,元素本身不出队列
               if(peek!=Thread.currentThread()){
                 continue;
               }
               //如果是线程的头部则尝试着去拿锁
               if(tryLock()){
                   waiters.poll();// poll拿到元素,元素出队列
                   return;
               }
               //没有拿到锁,则将线程挂起
               LockSupport.park();
           }
        }
    }

    @Override
    public boolean tryLock() {
        return owner.compareAndSet(null,Thread.currentThread());
    }

    @Override
    public void unlock() {
        if(tryUnlock()){
            Thread thread=waiters.peek();
            if(thread!=null){
                LockSupport.unpark(thread); //唤醒线程
            }
        }
    }


    public boolean tryUnlock(){
        if(owner.get()!=Thread.currentThread()){
            throw new IllegalMonitorStateException();
        }else{
            return owner.compareAndSet(Thread.currentThread(),null);
        }
    }
    
    @Override
    public void lockInterruptibly() throws InterruptedException{

    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    @Override
    public Condition newCondition() {
        return null;
    }
}

 其中Unsafe参见:Java魔法类:Unsafe应用解析

转载于:https://www.cnblogs.com/gudicao/p/11487072.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值