【java总结】多线程进阶篇之atomic包

在java语言中,类似i++这种操作并不是原子性的。它并非线程安全的语句,在实际使用中,我们经常需要使用synchronized语句来保证数据的正确。现如今,大多数处理器都包含原子性指令,常见的指令是CAS(compare and set)TAS(test and set),是一种加锁的原子操作指令。


CAS 操作

包含三个操作数 ——内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。CAS执行结果要么成功要么失败,对于失败的情形下一班采用不断重试。或者放弃。

java.util.concurrent.atomic包中,所有类都使用了原子变量,它并不用同步来实现,因此大大提高了效率。


atomic包含有的类如下:

AtomicBoolean 可以用原子方式更新的 boolean 值。   

AtomicInteger 可以用原子方式更新的 int 值。   

AtomicIntegerArray 可以用原子方式更新其元素的 int 数组。   

AtomicIntegerFieldUpdater<T> 基于反射的实用工具,可以对指定类的指定 volatile int 字段进行原子更新。   

AtomicLong 可以用原子方式更新的 long 值。   

AtomicLongArray 可以用原子方式更新其元素的 long 数组。   

AtomicLongFieldUpdater<T> 基于反射的实用工具,可以对指定类的指定 volatile long 字段进行原子更新。   

AtomicMarkableReference<V> AtomicMarkableReference 维护带有标记位的对象引用,可以原子方式对其进行更新。   

AtomicReference<V> 可以用原子方式更新的对象引用。   

AtomicReferenceArray<E> 可以用原子方式更新其元素的对象引用数组。   

AtomicReferenceFieldUpdater<T,V> 基于反射的实用工具,可以对指定类的指定 volatile 字段进行原子更新。   

AtomicStampedReference<V> AtomicStampedReference 维护带有整数“标志”的对象引用,可以用原子方式对其进行更新。   

 

public class AtomicBooleanTest {
	/** 
	06.     * 主要方法: 
	07.     * @see AtomicBoolean#compareAndSet(boolean, boolean)  第一个参数为原始值,第二个参数为要修改的新值,若修改成功则返回true,否则返回false 
	08.     * @see AtomicBoolean#getAndSet(boolean)   尝试设置新的boolean值,直到成功为止,返回设置前的数据 
	09.     */  

	public final static AtomicBoolean TEST_BOOLEAN = new AtomicBoolean();  
	public static void main(String[] args){
		for(int i=0;i<10;i++){
			new Thread(){
				public void run(){
					try{
						Thread.sleep(1000);
					}catch(InterruptedException e){
						e.printStackTrace();
					}
					if(TEST_BOOLEAN.compareAndSet(false, true)){
						System.out.println("我是线程"+Thread.currentThread().getName()+"我成功了!");
					}else 
						System.out.println("我是线程"+Thread.currentThread().getName()+"我失败了!");
				}
			}.start();
		}
	}
}


public class AtomicIntegerTest {
	public final static AtomicInteger TEST_INTEGER=new AtomicInteger(1);
		public static void main(String[] args)throws InterruptedException {
			final Thread[] threads=new Thread[10];
			for(int i=0;i<10;i++){
				final int num=i;
				threads[i]=new Thread(){
					public void run(){
						try{
							Thread.sleep(1000);
						}catch(InterruptedException e){
							e.printStackTrace();
						}
						int now=TEST_INTEGER.incrementAndGet();
						System.out.println("我是线程"+num+",我得到了值,增加后的值为:"+now);
					}
				};
				threads[i].start();
			}
			for(int i=0;i<10;i++){
				threads[i].join();
			}
			System.out.println("最终结果:"+TEST_INTEGER.get());
		}
}


public class AtomicIntegerArrayTest {
	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[100];
		for(int i = 0 ; i < 100 ; i++) {
			final int index = i % 10;
			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("=========================>\n执行已经完成,结果列表:");
		for(int i = 0 ; i < ATOMIC_INTEGER_ARRAY.length() ; i++) {
			System.out.println(ATOMIC_INTEGER_ARRAY.get(i));
		}
	}

}



ABA问题:如果一个线程修改V值假设原来是A,先修改成B,再修改回成A。当前线程的CAS操作无法分辨当前V值是否发生过变化。举个例子,你把屋子收拾干净之后,有事出门一趟,这时屋子整齐的状态成为A,在你出门的这段时间,有人把你的屋子弄乱,翻来翻去。这时脏乱的状态成为B,之后,又帮你恢复到原样,当你回家的时候,你看到的是状态A,你以为你的屋子一直存在于状态A,而实际上它经历了ABA这个过程。


public class AtomicReferenceTest {
	public final static AtomicReference <String>ATOMIC_REFERENCE = new AtomicReference<String>("abc"); 
	public static void main(String[] args){
		for (int i=0;i<100;i++){
			final int num=i;
			new Thread(){
				@Override
				public void run(){
					try{
						Thread.sleep((int)(Math.random()*100));
					}catch(InterruptedException e){
						e.printStackTrace();
					}
					if(ATOMIC_REFERENCE.compareAndSet("abc", "abc2")){
						System.out.println("我是线程"+num+"我获得了锁进行了对象修改!");
					}
				}
			}.start();
		}
		new Thread(){
			@Override 
			public void run(){
				while(!ATOMIC_REFERENCE.compareAndSet("abc2", "abc"));
				System.out.println("已经改为原始值!");
			}
		}.start();
	}
}



 

public class AtomicStampedReferenceTest {
	/*
	 * 将abc修改为abc2的线程仅有一个被访问,虽然被修改回了原始值,但
	 * 是其他线程也不会再将abc改为abc2。
	 */
	public final static AtomicStampedReference <String>ATOMIC_REFERENCE = new AtomicStampedReference<String>("abc" , 0);  
	public static void main(String []args) {
		for(int i = 0 ; i < 100 ; i++) {
			final int num = i; 
			final int stamp = ATOMIC_REFERENCE.getStamp();  
			 new Thread() {
				 public void run(){
					 try {
						 Thread.sleep(Math.abs((int)(Math.random() * 100))); 
					 }catch (InterruptedException e){
						 e.printStackTrace(); 
					 }
					 if(ATOMIC_REFERENCE.compareAndSet("abc" , "abc2" , stamp , stamp + 1)){
						 System.out.println("我是线程:" + num + ",我获得了锁进行了对象修改!");  

					 }
				 }
			 }.start();
		}
		new Thread(){
			public void run(){
				int stamp=ATOMIC_REFERENCE.getStamp();
				while(!ATOMIC_REFERENCE.compareAndSet("abc2", "abc" , stamp , stamp + 1));  
				System.out.println("已经改回为原始值!");  

			}
		}.start();
	}
}


  总结:使用atomic包下的各种类来处理线程线程之间的竞争要比使用synchronized关键字要高效的多,尽量使用原子类。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值