1. 程式人生 > >Java執行緒安全的計數器

Java執行緒安全的計數器

   一、多執行緒以及執行緒安全           java執行緒安全就是指控制多個執行緒對某個資源的有序訪問或修改。這涉及兩個很重要的概念:java的記憶體模型和java的執行緒同步機制。       1.java的記憶體模型 要解決兩個主要的問題:可見性和有序性 可見性: 多個執行緒之間是不能互相傳遞資料通訊的,它們之間的溝通只能通過共享變數來進行       2.執行緒同步        由於同一程序內的多個執行緒共享記憶體空間,在Java中,就是共享例項,當多個執行緒試圖同時修改某個例項的內容時,就會造成衝突,因此,執行緒必須實現共享互斥,使多執行緒同步。        最簡單的同步是將一個方法標記為synchronized,對同一個例項來說,任一時刻只能有一個synchronized方法在執行。當一個方法正在執行某個synchronized方法時,其他執行緒如果想要執行這個例項的任意一個synchronized方法,都必須等待當前執行synchronized方法的執行緒退出此方法後,才能依次執行。
       但是,非synchronized方法不受影響,不管當前有沒有執行synchronized方法,非synchronized方法都可以被多個執行緒同時執行。        synchronized和volatile關鍵字的區別:        (1)synchronized關鍵字保證了多個執行緒對於同步塊是互斥的,synchronized作為一種同步手段,解決java多執行緒的執行有序性和記憶體可見性,而volatile關鍵字只解決多執行緒的記憶體可見性問題;        (2)volatile關鍵字是java提供的一種同步手段,只不過它是輕量級。volatile只能保證多執行緒的記憶體可見性,不能保證多執行緒的執行有序性。
而最徹底的同步要保證有序性和可見性,例如synchronized。
       任何被volatile修飾的變數,都不拷貝副本到工作記憶體,任何修改都及時寫在主存。因此對於Valatile修飾的變數的修改,所有執行緒馬上就能看到,但是volatile不能保證對變數的修改是有序的。       3.基本知識 JAVA多執行緒實現方式:繼承Thread類、實現Runnable介面 使用哪一種:http://www.cnblogs.com/rollenholt/archive/2011/08/28/2156357.html 最好是後一種 二、例項程式碼 功能:編寫一個執行緒安全的計數器,5個執行緒同時跑,計數到1000,輸出執行緒名和計數直到1000
程式碼:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

/**
* 功能:執行緒安全的計數器,5個執行緒同時跑,計數到1000,輸出執行緒名和計數
* @author Yolanda
*
*/
public class MySafeThread implements Runnable{

     private static AtomicInteger count = new AtomicInteger(0);//執行緒安全的計數變數
     private int threadCount = 0;//執行緒編號
     private static int num = 1;

     /**
     * 功能:計數
     */
     public static void calc(){
          while((count.get())<1000)
          {
               count.incrementAndGet();//自增1,返回更新值
              System.out.println("正在執行是執行緒" + Thread.currentThread().getName() + ":" + count);         
          }
     }
     
     /**
     * 功能:執行緒執行方法,每次只能一個執行緒訪問
     */
     public synchronized void run() {
          while(true)
          {
               try {
                    Thread.sleep(1);
               } catch (InterruptedException e) {
                    e.printStackTrace();
               }     
               MySafeThread.calc();
          }
     }
     
     public static void main(String[] args) {          
          //建立五個執行緒例項並啟動
          for (int i = 1; i < 6; i++) {     
               Thread mySafeThread = new Thread(new MySafeThread());
               mySafeThread.start();
          }
     }     
}


結果:

正在執行是執行緒Thread-1:2

正在執行是執行緒Thread-4:4

正在執行是執行緒Thread-0:4

正在執行是執行緒Thread-3:2

正在執行是執行緒Thread-3:9

正在執行是執行緒Thread-3:10

正在執行是執行緒Thread-0:8

正在執行是執行緒Thread-4:7

正在執行是執行緒Thread-4:13

正在執行是執行緒Thread-4:14

正在執行是執行緒Thread-4:15

正在執行是執行緒Thread-4:16

正在執行是執行緒Thread-4:17

正在執行是執行緒Thread-4:18

