您的位置:首页 > 其它

多线程下的数据安全

2019-04-10 15:20 78 查看

前言

  多线程下的线程安全,主要是由于无法控制线程的执行顺序,无法确定那个线程是先执行,是由CPU确定的,出现线程不安全的情况-》每次运行的结构都不相同(程序无法按照我们想要的结果)

下面是一个简单的多线程例子,来说明下多线程下的数据安全

   

[code]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相关的类

   

直接上代码:

  

[code]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

直接上代码:

   

[code]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方式实现线程同步

[code]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.使用自定义锁,实现线程同步

   手写锁代码

 

[code]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.ato
1fff8
mic.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;
}
}

测试代码

 

[code]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);
}

}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: