1. 程式人生 > >JAVA 併發程式設計-執行緒範圍內共享變數(五)

JAVA 併發程式設計-執行緒範圍內共享變數(五)

執行緒範圍內共享變數要實現的效果為:

多個物件間共享同一執行緒內的變數


未實現執行緒共享變數的demo

package cn.itcast.heima2;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class ThreadScopeShareData {

	private static int data = 0;
//	private static Map<Thread, Integer> threadData = new HashMap<Thread, Integer>();
	
	public static void main(String[] args) {
		//共啟動2個執行緒
		for(int i=0;i<2;i++){
			//啟動一個執行緒
			new Thread(new Runnable(){
				@Override
				public void run() {
					data = new Random().nextInt();
					System.out.println(Thread.currentThread().getName() 
							+ " has put data :" + data);
					//以當前執行緒為key值放入到map中,當取值時根據各自的執行緒取各自的資料
//					threadData.put(Thread.currentThread(), data);
					new A().get();
					new B().get();
				}
			}).start();
		}
	}
	
	static class A{
		public void get(){
//			int data = threadData.get(Thread.currentThread());
			System.out.println("A from " + Thread.currentThread().getName() 
					+ " get data :" + data);
		}
	}
	
	static class B{
		public void get(){
//			int data = threadData.get(Thread.currentThread());			
			System.out.println("B from " + Thread.currentThread().getName() 
					+ " get data :" + data);
		}		
	}
}

執行結果:


通過打印出的結果可以看出,當Thread-0獲取了一個隨機數,修改了data的值,正在睡眠的時候,Thread-1又獲取了一個隨機數,同樣修改了data的值,然後Thread-1呼叫了靜態內部類ABget方法,實際上此時的data已經是Thread-1拿到的隨機數了。

當然,我們可以通過增加synchronized加鎖來控制執行緒的執行。讓Thread-0執行完方法之前,Thread-1不能修改data的值。

此外,還可以使用另外幾種方法來獲取執行緒執行時變數賦予的真正值。

執行緒範圍內共享變數實現方式: 

Map實現方式:

package cn.itcast.heima2;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class ThreadScopeShareData {

	
	private static Map<Thread, Integer> threadData = new HashMap<Thread, Integer>();
	
	public static void main(String[] args) {
		//共啟動2個執行緒
		for(int i=0;i<2;i++){
			//啟動一個執行緒
			new Thread(new Runnable(){
				@Override
				public void run() {
					int data = new Random().nextInt();
					System.out.println(Thread.currentThread().getName() 
							+ " has put data :" + data);
					//以當前執行緒為key值放入到map中,當取值時根據各自的執行緒取各自的資料
					threadData.put(Thread.currentThread(), data);
					new A().get();
					new B().get();
				}
			}).start();
		}
	}
	
	static class A{
		public void get(){
			int data = threadData.get(Thread.currentThread());
			System.out.println("A from " + Thread.currentThread().getName() 
					+ " get data :" + data);
		}
	}
	
	static class B{
		public void get(){
			int data = threadData.get(Thread.currentThread());			
			System.out.println("B from " + Thread.currentThread().getName() 
					+ " get data :" + data);
		}		
	}
}

執行結果:

 

ThreadLocal方式:

package cn.itcast.heima2;

import java.util.Random;

public class ThreadLocalTest {

	private static ThreadLocal<Integer> x = new ThreadLocal<Integer>();
	public static void main(String[] args) {
		for(int i=0;i<2;i++){
			new Thread(new Runnable(){
				@Override
				public void run() {
					int data = new Random().nextInt();
					System.out.println(Thread.currentThread().getName() 
							+ " has put data :" + data);
					x.set(data);
					new A().get();
					new B().get();
				}							
			}).start();
		}
	}
	
	static class A{
		public void get(){
			int data = x.get();
			System.out.println("A from " + Thread.currentThread().getName() 
					+ " get data :" + data);
		}
	}
	
	static class B{
		public void get(){
			int data = x.get();			
			System.out.println("B from " + Thread.currentThread().getName() 
					+ " get data :" + data);					
		}		
	}
}


存在的問題:一個ThreadLocal代表一個變數,故其中只能放一個數據,如果你有兩個變數要執行緒範圍內共享,則要定義兩個ThreadLocal。如下為解決方案:

擴充套件方式-單例方式處理物件:

package cn.itcast.heima2;

import java.util.Random;

public class ThreadLocalTest {

//	方式一
//	private static ThreadLocal<Integer> x = new ThreadLocal<Integer>();
	
	private static ThreadLocal<MyThreadScopeData> myThreadScopeData = new ThreadLocal<MyThreadScopeData>();
	public static void main(String[] args) {
		for(int i=0;i<2;i++){
			new Thread(new Runnable(){
				@Override
				public void run() {
					int data = new Random().nextInt();
					System.out.println(Thread.currentThread().getName() 
							+ " has put data :" + data);
//					方式一 ThreadLocal
//					x.set(data);
//					方式二 new物件方式,將多個屬性放到物件中
//					MyThreadScopeData myData = new MyThreadScopeData();
//					myData.setName("name" + data);
//					myData.setAge(data);
//					myThreadScopeData.set(myData);
//					方式三 使用單例模式
					MyThreadScopeData.getThreadInstance().setName("name" + data);
					MyThreadScopeData.getThreadInstance().setAge(data);
					
					new A().get();
					new B().get();
				}							
			}).start();
		}
	}
	
