【java并发】基于JUC CAS原理,自己实现简单独占锁

synchronized的基本原理回顾

在jvm内部,所有对象都含有单一的锁,jvm负责跟踪监视被加锁次数,叫做对象监视器。当线程第一次给对象加锁的时候,计数器会加1,离开时会减1.同样任务是可重入的,每次重入也是加1,离开减1. 
synchronized是独占式的,拿到对象锁才能继续,没有获取到锁就会阻塞。

JUC CAS乐观锁基本原理

synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。

乐观锁就是假设没有冲突而去完成某项操作,如果因为冲突失败就重试。
原理有点类似于在数据库记录字段增加一个版本号,每次更新的时候做两个事情:

1.检查数据库记录当前的版本号和读取到的版本号是否一致,不一致说明数据已不是最新,更新失败需要重试.

2.如果版本号一致,更新成功,同时将版本号加1.
在jdk1.5开始,Doug Lea在JUC类库里提供了类似的乐观锁的机制叫CAS.
CAS简介:compareAndSet的意思,就是先比较是否是期望值(是期望值,说明没人更改过,当然也有可能有ABA情况),如果是再设值,不是就设值失败,线程阻塞。如果是基于这个,就要保证比较和设值这两个动作是原子性的,如何保证呢?这个是借助于JNI,利用CPU硬件支持来完成的。利用硬件提供swap和test_and_set指令,单CPU下同一指令的多个指令周期不可中断,SMP中通过锁总线支持这两个指令的原子性。

volilate关键字
Java语言规范允许线程保存共享成员变量的私有拷贝,当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。这样就延伸出可见性的一个问题。
CAS解决了比较和更新的原子性,但是还有另外一个问题就是要保证可见性。Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
volatile关键字有两层含义:
1.对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。
2.volatile前后的代码不能重排

其他话题
volilate和cas只能乐观锁保证的状态控制的正确,而在设置状态失败的时候,仍然需要阻塞线程。juc里提供了LockSupport的park和unpark方法用于阻塞线程。而不同的场景下需要不同的等待策略和锁共享策略,juc提供了AbstractQueuedSynchronizer(AQS)为基类的一序列不同的锁,底层都是基于CAS、LocakSupport和Queue来管理,后续有时间细细分析。

juc基于的CAS,提供了带有原子性的基本类型封装类,如AtomicInteger、AtomicLong等。
AtomicInteger原理:
如自增:

Java代码
 
《【java并发】基于JUC CAS原理,自己实现简单独占锁》

  1. /**  
  2.  * Atomically increments by one the current value.  
  3.  *  
  4.  * @return the previous value  
  5.  */  
  6. public final int getAndIncrement() {   
  7.     for (;;) {   
  8.         int current = get();   
  9.         int next = current + 1;   
  10.         if (compareAndSet(current, next))  //cas   
  11.             return current;   
  12.     }   
  13. }  
    /**
     * Atomically increments by one the current value.
     *
     * @return the previous value
     */
    public final int getAndIncrement() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))  //cas
                return current;
        }
    }

 compareAndSet的实现如下:

Java代码
 
《【java并发】基于JUC CAS原理,自己实现简单独占锁》

  1. public final boolean compareAndSet(int expect, int update) {   
  2. urn unsafe.compareAndSwapInt(this, valueOffset, expect, update); //JNI调用   
  3. }  
    public final boolean compareAndSet(int expect, int update) {
	return unsafe.compareAndSwapInt(this, valueOffset, expect, update); //JNI调用
    }

 

 

 

自己实现简单乐观独占锁

基于cas,本人简单实现了一个乐观独占锁,代码如下:

 

基于CAS简单乐观独占锁

 

Java代码
 
《【java并发】基于JUC CAS原理,自己实现简单独占锁》

  1. package lock.test;   
  2.   
  3. import java.util.ArrayList;   
  4. import java.util.List;   
  5. import java.util.concurrent.atomic.AtomicBoolean;   
  6. import java.util.concurrent.locks.LockSupport;   
  7.   
  8. /**  
  9.  * 简单乐观独占锁  
  10.  */  
  11. public class OptimisticExclusiveLock {   
  12.   
  13.     /**  
  14.      * 独占锁标记 true 锁不可用 false 锁可用  
  15.      */  
  16.     private AtomicBoolean state = new AtomicBoolean(false);   
  17.     List<Thread>          queue = new ArrayList<Thread>();//阻塞队列   
  18.   
  19.     public boolean lock() {   
  20.         if (!state.get()&&state.compareAndSet(falsetrue)) {//取锁成功不会阻塞,程序会继续执行   
  21.             return true// 利用CAS   
  22.         } else {   
  23.             queue.add(Thread.currentThread());//加入阻塞队列   
  24.             LockSupport.park();//阻塞线程   
  25.             return false;   
  26.         }   
  27.     }   
  28.   
  29.     public boolean unLock() {   
  30.         if (state.get()) {   
  31.             queue.remove(Thread.currentThread());//从队列里移除   
  32.             if (state.compareAndSet(truefalse)) {// 利用CAS   
  33.                 if(!queue.isEmpty()){   
  34.                     LockSupport.unpark(queue.get(0));//唤醒第一个等待线程   
  35.                 }   
  36.                 return true;   
  37.             }   
  38.             return false;   
  39.         } else {   
  40.             return false;   
  41.         }   
  42.     }   
  43. }  
