首页 > 编程 > Java > 正文

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

2019-11-08 02:07:32
字体:
来源:转载
供稿:网友

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关键字要高效的多,尽量使用原子类。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表