多線程下的數據安全

前言

  多線程下的線程安全,主要是由於無法控制線程的執行順序,無法確定那個線程是先執行,是由CPU確定的,出現線程不安全的情況-》每次運行的結構都不相同(程序無法按照我們想要的結果)

下面是一個簡單的多線程例子,來說明下多線程下的數據安全

   

package com.el.jichu.thread.writerlock;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Auther: roman.zhang
 * @Date: 2019/4/10 8:44
 * @Version:V1.0
 * @Description:LockDemo1
 */
public class LockDemo1 {
   // private AtomicInteger atomicInteger=new AtomicInteger(0);
    private int i=0;

    public void add(){
        i++;
       //atomicInteger.getAndIncrement();
    }

    public static void main(String[] args) throws InterruptedException {
        LockDemo1 demo1 = new LockDemo1();
        for(int i=0;i<2;i++){
            new Thread(()->{
                for(int j=0;j<10000;j++){
                    demo1.add();
                }
            }).start();
        }

        Thread.sleep(3000);

       // System.out.println("計算結果是:"+demo1.atomicInteger);
        System.out.println("計算結果是:"+demo1.i);
    }

}

每次運行結果:

   第一次:11783

   第二次:12670

上面之所以出現這種情況,是多線程下,無法控制線程間的執行順序,無法確定在執行完一個線程後(即完成一個整體操作後,再去執行另一個線程)。

  解決方式

    1.使用AtomicInterger 下面是AtomicInterger相關的類

   

直接上代碼:

  

package com.el.jichu.thread.writerlock;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Auther: roman.zhang
 * @Date: 2019/4/10 8:44
 * @Version:V1.0
 * @Description:LockDemo1
 */
public class LockDemo1 {
    private AtomicInteger atomicInteger=new AtomicInteger(0);
    //private int i=0;

    public void add(){
        //i++;
       atomicInteger.getAndIncrement();
    }

    public static void main(String[] args) throws InterruptedException {
        LockDemo1 demo1 = new LockDemo1();
        for(int i=0;i<2;i++){
            new Thread(()->{
                for(int j=0;j<10000;j++){
                    demo1.add();
                }
            }).start();
        }

        Thread.sleep(3000);

        System.out.println("計算結果是:"+demo1.atomicInteger);
        //System.out.println("計算結果是:"+demo1.i);
    }

}

使用AtomicInter就能保證每次得到我們想要的結果

2.使用synchronized這個方式,不在介紹,介紹一個功能更多的Lock

直接上代碼:

   

package com.el.jichu.thread.writerlock;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Auther: roman.zhang
 * @Date: 2019/4/10 8:44
 * @Version:V1.0
 * @Description:LockDemo1
 */
public class LockDemo1 {
    //private AtomicInteger atomicInteger=new AtomicInteger(0);
    private int i=0;
    private Lock lock=new ReentrantLock();
    public  void add(){
        lock.lock();
        try {
            i++;
        } finally {
            lock.unlock();
        }
        //i++;
       //atomicInteger.getAndIncrement();
    }

    public static void main(String[] args) throws InterruptedException {
        LockDemo1 demo1 = new LockDemo1();
        for(int i=0;i<2;i++){
            new Thread(()->{
                for(int j=0;j<10000;j++){
                    demo1.add();
                }
            }).start();
        }

        Thread.sleep(3000);

       // System.out.println("計算結果是:"+demo1.atomicInteger);
        System.out.println("計算結果是:"+demo1.i);
    }

}

下面介紹下AtomicInteger的原理(CAS)

   CAS (硬件語言) 即每個線程從內存中拿到值(稱爲當前值),以及想把內存值改爲的目標值,每次去更新內存值時,都會將當前值與內存值比較,如果相同,則把內存值變爲目標值。

使用CAS方式實現線程同步

package com.el.jichu.thread.writerlock;

import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Auther: roman.zhang
 * @Date: 2019/4/10 8:44
 * @Version:V1.0
 * @Description:LockDemo1
 */
public class LockDemo1 {
    //private AtomicInteger atomicInteger=new AtomicInteger(0);
    private int i=0;
    private Lock lock=new ReentrantLock();

