zoukankan      html  css  js  c++  java
  • Java并发程序设计(七)乐天派:无锁

    无锁

    一、概述

    无锁是处理并发的一种乐观策略,它会假设对资源的访问是没有冲突的。既然没有冲突自然不需要等待,所以所有的线程都可以在不停顿的状态下执行。那遇到冲突怎么办?接下来请看,无锁绝招“CAS”即比较交换术。

    二、CAS原理

    CAS即Compare and swap.其算法过程是这样的:它有三个参数:

    1.V表示要更新的变量

    2.E表示期望值

    3.N表示新值

    仅当V等于E时,才会将V设为N。如果V和N不同,则说明有其他线程做了更新,则该线程什么都不做。当多个线程同时使用CAS进行变量操作时,只有一个会更新成功,其余都会失败。失败的线程不会被挂起,而是进行重试。

    三、无锁的线程安全整数:AtomicInteger

    AtomicInteger主要方法如下(对于其他无锁线程安全类,其方法类似):

    public final int get() //取得当前值
    public final void set(int newValue) //设置当前值
    public final int getAndset(int newValue) //设置新值返回旧值
    public final boolean compareAndSet(int except,int u) //如果当前值为except则设为u
    public final int getAndIncrement() //当前值加1返回旧值
    public final int getAndDecrement() 
    public final int getAndAdd(int delta)
    public final int incrementAndGet()
    public final int decrementAndGet()
    public final int addAndGet() 

    就AtomicInteger核心字段:

    private volatile int value; //代表AtomicInteger当前的值
    private static final long valueOffset; //value字段的偏移量

    AtomicInteger的使用示例:

    public class AutomicIntegerDemo {
    
        static AtomicInteger i=new AtomicInteger();
    
        public static class addThread implements Runnable{
    
            @Override
            public void run() {
                for(int j=0;j<1000;j++){
                    i.incrementAndGet();
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            Thread[] ts=new Thread[10];
            for(int j=0;j<10;j++){
                ts[j]=new Thread(new addThread());
            }
    
            for(int j=0;j<10;j++) ts[j].start();
            for (int j=0;j<10;j++) ts[j].join();
    
            System.out.println(i);
        }
    }

    相比使用锁,使用无锁会有更好的性能。

    四、CAS算法逻辑上瑕疵及解决办法:AtomicStampedReference

    CAS算法逻辑上的瑕疵:当你获得对象当前数据后,在准备修改为新值前,对象的值被其他对象连续修改了两次,而经过这两次修改后,对象的值又恢复到旧值。这样,当前线程就无法判断该值是否被修改过。也就是说你修改的对象数值没有过程状态信息。

    AtomicStampedReference内部不仅维护了对象值还维护了一个状态值。

    五、无锁数组

    当前可用的原子数组有:AtomicIntegerArray,AtomicLongArray和AtomicReferenceArray

    public class AtomicIntegerArrayDemo {
    
        static AtomicIntegerArray array=new AtomicIntegerArray(10);
    
        public static class addThread implements Runnable{
    
            @Override
            public void run() {
                for (int j=0;j<1000;j++){
                    array.getAndIncrement(j%array.length());
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            Thread[] ts=new Thread[10];
            for (int j=0;j<10;j++){
                ts[j]=new Thread(new addThread());
            }
    
            for (int j=0;j<10;j++) ts[j].start();
    
            for (int j=0;j<10;j++) ts[j].join();
    
            System.out.println(array);
        }
    }

    六、让普通变量也享受原子操作:AtomicIntegerFieldUpdater

    public class AtomicIntegerFieldUpdaterDemon {
    
        public static class Candidate{
            int id;
            volatile int score;
        }
    
        public final static AtomicIntegerFieldUpdater<Candidate> scoreUpdater=
                AtomicIntegerFieldUpdater.newUpdater(Candidate.class,"score");
    
        public static AtomicInteger checkScore=new AtomicInteger(0); //用于检测
    
        public static void main(String[] args) throws InterruptedException {
            final Candidate candidate=new Candidate();
            Thread[] threads=new Thread[1000];
            for (int i=0;i<1000;i++){
                threads[i]=new Thread(){
                    public void run(){
                        if (Math.random()>0.4){
                            scoreUpdater.incrementAndGet(candidate);
                            checkScore.incrementAndGet();
                        }
                    }
                };
                threads[i].start();
            }
    
            for (int i=0;i<1000;i++) threads[i].join();
            System.out.println("score="+candidate.score);
            System.out.println("checkScore="+checkScore);
        }
    }

    注意事项:

    1.Updater只能修改可见范围内的变量。

    2.变量必须是volatile

    3.变量不能是static的

    Simple is important!
  • 相关阅读:
    Visual Source Safe的使用方法
    二十道经典C#面试题
    C#中转义符
    在自己迷惑的时候,提醒自己
    C#中多态
    C#重载和重写
    DataTable定义
    C#怎么调用方法
    mysql数据类型——字符串char(m)和varchar(m)
    mysql数据类型——时间类型
  • 原文地址:https://www.cnblogs.com/Shadowplay/p/7484474.html
Copyright © 2011-2022 走看看