实现多线程原子性操作 - 极悦
首页 课程 师资 教程 报名

实现多线程原子性操作

  • 2020-11-06 17:44:58
  • 1169次 极悦

原子性操作,即为最小的操作单元,比如i=1,就是一个原子性操作,这个过程只涉及一个赋值操作。多线程原子性操作依赖在J.U.C包下的atomic系列的类。它主要包括四类:基本类型,数组类型,属性原子修改器类型,引用类型。


1.基本类型的实现:

package concurrent;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicTest {

private static final AtomicInteger at=new AtomicInteger();

private static final ExecutorService es=Executors.newFixedThreadPool(20);

public static void main(String[] args) {

// TODO Auto-generated method stub

long start=System.currentTimeMillis();

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

{

Thread t1=new Thread() {

public void run()

{

System.out.println(Thread.currentThread().getName()+"实现了一次自增原子操作,结果为:"+at.incrementAndGet());

}

};

es.execute(t1);

}

try

{

Thread.sleep(5000);

}

catch(InterruptedException e)

{

e.printStackTrace();

}

System.out.println("计算过程的耗时为:"+(System.currentTimeMillis()-start-5000));

System.out.println("累加1000次,得到结果"+at);

}

}

运行结果如下:

pool-1-thread-13实现了一次自增原子操作,结果为:984

pool-1-thread-8实现了一次自增原子操作,结果为:983

pool-1-thread-14实现了一次自增原子操作,结果为:982

pool-1-thread-20实现了一次自增原子操作,结果为:981

pool-1-thread-10实现了一次自增原子操作,结果为:980

pool-1-thread-12实现了一次自增原子操作,结果为:979

pool-1-thread-3实现了一次自增原子操作,结果为:978

pool-1-thread-7实现了一次自增原子操作,结果为:977

pool-1-thread-4实现了一次自增原子操作,结果为:976

pool-1-thread-18实现了一次自增原子操作,结果为:1000

pool-1-thread-9实现了一次自增原子操作,结果为:999

pool-1-thread-17实现了一次自增原子操作,结果为:998

pool-1-thread-6实现了一次自增原子操作,结果为:997

pool-1-thread-5实现了一次自增原子操作,结果为:996

pool-1-thread-2实现了一次自增原子操作,结果为:995

计算过程的耗时为:9

累加1000次,得到结果1000

由上面可知使用基本类型的原子操作类进行数字的自增,不仅可以保证操作操作的原子性,而且相对来说花费的时间代价比使用synchronized加锁的时间代价要小。


2.数组类型

以下以AtomicIntegerArray为例通过对一个数组内的每个元素进行自增计算,从而来介绍数组类型的原子类的运用。

package concurrent;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.atomic.AtomicIntegerArray;

public class AtomicTest {

private static final AtomicIntegerArray at=new AtomicIntegerArray(new int[5]);

private static final ExecutorService es=Executors.newFixedThreadPool(20);

public static void main(String[] args) {

// TODO Auto-generated method stub

long start=System.currentTimeMillis();

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

{

Thread t1=new Thread() {

public void run()

{

for(int i=0;i<5;i++)

System.out.println(Thread.currentThread().getName()+"实现了对第"+(i+1)+"个元素一次自增原子操作,结果为:"+at.incrementAndGet(i));

}

};

es.execute(t1);

}

try

{

Thread.sleep(5000);

}

catch(InterruptedException e)

{

e.printStackTrace();

}

System.out.println("计算过程的耗时为:"+(System.currentTimeMillis()-start-5000));

for(int i=0;i<5;i++)

System.out.println("第"+(i+1)+"个元素累加1000次,得到结果"+at.get(i));

}

}

运行程序结果如下,其中运算过程部分给出:

pool-1-thread-3实现了对第5个元素一次自增原子操作,结果为:980

pool-1-thread-20实现了对第1个元素一次自增原子操作,结果为:989

pool-1-thread-20实现了对第2个元素一次自增原子操作,结果为:1000

pool-1-thread-20实现了对第3个元素一次自增原子操作,结果为:1000

pool-1-thread-20实现了对第4个元素一次自增原子操作,结果为:1000

pool-1-thread-20实现了对第5个元素一次自增原子操作,结果为:1000

pool-1-thread-15实现了对第5个元素一次自增原子操作,结果为:998

计算过程的耗时为:9

第1个元素累加1000次,得到结果1000

第2个元素累加1000次,得到结果1000

第3个元素累加1000次,得到结果1000

第4个元素累加1000次,得到结果1000

第5个元素累加1000次,得到结果1000

