如果你學過操作系統,我想你肯定對原子操作不會陌生,著名的哲學家就餐問題大家估計也不會陌生。如果現在有一個多線程的程序,有一個公共的訪問變量,那麼想正確的控制這個公共變量的值,每個學過計算機的人的處理方式肯定是使用一些互斥量,或者鎖機制,或者信號量的方法保證對公共變量的訪問時原子的。
當然自己實現這些操作也是不難的,但是現在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