Java高并发-无锁

无锁类的原理详解

CAS

CAS算法的过程是这样:它包含3个参数CAS(V,E,N)。V表示要更新的量,E表示预期值,N表示新值。仅当V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。最后,CAS返回当前V的真实值。CAS操作是抱着乐观的态度进行的,它总是认为自己可以成功完成操作。当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败。失败的线程不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。基于这样的原理,CAS操作即使没有锁,也可以发现其他线程对当前线程的干扰,并进行恰当的处理。

CAS操作是原子性的,由一条CPU指令完成

CPU指令

无锁类的使用

AtomicInteger 继承Number类

概述

主要接口

public final int get()                                                //取得当前值

public final void set(int newValue)                               //设置当前值

public final int getAndSet(int newValue)                    //设置新值,并返回旧值

public final boolean compareAndSet(int expect, int u) //如果当前值为expect,则设置为u

public final int getAndIncrement()                              //当前值加1,返回旧值

public final int getAndDecrement()                             //当前值减1,返回旧值

public final int getAndAdd(int delta)                           //当前值增加delta,返回旧值

public final int incrementAndGet()                              //当前值加1,返回新值

public final int decrementAndGet()                             //当前值减1,返回新值

public final int addAndGet(int delta)                           //当前值加delta,返回新值

主要接口的实现

Unsafe

概述

非安全的操作,比如:

根据偏移量设置值

park()

底层的CAS操作

非公开API,在不同版本的JDK中,可能有较大差异

主要接口

//获得给定对象偏移量上的int值

public native int getInt(Object o, long offset);

//设置给定对象偏移量上的int值

public native void putInt(Object o, long offset, int x);

//获得字段在对象中的偏移量

public native long objectFieldOffset(Field f);

//设置给定对象的int值,使用volatile语义

public native putIntVolatile(Object o,long offset, int x);

//获得给定对象对象的int值,使用volatile语义

public native int getIntVolatile(Object o,long offset);

//和putIntVolatile()一样,但是它要求被操作字段就是volatile类型的

public native void putOrderedInt(Object o, long offset, int x);

AtomicReference

概述

对引用进行修改

是一个模板类,抽象化了数据类型

主要接口

public class AtomicIntegerDemo {

         static AtomicInteger i = AtomicInteger();

         public static class AddThread implements Runnable{

                   public void run(){

                            for(int k = 0; k < 10000; k++)

                                     i.incrementAndGet();

                   }

         }

         public static void main() throws InterruptedException {

                   Thread[] ts = new Thread[10];

                   for(int k = 0; k < 10; k++){

                            ts[k] = new Thread(new AddThread());

                   }

                   for(int k = 0; k < 10; k++){ts[k].start();}

                   for(int k = 0; k < 10; k++){ts[k].join();}

                   System.out.println(i);

         }

}

原子引用保证线程安全

public class AtomicReferenceTest {

         public final static AtomicReference<String> atomicStr = new AtomicReference<String>();

         public static void main(String[] args) {

                   for(int i = 0; i < 10; i++){

                            final int num = i;

                            new Thread() {

                                     public void run() {

                                               try {

                                                        Thread.sleep(Math.abs((int)(Math.random()*100)));

                                               } catch (InterruptedException e) {

                                                        e.printStackTrace();

                                               }

                                               if(atomicStr.compareAndSet("abc","def")){

                                                        System.out.println("Thread:"+Thread.currentThread().getId()+"Change value to def");

                                               } else {

                                                        System.out.println("Thread:"+Thread.currentThread().getId()+"FAIlED");

                                               }

                                     }

                            }.start();

                   }

         }

}

 

AtomicStampedReference

概述

解决ABA问题

主要接口

//比较设置参数依次为:期望值 写入新值 期望时间戳 新时间戳

public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp )

//获得当前对象引用

public  V getReference()

//获得当前时间戳

public int getStamp()

//设置当前对象引用和时间戳

public void set(V newReference, int newStamp)

public class AtomicStampedReferenceDemo {

         static AtomicStampedReference<Integer> money = new AtomicStampedReference<Integer>(19,0);

         public static void main(String[] args) {

                   //模拟多个线程同时更新后台数据库,为用户充值

                   for (int i = 0; i < 3; i++) {

                            final int timestamp = money.getStamp();

                            new Thread() {

                                     public void run(){

                                               while (true) {

                                                        while (true) {

                                                                 Integer m = money.getReference();

                                                                 if (m < 20) {

                                                                           if (money.compareAndSet(m, m+20, timestamp, timestamp + 1))

                                                                                    System.out.println("余额小于20元,充值成功,余额:"

                                                                                                       + money.getReference()+ "元");

                                                                                    break;

                                                                           }

                                                                 } else {

                                                                           //System.out.println("余额大于20元,无需充值");

                                                                           break;

                                                                 }

                                                        }

                                               }

                                     }

                            }.start();

                   }

 