从结果可以看出,数组类型的原子类的使用,可以保证每个元素的自增等操作都满足原子性。


3.属性原子修改器

以AtomicIntegerFieldUpdater类为例,该类有一个静态方法newUpdater(Class tclass,String fieldName),则这个表示为tclass类的fieldName属性创建一个属性原子修改器,如果需要修改该属性,则只需要调用原子修改器的方法,比如addAndGet(tclass obj,int delta):该方法表示将该修改器对应的属性增加delta。以下通过代码来了解属性原子修改器的作用。

package concurrent;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

class Count{

public volatile int number;

}

public class AtomicTest {

private static final AtomicIntegerFieldUpdateraifu=AtomicIntegerFieldUpdater.newUpdater(Count.class, "number");

private static final ExecutorService es=Executors.newFixedThreadPool(20);

public static void main(String[] args) {

// TODO Auto-generated method stub

final Count count=new Count();

long start=System.currentTimeMillis();

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

{

Thread t1=new Thread() {

public void run()

{

System.out.println(Thread.currentThread().getName()+"实现了一次自增原子操作,结果为:"+aifu.addAndGet(count, 1));

}

};

es.execute(t1);

}

try

{

Thread.sleep(5000);

}

catch(InterruptedException e)

{

e.printStackTrace();

}

System.out.println("计算过程的耗时为:"+(System.currentTimeMillis()-start-5000));

for(int i=0;i<5;i++)

System.out.println("第"+(i+1)+"个元素累加1000次,得到结果"+count.number);

}

}

运行以上程序,得到如下结果:

pool-1-thread-17实现了一次自增原子操作,结果为:993

pool-1-thread-5实现了一次自增原子操作,结果为:992

pool-1-thread-19实现了一次自增原子操作,结果为:991

pool-1-thread-3实现了一次自增原子操作,结果为:990

pool-1-thread-15实现了一次自增原子操作,结果为:989

pool-1-thread-20实现了一次自增原子操作,结果为:988

pool-1-thread-18实现了一次自增原子操作,结果为:987

pool-1-thread-6实现了一次自增原子操作,结果为:986

pool-1-thread-7实现了一次自增原子操作,结果为:985

pool-1-thread-9实现了一次自增原子操作,结果为:984

计算过程的耗时为:10

第1个元素累加1000次,得到结果1000

第2个元素累加1000次,得到结果1000

第3个元素累加1000次,得到结果1000

第4个元素累加1000次,得到结果1000

第5个元素累加1000次,得到结果1000

从上面结果可以看出,属性原子修改器的使用也能达到原子性操作的目的。


4.引用类型

以AtomicReference类为例,通过AtomicReferencear=new AtomicReference<>();ar可以调用set()方法设置初始值,调用compareAndSet(expect,update):这个方法就是将存在ar中的值与expect值作比较,如果两者相等,则更新该值为update;代码如下:

package concurrent;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.atomic.AtomicReference;

class Count{

public int count;

public Count(int count)

{

this.count=count;

}

public String toString()

{

return "这个对象的位置是:"+count;

}

}

public class AtomicTest {

private static final AtomicReferencear=new AtomicReference();

public static void main(String[] args) {

// TODO Auto-generated method stub

Count count=new Count(1001);

long start=System.currentTimeMillis();

ar.set(count);

System.out.println("你好,"+ar.get());

Count count1=new Count(1002);

ar.compareAndSet(count, count1);//内存值与count是一样的,所以值更新为count1

System.out.println("我发生了改变:"+ar.get());

Count count2=new Count(1003);

ar.compareAndSet(count, count2);//此时内存智为count1,与count不一致,所以无法更新,因此内存值依然为count1

System.out.println("我发生了改变:"+ar.get());

}

}

运行程序,结果如下:

你好,这个对象的位置是:1001

我发生了改变:这个对象的位置是:1002

我发生了改变:这个对象的位置是:1002


以上就是Java多线程原子性操作的实现,总而言之,就是依赖atomic系列类来实现的。基本类型的类主要包括AtomicInteger、AtomicLong、AtomicBoolean等;数组类型主要包括AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray;属性原子修改器类型主要包括AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater ;引用类型主要包括AtomicReference、AtomicStampedRerence、AtomicMarkableReference。对于这些类的具体讲解可以参照本站的Java多线程教程加以学习。


选你想看

你适合学Java吗?4大专业测评方法

代码逻辑 吸收能力 技术学习能力 综合素质

先测评确定适合在学习

在线申请免费测试名额
价值1998元实验班免费学
姓名
手机
提交