正在執行是執行緒Thread-4:19

正在執行是執行緒Thread-4:20

......

正在執行是執行緒Thread-4:988

正在執行是執行緒Thread-4:989

正在執行是執行緒Thread-4:990

正在執行是執行緒Thread-4:991

正在執行是執行緒Thread-4:992

正在執行是執行緒Thread-4:993

正在執行是執行緒Thread-4:994

正在執行是執行緒Thread-4:995

正在執行是執行緒Thread-4:996

正在執行是執行緒Thread-4:997

正在執行是執行緒Thread-4:998

正在執行是執行緒Thread-4:999

正在執行是執行緒Thread-4:1000

正在執行是執行緒Thread-2:399

正在執行是執行緒Thread-1:398

正在執行是執行緒Thread-0:700

正在執行是執行緒Thread-3:402

===========================我是更新線==============================

看上面的結果其實是有一點不對的,前面的幾個計數發生了重複,這是由於鎖的位置不正確導致的。所以對這個程式重寫了一下。

package com.yolanda.fun.thread;


import java.util.concurrent.atomic.AtomicInteger;


public class MySafeCalcThread1 implements Runnable {
	
	private static AtomicInteger count = new AtomicInteger(0);
	
    public synchronized static void calc() {
    	if ((count.get()) < 1000) {
    		int c = count.incrementAndGet();// 自增1,返回更新值
    		System.out.println("正在執行是執行緒" + Thread.currentThread().getName() + ":" + c);
    	}
			
	}
	
	public void run() {
		 while(true)
         {
			 MySafeCalcThread1.calc();
			 try {
				 Thread.sleep(0);
			 } catch (InterruptedException e) {
				 // TODO Auto-generated catch block
				 e.printStackTrace();
			 }	 
         }
		
	}


	public static void main(String[] args) {
		for (int i = 0; i < 6; i++) {
			MySafeCalcThread1 thread = new MySafeCalcThread1();
			Thread t = new Thread(thread);
			t.start();
			
		}


	}




}

然後結果就對啦,就沒有重複計數的問題啦。

結果:

正在執行是執行緒Thread-0:1
正在執行是執行緒Thread-1:2
正在執行是執行緒Thread-0:4
正在執行是執行緒Thread-2:3
正在執行是執行緒Thread-2:7
正在執行是執行緒Thread-0:6
正在執行是執行緒Thread-0:9
正在執行是執行緒Thread-1:5
正在執行是執行緒Thread-0:12
正在執行是執行緒Thread-5:11
正在執行是執行緒Thread-3:10
正在執行是執行緒Thread-2:8

.......

正在執行是執行緒Thread-4:996
正在執行是執行緒Thread-4:997
正在執行是執行緒Thread-4:998
正在執行是執行緒Thread-4:999
正在執行是執行緒Thread-4:1000
正在執行是執行緒Thread-1:743
正在執行是執行緒Thread-3:742
正在執行是執行緒Thread-0:935
正在執行是執行緒Thread-5:884
正在執行是執行緒Thread-2:747

還有一種寫法是這樣子的~

package com.yolanda.fun.thread;


import java.util.concurrent.atomic.AtomicInteger;


public class MySafeCalcThread2 implements Runnable {
	
	private static AtomicInteger count = new AtomicInteger(0);
	
	public static void calc() {
		
		while ((count.get()) < 1000) {
			int c = 0;
			synchronized (count) {
				if ((count.get()) < 1000) {
					c = count.incrementAndGet();// 自增1,返回更新值
				}
			}
			System.out.println("正在執行是執行緒" + Thread.currentThread().getName() + ":" + c);
		}




	}
	
	public void run() {
		 while(true)
         {
			 MySafeCalcThread2.calc();
			 try {
				 Thread.sleep(0);
			 } catch (InterruptedException e) {
				 // TODO Auto-generated catch block
				 e.printStackTrace();
			 }	 
         }
		
	}


	public static void main(String[] args) {
		for (int i = 0; i < 6; i++) {
			MySafeCalcThread2 thread = new MySafeCalcThread2();
			Thread t = new Thread(thread);
			t.start();
			
		}


	}




}


結果:

