多线程下的数据安全
发布日期:2021-11-15 14:57:22 浏览次数:23 分类:技术文章

本文共 7849 字,大约阅读时间需要 26 分钟。

前言

  多线程下的线程安全,主要是由于无法控制线程的执行顺序,无法确定那个线程是先执行,是由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
owner=new AtomicReference<>(); //一个容器存储等待的线程 ConcurrentHashMap
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
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);    }}

 

转载地址:https://blog.csdn.net/qq_39736103/article/details/89173351 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:为什么阿里巴巴禁止在 foreach 循环里进行元素的 remove/add 操作
下一篇:微服务架构-Hystrix

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2024年04月05日 04时36分37秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章