package lock.test;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;

/**
 * 简单乐观独占锁
 */
public class OptimisticExclusiveLock {

    /**
     * 独占锁标记 true 锁不可用 false 锁可用
     */
    private AtomicBoolean state = new AtomicBoolean(false);
    List<Thread>          queue = new ArrayList<Thread>();//阻塞队列

    public boolean lock() {
        if (!state.get()&&state.compareAndSet(false, true)) {//取锁成功不会阻塞,程序会继续执行
            return true; // 利用CAS
        } else {
            queue.add(Thread.currentThread());//加入阻塞队列
            LockSupport.park();//阻塞线程
            return false;
        }
    }

    public boolean unLock() {
        if (state.get()) {
            queue.remove(Thread.currentThread());//从队列里移除
            if (state.compareAndSet(true, false)) {// 利用CAS
                if(!queue.isEmpty()){
                    LockSupport.unpark(queue.get(0));//唤醒第一个等待线程
                }
                return true;
            }
            return false;
        } else {
            return false;
        }
    }
}

 

简单乐观独占锁测试

 

Java代码
 
《【java并发】基于JUC CAS原理,自己实现简单独占锁》

  1. package lock.test;   
  2.   
  3. /**  
  4.  * @author Administrator 独占锁测试  
  5.  */  
  6. public class OptimisticExclusiveLockTest {   
  7.   
  8.     public static OptimisticExclusiveLock lock = new OptimisticExclusiveLock(); // 独占锁   
  9.     public static volatile int            i    = 0;                            // 保证可见性   
  10.   
  11.     public class Task implements Runnable {   
  12.   
  13.         @Override  
  14.         public void run() {   
  15.             while (true) {   
  16.                 try {   
  17.                     lock.lock();//加锁   
  18.                     i += 2;   
  19.                     System.out.println(“thread name:” + Thread.currentThread().getName() + ” i=” + i);   
  20.                 } finally {   
  21.                     lock.unLock();//释放锁   
  22.                     try {   
  23.                         Thread.currentThread().sleep(10);   
  24.                     } catch (InterruptedException e) {   
  25.                         // TODO Auto-generated catch block   
  26.                         e.printStackTrace();   
  27.                     }   
  28.                 }   
  29.             }   
  30.         }   
  31.     }   
  32.   
  33.     public void runTask() {   
  34.         for (int i = 0; i < 100; i++) {   
  35.             Thread t = new Thread(new Task(), “thread” + i);   
  36.             t.start();   
  37.         }   
  38.     }   
  39.   
  40.     public static void main(String[] args) {   
  41.         OptimisticExclusiveLockTest test = new OptimisticExclusiveLockTest();   
  42.         test.runTask();   
  43.   
  44.     }   
  45. }  
package lock.test;

/**
 * @author Administrator 独占锁测试
 */
public class OptimisticExclusiveLockTest {

    public static OptimisticExclusiveLock lock = new OptimisticExclusiveLock(); // 独占锁
    public static volatile int            i    = 0;                            // 保证可见性

    public class Task implements Runnable {

        @Override
        public void run() {
            while (true) {
                try {
                    lock.lock();//加锁
                    i += 2;
                    System.out.println("thread name:" + Thread.currentThread().getName() + " i=" + i);
                } finally {
                    lock.unLock();//释放锁
                    try {
                        Thread.currentThread().sleep(10);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public void runTask() {
        for (int i = 0; i < 100; i++) {
            Thread t = new Thread(new Task(), "thread" + i);
            t.start();
        }
    }

    public static void main(String[] args) {
        OptimisticExclusiveLockTest test = new OptimisticExclusiveLockTest();
        test.runTask();

    }
}

 

 这里实现的简单乐观独占锁很简单,但是能保证并发性。

 JUC里面基于CAS实现了很多的锁,主要是基于AQS实现,如ReentrantLock,CountDownLatch,Semaphore,FutureTask等,适用于不同的锁场景。

转自:
http://singleant.iteye.com/blog/1405478

    原文作者:java锁
    原文地址: https://blog.csdn.net/jeff06143132/article/details/10055899
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