正在執行是執行緒Thread-0:1
正在執行是執行緒Thread-3:2
正在執行是執行緒Thread-4:3
正在執行是執行緒Thread-5:4
正在執行是執行緒Thread-0:5
正在執行是執行緒Thread-0:6
正在執行是執行緒Thread-2:7
正在執行是執行緒Thread-2:8
正在執行是執行緒Thread-1:9
正在執行是執行緒Thread-2:10

........

正在執行是執行緒Thread-2:990
正在執行是執行緒Thread-2:991
正在執行是執行緒Thread-2:992
正在執行是執行緒Thread-1:993
正在執行是執行緒Thread-2:994
正在執行是執行緒Thread-0:995
正在執行是執行緒Thread-5:996
正在執行是執行緒Thread-5:997
正在執行是執行緒Thread-5:998
正在執行是執行緒Thread-5:999
正在執行是執行緒Thread-5:1000

============================我是更新線===============================

在此處補充一下JAVA多執行緒實現的兩種方式

第一種:繼承Thread類

package com.yolanda.fun.thread;

public class MyThread extends Thread {
	
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println("執行緒" + Thread.currentThread().getName() + "在執行");
		}
	}

	public static void main(String[] args) {
		MyThread thread = new MyThread();
		thread.start();
		
		for (int i = 0; i < 10; i++) {
			System.out.println("執行緒" + Thread.currentThread().getName() + "在執行");// 執行緒main
		}

	}

}



結果:

執行緒main在執行
執行緒Thread-0在執行
執行緒main在執行
執行緒main在執行
執行緒Thread-0在執行
執行緒Thread-0在執行
執行緒main在執行
執行緒Thread-0在執行
執行緒main在執行
執行緒Thread-0在執行
執行緒Thread-0在執行
執行緒Thread-0在執行
執行緒Thread-0在執行
執行緒Thread-0在執行
執行緒Thread-0在執行
執行緒main在執行
執行緒main在執行
執行緒main在執行
執行緒main在執行
執行緒main在執行

結果分析:這裡可以看到main執行緒和Thread-0執行緒交替執行。

所謂的多執行緒,指的是兩個執行緒的程式碼可以同時執行,而不必一個執行緒需要等待另一個執行緒內的程式碼執行完才可以執行。對於單核CPU來說,是無法做到真正的多執行緒的,每個時間點上,CPU都會執行特定的程式碼,由於CPU執行程式碼時間很快,所以兩個執行緒的程式碼交替執行看起來像是同時執行的一樣。那具體執行某段程式碼多少時間,就和分時機制系統有關了。分時系統把CPU時間劃分為多個時間片,作業系統以時間片為單位片為單位各個執行緒的程式碼,越好的CPU分出的時間片越小。所以看不到明顯效果也很正常,一個執行緒列印5句話本來就很快,可能在分出的時間片內就執行完成了。所以,最簡單的解決辦法就是把for迴圈的值調大一點就可以了(也可以在for迴圈里加Thread.sleep方法)。

第二種:實現Runnable介面。和繼承自Thread類差不多,不過實現Runnable後,還是要通過一個Thread來啟動

package com.yolanda.fun.thread;

public class MyRunnable implements Runnable{

	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println("執行緒" + Thread.currentThread().getName() + "在執行");
		}
	}
	
	public static void main(String[] args) {
		MyRunnable thread = new MyRunnable();
		Thread t = new Thread(thread);
		t.start();
		
		for (int i = 0; i < 10; i++) {
			System.out.println("執行緒" + Thread.currentThread().getName() + "在執行");
		}
	}

}


結果:

執行緒main在執行
執行緒Thread-0在執行
執行緒main在執行
執行緒Thread-0在執行
執行緒main在執行
執行緒Thread-0在執行
執行緒main在執行
執行緒main在執行
執行緒main在執行
執行緒main在執行
執行緒main在執行
執行緒main在執行
執行緒main在執行
執行緒Thread-0在執行
執行緒Thread-0在執行
執行緒Thread-0在執行
執行緒Thread-0在執行
執行緒Thread-0在執行
執行緒Thread-0在執行
執行緒Thread-0在執行

兩個執行緒也是交替執行。

其實Thread類也是實現的Runnable介面。