    static Unsafe unsafe=null;
    private static  long valueOffset;
    static {
        //反射
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            unsafe= (Unsafe) theUnsafe.get(null);
            //目標:通過unsafe去調用底層硬件原語
            //無法直接操作內存,委屈求全,只能去通過對象中屬性的偏移量,去修改值
            valueOffset = unsafe.objectFieldOffset(LockDemo1.class.getDeclaredField("i"));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    public  void add(){
        ///使用CAS去實現同步
        //爲什麼是個循環:因爲CAS會失敗,因此纔會使用循環
        int current ;
        do{
            current=unsafe.getIntVolatile(this,valueOffset);
        }while(!unsafe.compareAndSwapInt(this,valueOffset,current,current+1));
        /**
         * unsafe.compareAndSwapInt(當前對象,內存值,當前值,新值)
         */
       /* lock.lock();
        try {
            i++;
        } finally {
            lock.unlock();
        }*/
        //i++;
       //atomicInteger.getAndIncrement();
    }

    public static void main(String[] args) throws InterruptedException {
        LockDemo1 demo1 = new LockDemo1();
        for(int i=0;i<2;i++){
            new Thread(()->{
                for(int j=0;j<10000;j++){
                    demo1.add();
                }
            }).start();
        }

        Thread.sleep(3000);

       // System.out.println("計算結果是:"+demo1.atomicInteger);
        System.out.println("計算結果是:"+demo1.i);
    }

}

  AtomicXXX源碼:

  

 

3.使用自定義鎖,實現線程同步

   手寫鎖代碼

 

package com.el.jichu.thread.writerlock;

import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;

/**
 * @Auther: roman.zhang
 * @Date: 2019/4/10 10:32
 * @Version:V1.0
 * @Description:CustomLock
 *    思路
 *       1.沒有獲取鎖的線程,如何讓線程掛起,不在往下執行,等待其他線程釋放鎖。
 *       2.釋放鎖之後,如何通知其他線程去獲取鎖
 */
public class CustomLock implements Lock {
    //鎖的擁有者
    AtomicReference<Thread> owner=new AtomicReference<>();

    //一個容器存儲等待的線程
    ConcurrentHashMap<Thread,Object> queue=new ConcurrentHashMap<>();

    @Override
    public void lock() {
       while( !owner.compareAndSet(null,Thread.currentThread())){
           //沒有獲取成功,則將該線程停下來
           queue.put(Thread.currentThread(),"");
           //正在運行的線程進入停車場
           LockSupport.park();
           queue.remove(Thread.currentThread());
       }

    }
    @Override
    public void unlock() {
          while(owner.compareAndSet(Thread.currentThread(),null)){//釋放鎖
              //通知其他線程去park線程,繼續去強鎖
            //  Thread next=null;
              ConcurrentHashMap.KeySetView<Thread, Object> threads = queue.keySet();
              for(Thread t:threads){
                  LockSupport.unpark(t);
              }
          }
    }



    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock() {
        return false;
    }

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



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

測試代碼

 

package com.el.jichu.thread.writerlock;

import java.util.concurrent.locks.Lock;

/**
 * @Auther: roman.zhang
 * @Date: 2019/4/10 15:15
 * @Version:V1.0
 * @Description:LockDomo2
 */
public class LockDomo2 {
    private  int i;
    //private AtomicInteger atomicInteger=new AtomicInteger(0);
    private Lock customLock=new CustomLock();
    public  void add(){
        customLock.lock();
        try {
            i++;

        } finally {
            customLock.unlock();
        }
        //atomicInteger.getAndIncrement();
    }

    public static void main(String[] args) throws InterruptedException {
        LockDomo2 demo2 = new LockDomo2();
        for(int i=0;i<2;i++){
            new Thread(()->{
                for(int j=0;j<10000;j++){
                    demo2.add();
                }
            }).start();
        }

        Thread.sleep(3000);

        // System.out.println("計算結果是:"+demo1.atomicInteger);
        System.out.println("計算結果是:"+demo2.i);
    }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章