	static class A{
		public void get(){
//			方式一 ThreadLocal
//			int data = x.get();
//			System.out.println("A from " + Thread.currentThread().getName() 
//					+ " get data :" + data);
//			方式二 new物件方式,將多個屬性放到物件中
//			MyThreadScopeData myData = myThreadScopeData.get();;
//			System.out.println("A from " + Thread.currentThread().getName() 
//					+ " getMyData: " + myData.getName() + "," +
//					myData.getAge());
//			方式三 使用單例模式
			MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
			System.out.println("A from " + Thread.currentThread().getName() 
					+ " getMyData: " + myData.getName() + "," +
					myData.getAge());
		}
	}
	
	static class B{
		public void get(){
//			int data = x.get();			
//			System.out.println("B from " + Thread.currentThread().getName() 
//					+ " get data :" + data);
//			MyThreadScopeData myData = myThreadScopeData.get();;
//			System.out.println("B from " + Thread.currentThread().getName() 
//					+ " getMyData: " + myData.getName() + "," +
//					myData.getAge());
			MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
			System.out.println("B from " + Thread.currentThread().getName() 
					+ " getMyData: " + myData.getName() + "," +
					myData.getAge());			
		}		
	}
}

class MyThreadScopeData{
	
	private MyThreadScopeData(){}
	
	private static MyThreadScopeData instance = null;//new MyThreadScopeData();
	
	private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>();
	
	public static /*synchronized*/ MyThreadScopeData getThreadInstance(){
		MyThreadScopeData instance = map.get();
		if(instance == null){
			instance = new MyThreadScopeData();
			map.set(instance);
		}
		return instance;
	}
	
	
	private String name;
	private int age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}


總結:

    synchronized和使用ThreadLocal均可以解決以上的問題,只是這是兩種不同的方式,synchronized是依賴鎖的機制一個執行完後另一個再執行。ThreadLocal會為每一個執行緒維護一個和該執行緒繫結的變數的副本,從而隔離了多個執行緒的資料,每一個執行緒都擁有自己的變數副本,從而也就沒有必要對該變數進行同步了。

    概括起來說,對於多執行緒資源共享的問題,同步機制採用了“以時間換空間”的方式,而ThreadLocal採用了“以空間換時間”的方式。前者僅提供一份變數,讓不同的執行緒排隊訪問,而後者為每一個執行緒都提供了一份變數,因此可以同時訪問而互不影響。

    當然ThreadLocal並不能替代同步機制,兩者面向的問題領域不同。同步機制是為了同步多個執行緒對相同資源的併發訪問,是為了多個執行緒之間進行通訊的有效方式;而ThreadLocal是隔離多個執行緒的資料共享,從根本上就不在多個執行緒之間共享資源(變數),這樣當然不需要對多個執行緒進行同步了。

    ThreadLocal的應用:

    在業務邏輯層需要呼叫多個Dao層的方法,我們要保證事務(jdbc事務)就要確保他們使用的是同一個資料庫連線.那麼如何確保使用同一個資料庫連線呢?


相關推薦

JAVA 併發程式設計-執行範圍共享變數

執行緒範圍內共享變數要實現的效果為:多個物件間共享同一執行緒內的變數未實現執行緒共享變數的demo:package cn.itcast.heima2; import java.util.HashMap; import java.util.Map; import java.u

(三) Java執行詳解之執行範圍共享變數及ThreadLocal類使用

執行緒範圍內共享變數 HashTable方式實現 在開發中經常會遇到一種情況:有一個變數會被多個執行緒訪問,但是要確保同個執行緒內訪問的是同一個物件,Hashtable方式實現程式碼如下: public class ThreadExample5 {

執行併發----執行範圍共享變數

執行緒範圍內共享資料圖解:mport java.util.HashMap; import java.util.Map; import java.util.Random; public class Th

Java執行執行範圍共享變數的概念與作用

要實現執行緒範圍內的資料共享,就是說不管是A模組還是B模組,如果它們現在在同一個執行緒上執行,它們操作的資料應該是同一個,下面的做法就不行: package javaplay.thread.test; import java.util.Random; public

05_張孝祥_Java多執行_執行範圍共享變數的概念與作用

概念 可以將每個執行緒用到的資料與對應的執行緒號存放到一個map集合中,使用資料時從這個集合中根據執行緒號獲取對應執行緒的資料,就可以實現執行緒範圍內共享相同的變數。 程式碼 Runnable中的run()方法裡面執行Thread.currentThrea

執行範圍共享變數的概念與作用

package cn.cblue.heima2; import java.util.HashMap; import java.util.Map; import java.util.Rando

android 執行範圍共享變數以及ThreadLocal的使用

執行緒在java中是一個重頭戲,算是比較難的一快,特別是併發哪一塊,關於併發這一塊,專案上幾乎也沒用到,今天是講執行緒範圍內的共享變數,突然聽到這個概念,可能心裡有點發愣,打個簡單比方:有三個執行緒,

ThreadLocal實現執行範圍共享變數

 1.多執行緒範圍內訪問共享物件和資料的方式。  1).如果每個執行緒要執行的程式碼一樣,可以使用同一個Runnable物件,這個Runnable物件中有共享資料,例如:買票系統。  2).每一個執行緒要執行的程式碼不一樣,這樣就需要使用多個Runnable物件了。有以下幾