兩種實現方式對比的關鍵就在於extends和implements的對比,當然是後者好。因為第一,繼承只能但繼承,實現可以多實現;第二,實現的方式對比繼承的方式,也有利於減小程式之間的耦合。

因此,多執行緒的實現幾乎都是使用的Runnable介面的方式。

執行緒狀態

虛擬機器中的執行緒狀態有六種,定義在Thread.State中:


1、新建狀態NEW
new了但是沒有啟動的執行緒的狀態。比如"Thread t = new Thread()",t就是一個處於NEW狀態的執行緒

2、可執行狀態RUNNABLE
new出來執行緒,呼叫start()方法即處於RUNNABLE狀態了。處於RUNNABLE狀態的執行緒可能正在Java虛擬機器中執行,也可能正在等待處理器的資源,因為一個執行緒必須獲得CPU的資源後,才可以執行其run()方法中的內容,否則排隊等待

3、阻塞BLOCKED
如果某一執行緒正在等待監視器鎖,以便進入一個同步的塊/方法,那麼這個執行緒的狀態就是阻塞BLOCKED

4、等待WAITING
某一執行緒因為呼叫不帶超時的Object的wait()方法、不帶超時的Thread的join()方法、LockSupport的park()方法,就會處於等待WAITING狀態

5、超時等待TIMED_WAITING
某一執行緒因為呼叫帶有指定正等待時間的Object的wait()方法、Thread的join()方法、Thread的sleep()方法、LockSupport的parkNanos()方法、LockSupport的parkUntil()方法,就會處於超時等待TIMED_WAITING狀態

6、終止狀態TERMINATED
執行緒呼叫終止或者run()方法執行結束後,執行緒即處於終止狀態。處於終止狀態的執行緒不具備繼續執行的能力。

相關推薦

java 執行安全的全域性計數器-AtomicInteger原子操作類

