多執行緒中的全域性資訊統計的一種簡單實現方式(java實現)
如果你學過作業系統,我想你肯定對原子操作不會陌生,著名的哲學家就餐問題大家估計也不會陌生。如果現在有一個多執行緒的程式,有一個公共的訪問變數,那麼想正確的控制這個公共變數的值,每個學過計算機的人的處理方式肯定是使用一些互斥量,或者鎖機制,或者訊號量的方法保證對公共變數的訪問時原子的。
當然自己實現這些操作也是不難的,但是現在java提供了一種更加簡單搞笑的實現方式。
java.util.concurrent.atomic
類 AtomicLong
這個類AtomicLong是一個原子類,程式中對這種高變數的操作都是原子操作。
可以用原子方式更新的 long
值。有關原子變數屬性的描述,請參閱 java.util.concurrent.atomic
包規範。AtomicLong
可用在應用程式中(如以原子方式增加的序列號),並且不能用於替換 Long
。但是,此類確實擴充套件了 Number
下面我將介紹一個應用場景,可以很好地使用原子類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