Java併發、六、七執行範圍共享資料、ThreadLocal、共享資料的三種方法

深切懷念傳智播客張孝祥老師,特將其代表作——Java併發庫視訊研讀兩遍,受益頗豐,記以後閱 05. 執行緒範圍內共享變數的概念與作用 執行緒範圍內共享資料圖解: 程式碼演示: class ThreadScopeShareData {        三個模組共享資料,主執

java執行併發庫高階應用 之 執行範圍共享資料

轉自:http://blog.csdn.net/xushuaic/article/category/1335611 筆記摘要:  所謂執行緒範圍內共享資料,即對於相同的程式程式碼,多個模組在同一個執行緒中執行時要共享一份資料,而在另外執行緒中執行時又共

執行併發庫高階應用 之 執行範圍共享資料

筆記摘要:  所謂執行緒範圍內共享資料,即對於相同的程式程式碼,多個模組在同一個執行緒中執行時要共享一份資料,而在另外執行緒中執行時又共享另外一份資料,              API中為我們提供了一個操作執行緒範圍內共享資料的類ThreadLocal,對於執行緒範

十三java併發程式設計--執行中斷

1、自己新增執行緒退出標誌位。 如下程式碼所示: package thread_priority; /** * Created by fang on 2017/12/3. * */ public class MyThread imple

十二java併發程式設計--執行優先順序

執行緒的優先順序並不能保證現成的執行次序。只不過,優先順序高的執行緒獲取CPU資源的概率較大,優先順序低的也並不是沒有機會執行。 優先順序用1-10的整數表示,數值越大優先順序越高,預設

java併發程式設計--執行的死鎖(deadlock)

執行緒在作業系統使用不同的資源,一般以以下方式使用這些資源。 1)請求一個資源。 2)使用這個資源。 3)釋放資源。 1、什麼是死鎖? 死鎖的情況是,一些執行緒被阻塞,每個執行緒都擁有一個資源,並且等待另外一個程序以獲取另外的一個資源。 想了想

十六java併發程式設計--執行的死鎖解決方案(生產者和消費者幾種實現方式)

上一篇中,主要了解了什麼時候死鎖,並且提出死鎖的一個解決方案,多個鎖要按照一定的順序來。 本片主要是利用生產者消費者模式解決執行緒的死鎖。 多執行緒生產者和消費者一個典型的多執行緒程式。一個生產者生產提供消費的東西,但是生產速度和消費速度是不同的。這就需要讓

十四java併發程式設計--執行的阻塞

java中我們可以使用執行緒類的三種方式來阻止執行緒的執行。 執行緒的狀態圖如下(圖片來自網路): 1、yield() yield英文的意思是屈服,如同其意,當前執行緒屈服,暫停,讓同等優先順序的執行緒執行。 yield()方法可以暫停當前

Java併發程式設計--執行安全問題與解決方案

本文簡介: 用多執行緒開發的人都知道,在多執行緒的開發過程中有可能會出現執行緒安全問題(專業術語叫記憶體可見性問題),但並不一定每次都會出現。出現這樣的情況,也會另開發者頭皮發麻,無從下手,接下來我們會慢慢深入,揭開多執行緒的神祕面紗。 本文主要介紹了Jav

[Java併發程式設計]-執行的六種狀態及其狀態轉換

轉載請註明:http://blog.csdn.net/UniKylin/article/details/45050823 1.執行緒自身資訊 執行緒執行的過程會產生很多資訊,這些資訊都儲存在Thread類中的成員變數裡面,常見的有: a.執行緒的ID是唯

java併發程式設計—— 執行池原理 詳解 ThreadPoolExecutor

為什麼要使用執行緒池 降低資源消耗: 通過重複利用執行緒,減少執行緒的建立銷燬損耗的資源 提高響應速度: 任務到達時,不用重新建立執行緒,之間可以使用已經建立好的執行緒執行 提高執行緒的可管理性 執行緒池實現分析 我們使用如下的demo來一步一

JAVA併發程式設計——執行協作通訊

執行緒間的協作 在前面我們瞭解了很多關於同步(互斥鎖)的問題,下面來看一下執行緒之間的協作。這裡主要說一下Java執行緒中的join()、sleep()、yield()、wait()、notify()和notifyAll()方法。其中wait()、notify(