                   // 用户消费线程,模拟消费行为

                   new Thread() {

                            public void run() {

                                     for (int i = 0; i < 100; i++) {

                                               while (true) {

                                                        int timestamp = money.getStamp();

                                                        Integer m = money.getReference();

                                                        if (m > 10) {

                                                                 System.out.println("大于10元");

                                                                 if (money.compareAndSet(m, m - 10, timestamp, timestamp + 1)) {

                                                                           System.out.println("成功消费10元,余额:" + money.getReference());

                                                                           break;

                                                       

                                                                 }

                                                        } else {

                                                                 System.out.println("没有足够的金额");

                                                                 break;

                                                        }

                                               }

                                               try {

                                                        Thread.sleep(100);

                                               } catch (InterruptedException e) {

                                               }       

                                     }

                            }

                    }.start();

         }

}

 

AtomicIntegerArray

概述

支持无锁的数组

主要接口

//获得数组第i个下标的元素

public final int get(int i)

//获得数组的长度

public final int length()

//将数组第i个下标设置为newValue,并返回旧的值

public final int getAndSet(int i, int newValue)

//进行CAS操作,如果第i个下标的元素等于expect,则设置为update,设置成功返回true

public final boolean compareAndSet(int i,int expect,int update)

//将第i个下标的元素加1

public final int getAndIncrement(int i)

//将第i个下标的元素减1

public final int getAndDecrement(int i)

//将第i个下标的元素增加delta(delta可以是负数)

public final int getAndAdd(int i,int delta)

 

public class AtomicIntegerArrayDemo {

         static AtomicIntegerArray arr = new AtomicIntegerArray(10); //10个线程安全的数据

         public static class AddThread implements Runnable{

                   public void run(){

                            for(int k = 0; k < 100000; k++)

                                     //我们开了10个线程,对数组中每一个下标所在元素进行加1操作

 

                                     arr.getAndIncrement(k%arr.length());             }

         }

         public static void main(String[] args) throws InterruptedException {

                   Thread[] ts = new Thread[10];

                   for(int k = 0; k < 10; k++){

                            ts[k] = new Thread(new AddThread());

                   }

                   //开了10个线程

                   for(int k = 0; k < 10; k++){ts[k].start();}

                   for(int k = 0; k < 10; k++){ts[k].join();}

                   System.out.println(arr);

         }

}

AutomicIntegerFieldUpdater

概述

让普通变量也享受原子操作

主要接口

AutomicIntegerFieldUpdater.newUpdater()//新建一个更新器

incrementAndGet()//对数据进行更新

小说明

1.Updater只能修改它可见范围内的变量。因为Updater使用反射得到这个变量。如果变量不可见,就会出错。比如如果score申明为private,就是不可行的。

2.为了确保变量被正确的读取,它必须是volatile类型的,如果我们原有代码中未申明这个类型,那么简单的申明一下就行,这不会引起什么问题

3.由于CAS操作会通过对象实例中的偏移量直接进行赋值,因此,它不支持static字段(Unsafe,objectFieldOffset偏移量)

public class AtomicIntegerFieldUpdaterDemo {

         public static class Cadidate{

         int id;

         volatile int score;

         }

         public final static AtomicIntegerFieldUpdater<Cadidate> scoreUpdater

                   = AtomicIntegerFieldUpdater.newUpdater(Cadidate.class, "score");

         //检查Updater是否工作正确

         public static AtomicInteger allScore = new AtomicInteger(0);

         public static void main(String[] args) throws InterruptedException {

                   final Cadidate stu = new Candidate();

                   Thread[] t = new Thread[1000];

                   for (int i = 0 ; i < 1000 ; i++){

                            t[i] = new Thread(){

                                     public void run(){

                                              if (Math.random()>0.4){

                                                        scoreUpdater.incrementAndGet(stu);

                                                        allScore.incrementAndGet();

                                               }

                                     }

                            };

                            t[i].start();

                   }

                   for (int i = 0 ; i < 10000 ; i++) { t[i].join();}

                   System.out.println("score="+stu.score);

                   System.out.println("allScore="+allScore);

         }

}

Console:

score=5994

allScore=5994

有锁Vector

无锁的Vector实现

LockFreeVector

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值