首先 , 測試一下正常程式碼 public class Test1 { public static int count = 0; public static void main(String[] args) { for (int i =

Java執行安全計數器

   一、多執行緒以及執行緒安全           java執行緒安全就是指控制多個執行緒對某個資源的有序訪問或修改。這涉及兩個很重要的概念:java的記憶體模型和java的執行緒同步機制。    

Java執行安全

執行緒不安全的類: HashMap LinkedHashMap TreeMap ArrayList LinkedList HashSet TreeSet LinkedHashSet StringBuilder 執行緒安全的類: ConcurrentHashMap Hasht

Java執行安全與多執行開發

網際網路上充斥著對Java多執行緒程式設計的介紹,每篇文章都從不同的角度介紹並總結了該領域的內容。但大部分文章都沒有說明多執行緒的實現本質,沒能讓開發者真正“過癮”。 從Java的執行緒安全鼻祖內建鎖介紹開始,讓你瞭解內建鎖的實現邏輯和原理以及引發的效能問題,接著說明了Java多執行緒程式設計中鎖的存在是為

java執行安全問題

併發搶票過程中,如果不加同步鎖,就會出現執行緒不安全問題,導致程式碼執行結果出現嚴重問題。另外同一個執行緒啟動多次是不允許的。package sx.test.thread;public class TicketDemo {public static void main(Str

Java-執行安全

Java-執行緒安全 0x01 什麼是執行緒安全 執行緒安全是針對某個物件來說,如果當多執行緒訪問此物件時,不用再用額外方式如同步鎖等,總能執行獲得正確結果,那就可以說這個物件程式碼執行緒安全。 0x02 Java中的執行緒安全 Java中執行緒安全強度由強到弱是: 不可

關於java執行安全問題

執行緒:負責一個程序中的程式碼執行。執行緒是程序中的一個執行路徑。 執行緒安全問題出現的根本原因:1. 存在著兩個或者兩個以上的執行緒。2. 多個執行緒共享了著一個資源, 而且操作資源的程式碼有多句。 執行緒安全問題的解決方案:1. 使用同步程式碼塊格式:synchroni

Java執行安全佇列Queue

在Java多執行緒應用中,佇列的使用率很高,多數生產消費模型的首選資料結構就是佇列。Java提供的執行緒安全的Queue可以分為阻塞佇列和非阻塞佇列,其中阻塞佇列的典型例子是BlockingQueue,非阻塞佇列的典型例子是ConcurrentLinkedQue

java執行安全之併發Queue(十三)

併發Queue        在併發的佇列上jdk提供了兩套實現,一個是以ConcurrentLinkedQueue為代表的高效能佇列,一個是以BlockingQueue介面為代表的阻塞佇列,無論在那種都繼承自Queue。 如圖繼承Queue共有二十四個:

java執行安全之synchronized鎖重入及出現異常鎖自動釋放(五)

科技快訊       11月16日下午訊息,以“Bring AI to Life”為主題的2017百度世界大會今天在北京國貿大酒店和北京嘉裡大酒店舉行。愛奇藝創始人兼CEO龔宇在大會上發表了主題為“愛奇藝·更懂娛樂”的主題演講,龔宇表示愛奇藝對於科技的重視與百

java執行安全的實現方法_筆記

閱讀《深入理解java虛擬機器》後的閱讀筆記。1、 互斥同步同步時指在多個執行緒併發訪問共享資料時,保證共享資料在同一個時刻只被一個(或者是一些,使用訊號量的時候)執行緒使用。而互斥是實現同步的一種手段,臨界區,互斥量和訊號量都是主要的互斥實現方式。  1.1 在java中最

java執行安全之併發Queue

關閉 原 java執行緒安全之併發Queue(十三) 2017年11月19日 23:40:23 小彬彬~ 閱讀數:12092更多

java執行安全的容器有哪些?

同步容器類:使用了synchronized 1.Vector 2.HashTable 併發容器: 3.ConcurrentHashMap:分段 4.CopyOnWriteArrayList:寫時複製 5.CopyOnWriteArraySet:寫時複製 Queue: 6.C

學習筆記之Java執行安全雜談(上)——執行安全概念和基本方案

執行緒安全問題絕對是併發開發中一個重點中的重點,這篇就來說說執行緒安全相關的一些問題。執行緒安全是什麼概念?這個概念說簡單也簡單,說複雜也複雜,“安全”的概念是什麼,用我個人的話說的淺顯些,就是類/物件本身在多執行緒併發執行的場景下,能夠保證程式的邏輯是可以接受的而不是被

Java執行安全佇列操作

題目如下: 使用 wait notify 實現一個佇列,佇列有2個方法,add 和 get 。add方法往佇列中新增元素,get方法往佇列中獲得元素。佇列必須是執行緒安全的。如果get執行時,佇列為空,執行緒必須阻塞等待,直到有佇列有資料。如果add時,佇列已

Java執行安全的集合詳解

一、早期執行緒安全的集合 我們先從早期的執行緒安全的集合說起,它們是Vector和HashTable 1.Vector Vector和ArrayList類似,是長度可變的陣列,與ArrayList不同的是,Vector是執行緒安全的,它給幾乎所有的publ

java執行安全問題產生的原因

    最近工作中遇到不少多執行緒問題,但自己一直對多執行緒的理解比較表層。沒有深入探究。正是最近工作中遇到的問題,致使我深入的去了解多執行緒安全問題為什麼會產生。    多執行緒使我們的計算機可以同時執行多個程式,感覺很美好。    but,多執行緒的安全問題,到底怎麼回事

java執行安全問題之靜態變數、例項變數、區域性變數

 java多執行緒程式設計中,存在很多執行緒安全問題,至於什麼是執行緒安全呢,給出一個通俗易懂的概念還是蠻難的,如同《java併發程式設計實踐》中所說: 寫道 給執行緒安全下定義比較困難。存在很多種定義,如:“一個類在可以被多個執行緒安全呼叫時就是執行緒安全的”。 

Java執行安全之volatile關鍵字

一、前言 我們知道在多執行緒的場景下,執行緒安全是必須要著重考慮的。Java語言包含兩種內在的同步機制:同步塊(synchronize關鍵字)和 volatile 變數。但是其中 Volatile 變數雖然使用簡單,有時候開銷也比較低,但是同時它的同步性較差,

java執行安全的容器類

四、執行緒安全的容器類 Java編碼中,我們經常需要用到容器來程式設計。在併發環境下,Java提供一些已有容器能夠支援併發。 1.Map 在Map類中,提供兩種執行緒安全容器。 java.util.Hashtable Hashtable和H