• Java并发基础10:原子性操作类的使用


    在 java5 以后,我们接触到了线程原子性操作,也就是在修改时我们只需要保证它的那个瞬间是安全的即可,经过相应的包装后可以再处理对象的并发修改,本文总结一下Atomic系列的类的使用方法,其中包含:

    类型 Integer Long
    基本类型 AtomicInteger AtomicLong AtomicBoolean
    数组类型 AtomicIntegerArray AtomicLongArray AtomicReferenceArray
    属性原子修改器 AtomicIntegerFieldUpdater AtomicLongFieldUpdater AtomicReferenceFieldUpdater

    1. 基本类型的使用

    首先看一下AtomicInteger的使用,AtomicInteger主要是针对整数的修改的,看一下示例代码:

    public class AtomicIntegerDemo {
    
    	/**
    	 * 常见的方法列表
    	 * @see AtomicInteger#get()             直接返回值
    	 * @see AtomicInteger#getAndAdd(int)    增加指定的数据,返回变化前的数据
    	 * @see AtomicInteger#getAndDecrement() 减少1,返回减少前的数据
    	 * @see AtomicInteger#getAndIncrement() 增加1,返回增加前的数据
    	 * @see AtomicInteger#getAndSet(int)    设置指定的数据,返回设置前的数据
    	 * 
    	 * @see AtomicInteger#addAndGet(int)    增加指定的数据后返回增加后的数据
    	 * @see AtomicInteger#decrementAndGet() 减少1,返回减少后的值
    	 * @see AtomicInteger#incrementAndGet() 增加1,返回增加后的值
    	 * @see AtomicInteger#lazySet(int)      仅仅当get时才会set
    	 * 
    	 * @see AtomicInteger#compareAndSet(int, int) 尝试新增后对比,若增加成功则返回true否则返回false
    	 */
    	public final static AtomicInteger TEST_INTEGER = new AtomicInteger(1);
    	
    	public static void main(String []args) {
    
    		 for(int i = 0 ; i < 10 ; i++) { //开启10个线程
    
    			 new Thread() {
    				 public void run() {
    					 try {
    						Thread.sleep(1000);
    					} catch (InterruptedException e) {
    						e.printStackTrace();
    					}
    					int now = TEST_INTEGER.incrementAndGet(); //自增
    					System.out.println(Thread.currentThread().getName() + " get value : " + now);
    				 }
    			 }.start();
    		 }
    	}
    }
    

    看一下结果:

    Thread-3 get value : 4
    Thread-7 get value : 5
    Thread-9 get value : 9
    Thread-4 get value : 6
    Thread-0 get value : 3
    Thread-1 get value : 8
    Thread-5 get value : 11
    Thread-8 get value : 7
    Thread-2 get value : 10
    Thread-6 get value : 2

    可以看出,10个线程之间是线程安全的,并没有冲突。也就是说,我们使用原子性操作类去操作基本类型int就可以解决线程安全问题,一个线程在操作的时候,会对其它线程进行排斥,不用我们手动去使用synchronized实现互斥操作了。AtomicLong和AtomicBoolean类似,就不举例子了。

    2. 数组类型的使用

    下面要开始说Atomic的数组用法,Atomic的数组要求不允许修改长度等,不像集合类那么丰富的操作,不过它可以让数组上每个元素的操作绝对安全的,也就是它细化的力度还是到数组上的元素,做了二次包装,虽然是数组类型的,但是最后还是操作数组中存的数,所以会了上面的基本类型的话,数组类型也很好理解。这里主要看一下AtomicIntegerArray的使用,其它的类似。

    public class AtomicIntegerArrayTest {
    
    	/**
    	 * 常见的方法列表
    	 * @see AtomicIntegerArray#addAndGet(int, int) 执行加法,第一个参数为数组的下标,第二个参数为增加的数量,返回增加后的结果
    	 * @see AtomicIntegerArray#compareAndSet(int, int, int) 对比修改,参数1:数组下标,参数2:原始值,参数3,修改目标值,修改成功返回true否则false
    	 * @see AtomicIntegerArray#decrementAndGet(int) 参数为数组下标,将数组对应数字减少1,返回减少后的数据
    	 * @see AtomicIntegerArray#incrementAndGet(int) 参数为数组下标,将数组对应数字增加1,返回增加后的数据
    	 * 
    	 * @see AtomicIntegerArray#getAndAdd(int, int) 和addAndGet类似,区别是返回值是变化前的数据
    	 * @see AtomicIntegerArray#getAndDecrement(int) 和decrementAndGet类似,区别是返回变化前的数据
    	 * @see AtomicIntegerArray#getAndIncrement(int) 和incrementAndGet类似,区别是返回变化前的数据
    	 * @see AtomicIntegerArray#getAndSet(int, int) 将对应下标的数字设置为指定值,第二个参数为设置的值,返回是变化前的数据
    	 */
    	private final static AtomicIntegerArray ATOMIC_INTEGER_ARRAY = new AtomicIntegerArray(new int[]{1,2,3,4,5,6,7,8,9,10});
    	
    	public static void main(String []args) throws InterruptedException {
    		Thread []threads = new Thread[10];
    		for(int i = 0 ; i < 10 ; i++) {
    			final int index = i;
    			final int threadNum = i;
    			threads[i] = new Thread() {
    				public void run() {
    					int result = ATOMIC_INTEGER_ARRAY.addAndGet(index, index + 1);
    					System.out.println("线程编号为:" + threadNum + " , 对应的原始值为:" + (index + 1) + ",增加后的结果为:" + result);
    				}
    			};
    			threads[i].start();
    		}
    		for(Thread thread : threads) {
    			thread.join();
    		}
    		System.out.println("=========================>
    执行已经完成,结果列表:");
    		for(int i = 0 ; i < ATOMIC_INTEGER_ARRAY.length() ; i++) {
    			System.out.println(ATOMIC_INTEGER_ARRAY.get(i));
    		}
    	}
    }
    

    运行结果是给每个数组元素加上相同的值,它们之间互不影响。

    3. 作为类属性的使用

    当某个数据类型是某个类中的一个属性的时候,然后我们要操作该数据,就需要使用属性原子修改器了,这里还是以Integer为例,即:AtomicIntegerFieldUpdater。示例代码如下:

    public class AtomicIntegerFieldUpdaterTest {  
    	  
        static class A {  
            volatile int intValue = 100;  
        }  
          
        /** 
         * 可以直接访问对应的变量,进行修改和处理 
         * 条件:要在可访问的区域内,如果是private或挎包访问default类型以及非父亲类的protected均无法访问到 
         * 其次访问对象不能是static类型的变量(因为在计算属性的偏移量的时候无法计算),也不能是final类型的变量(因为根本无法修改),必须是普通的成员变量 
         *  
         * 方法(说明上和AtomicInteger几乎一致,唯一的区别是第一个参数需要传入对象的引用) 
         * @see AtomicIntegerFieldUpdater#addAndGet(Object, int) 
         * @see AtomicIntegerFieldUpdater#compareAndSet(Object, int, int) 
         * @see AtomicIntegerFieldUpdater#decrementAndGet(Object) 
         * @see AtomicIntegerFieldUpdater#incrementAndGet(Object) 
         *  
         * @see AtomicIntegerFieldUpdater#getAndAdd(Object, int) 
         * @see AtomicIntegerFieldUpdater#getAndDecrement(Object) 
         * @see AtomicIntegerFieldUpdater#getAndIncrement(Object) 
         * @see AtomicIntegerFieldUpdater#getAndSet(Object, int) 
         */  
        public final static AtomicIntegerFieldUpdater<A> ATOMIC_INTEGER_UPDATER = AtomicIntegerFieldUpdater.newUpdater(A.class, "intValue");  
          
        public static void main(String []args) {  
            final A a = new A();  
            for(int i = 0 ; i < 10 ; i++) {  
    
                new Thread() {  
                    public void run() {  
                        if(ATOMIC_INTEGER_UPDATER.compareAndSet(a, 100, 120)) {  
                            System.out.println(Thread.currentThread().getName() + " 对对应的值做了修改!");  
                        }  
                    }  
                }.start();  
            }  
        }  
    }  
    

    可以看到,这里需要将类和类属性传进去才行,传进去后其实跟前面操作Integer没什么不同了,本质都一样的,运行一下,结果只有一个线程能对其进行修改。

    线程的原子性操作类的使用就简单总结到这,其他的操作类原理都相似,可以参考 JDK 的文档,可以很容易写出相应的测试代码。

    原子性操作类的使用就分享这么多,如有错误之处,欢迎指正,我们一同进步~

  • 相关阅读:
    oracle多个单引号的处理
    oracle 存储过程 动态sql语句
    Python内置方法的时间复杂度
    链表和数组的区别
    python enumerate用法总结
    Python 特殊语法:filter、map、reduce、lambda
    Pandas中DateFrame修改列名
    机器学习通用框架
    Python文件处理之文件写入方式与写缓存(三)
    转载: scikit-learn学习之回归分析
  • 原文地址:https://www.cnblogs.com/eson15/p/10287670.html
Copyright © 2020-2023  润新知