如果你学过操作系统,我想你肯定对原子操作不会陌生,著名的哲学家就餐问题大家估计也不会陌生。如果现在有一个多线程的程序,有一个公共的访问变量,那么想正确的控制这个公共变量的值,每个学过计算机的人的处理方式肯定是使用一些互斥量,或者锁机制,或者信号量的方法保证对公共变量的访问时原子的。
当然自己实现这些操作也是不难的,但是现在java提供了一种更加简单搞笑的实现方式。
java.util.concurrent.atomic
类 AtomicLong
java.lang.Object java.lang.Number java.util.concurrent.atomic.AtomicLong这个类 AtomicLong是一个原子类,程序中对这种高变量的操作都是原子操作。
可以用原子方式更新的 long
值。有关原子变量属性的描述,请参阅 java.util.concurrent.atomic
包规范。AtomicLong
可用在应用程序中(如以原子方式增加的序列号),并且不能用于替换 Long
。但是,此类确实扩展了 Number
,允许那些处理基于数字类的工具和实用工具进行统一访问。有关这个类的一个比较详细的介绍,有一个网址提供了下面的介绍。http://blog.csdn.net/jationxiaozi/article/details/6322151
下面我将介绍一个应用场景,可以很好地使用原子类AtomicLong来实现在多线程中正确无误的进行信息统计。
假设有一个应用,有多个线程,他们的工作是一致的,例如他们都是在生成产品,他们都能看见一个全局的变量count,这个变量时用来统计到现在为止所有的人一共生成了多少产品,显然为了能够保证准确性,我们必须保证所有的线程在更新count的时候其它线程不去访问count,而AtomicLong类型的变量正好符合这个要求。现在我们的主管人员会定期的去访问这个变量统计一下从开始工作到现在为止工厂的生成产品的平均速度。
为了满足如下的要求我写了一个简单的多线程中的全局信息统计的一种简单实现方式。
WorkThread,生产者的实现方式如下,
package com.jack.qiu.thread;
import java.util.concurrent.atomic.AtomicLong;
public class WorkThread extends Thread{
/**
* 将AtomicLong设定为静态的相当于公共变量
*/
public static AtomicLong count = new AtomicLong(0);
public static long timeBegin = System.currentTimeMillis();
//private String name;
//private int x;
//private Lock lock;
public WorkThread(String name)
{
this.setName(name);
//this.x = x;
//this.lock = lock;
}
/**
* 模拟生产过程
*/
private void doSomeThing()
{
long millSec = (long) (Math.random()*1000);
try {
Thread.sleep(millSec);//模拟生产过程
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return;
}
public void run()
{
while(true)
{
//here
doSomeThing();
/**
* 原子操作
*/
count.addAndGet(1);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
下面的线程模拟主管人员的工作,他会每10秒就获取当前的生产总量,然后计算生产速度
package com.jack.qiu.thread;
public class Statics extends Thread{
/**
* 设定7个工作人员
*/
WorkThread[] threads = new WorkThread[7];
private static String div(long begin,long now,long count)
{
double gap = (now-begin)/1000.0;
return String.format("%1.2f",count/gap);
}
public void run()
{
//Lock lock = new ReentrantLock(false);
for(int i=0;i<threads.length;i++)
{
String threadName = "thread"+i;
threads[i] = new WorkThread(threadName);
}
for(int i=0;i<threads.length;i++)
{
threads[i].start();
}
try
{
while(true)
{
Thread.sleep(10000);
System.out.println("--------jack statics-------");
/**
* 管理者获取当前的生产总数,为了保证所有的操作都是原子的使用了如下的操作
*/
long ccount = WorkThread.count.addAndGet(0);
System.out.println("from begin to now we have finished "+ ccount+" items");
System.out.println("the rate is "+div(WorkThread.timeBegin,System.currentTimeMillis(),ccount)+" per second\n\n");
}
}catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return;
}
}
最后面的启动模拟程序的main函数如下:
package com.jack.qiu.test;
import com.jack.qiu.thread.Statics;
public class Main {
public static void main(String[] argv)
{
Statics stat = new Statics();
stat.start();
try {
stat.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
好了我们看到了整个程序非常的简单,我们再对count进行统计和计算的时候没有只是用了 AtomicLong类别给我们提供的一个方法,就是如此的简单高效。相信大家也是很期待运行结果吧。
--------jack statics-------
from begin to now we have finished 50 items
the rate is 5.00 per second
--------jack statics-------
from begin to now we have finished 95 items
the rate is 4.74 per second
--------jack statics-------
from begin to now we have finished 144 items
the rate is 4.79 per second