線程間通訊

1.輪詢機制

經過不斷訪問共享對象的狀態,來判斷是否知足了線程執行要求java

定義操做類MyObject,內部有一個volatile關鍵字修飾的list成員變量,使用volatile主要是爲了讓操做類對象的改變可讓每一個線程都能感應到。代碼以下:ide

package com.feng.example;

import java.util.ArrayList;
import java.util.List;

public class MyObject {

	//此處必須是volatile,不然線程B中的object感應不到線程A中的object的變化
	volatile private List<String> list = new ArrayList<String>();   
	
	 public void add() {

		 list.add("hahaha");
	 }
	 
	 public int size(){
		 //System.out.println("hahah");
		 return list.size();
	 }
}

定義兩個線程類MyThreadA 用於向MyObject對象中添加數據,MyThreadB用於在list中存在大於等於五個元素時退出函數

代碼以下:工具

package com.feng.example;

public class MyThreadA extends Thread{

	private MyObject object;
	
	public MyThreadA(MyObject object)
	{
		this.object = object;
	}
	
	@Override
	public void run() {
		
		try {
			for(int i=0; i<10; i++)
			{
				System.out.println("添加了第"+i+"個元素");
				object.add();
				Thread.sleep(1000);
			}
			
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}	
}
package com.feng.example;

public class MyThreadB extends Thread {

	private MyObject object;

	public MyThreadB(MyObject object) {
		this.object = object;
	}

	@Override
	public void run() {

		// object.print();
		try {
			while (true) {
				if (object.size() >= 5) {
					System.out.println("有五個元素,線程B應該退出了");
					// 終止線程
					throw new InterruptedException();
				}
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

測試類代碼以下:測試

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */
	
	public static void main(String[] args) {
			
		MyObject object = new MyObject();
		
		Thread a = new MyThreadA(object);
		Thread b = new MyThreadB(object);
		
		a.start();
		b.start();
	
	}

}

分析:線程b在while(true)無限循環中不斷的判斷object對象的個數,若是超過5就會終止線程。線程b須要不斷的去訪問object對象的size方法才能知道object對象的狀態。這就是輪詢的方式,很是的浪費cpu資源。
this

執行結果以下:spa

添加了第0個元素
添加了第1個元素
添加了第2個元素
添加了第3個元素
添加了第4個元素java.lang.InterruptedException
有五個元素,線程B應該退出了

	at com.feng.example.MyThreadB.run(MyThreadB.java:20)
添加了第5個元素
添加了第6個元素
添加了第7個元素
添加了第8個元素
添加了第9個元素

2.等待/通知機制

2.1等待/通知的知識點

使用wait和notify來實現多個線程間的通訊。下面介紹一下這兩個方法的注意事項線程

(1)wait使線程進入阻塞隊列,notify喚醒保持同一鎖對象的wait阻塞線程code

(2)wait和notify都只能在synchronized語句塊或者synchronized方法中使用,保證使用時是持有鎖的,不然會拋出異常對象

(3)調用wait方法,直接釋放鎖,調用notify方法,執行完同步代碼段後才釋放鎖

(4)線程被notify喚醒後進入就緒隊列,從新競爭鎖,並非直接獲取鎖對象

(5)notify方法只喚醒一個線程,若是有多個wait線程,隨機喚醒一個,notifyAll是喚醒全部(前提仍是要有同一個鎖對象)

(6)notify方法執行以後,若是沒有wait阻塞線程,則直接忽略

(7)終止wait中的線程,拋出異常

(8)wait(long) 等待特定的時間,在特定時間內沒有被喚醒將自動喚醒

wait和notify,notifyAll方法是object中的方法,全部的類都是繼承於object,所以全部的類都有wait和notify,notifyAll方法。

使用等待/通知機制實現上述功能:

修改兩個線程類MyThreadA與MyThreadB,代碼以下:

package com.feng.example;

public class MyThreadA extends Thread{

	private MyObject object;
	
	public MyThreadA(MyObject object)
	{
		this.object = object;
	}
	
	@Override
	public void run() {
		
		try {
			synchronized(object)
			{
				for(int i=0; i<10; i++)
				{
					System.out.println("添加了第"+i+"個元素");
					object.add();
					if(object.size()==5)
					{
						object.notify();
						System.out.println("通知已發出");
					}
					Thread.sleep(1000);
				}
			}
			
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}	
}
package com.feng.example;

public class MyThreadB extends Thread {

	private MyObject object;

	public MyThreadB(MyObject object) {
		this.object = object;
	}

	@Override
	public void run() {

		
		// object.print();
		try {
			synchronized(object)
			{
				if(object.size() < 5)
				{
					System.out.println("線程b等待");
					object.wait();
					System.out.println("線程b被喚醒 ");
				}
				throw new InterruptedException();
			}		
			
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

測試類代碼修改以下:

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */
	
	public static void main(String[] args) {
			
		MyObject object = new MyObject();
		
		Thread a = new MyThreadA(object);
		Thread b = new MyThreadB(object);
		
		b.start();
		try {
			Thread.sleep(50);  //確保線程b先執行
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		a.start();
		
	}

}

分析:線程a,b啓動,線程a先獲取到object的鎖,往object對象中添加元素,添加到五個時,發出notify通知,可是線程a並不釋放鎖,而是繼續執行,執行完同步代碼段以後釋放鎖,而後線程b獲取到鎖,終止線程b

運行結果以下:

線程b等待
添加了第0個元素
添加了第1個元素
添加了第2個元素
添加了第3個元素
添加了第4個元素
通知已發出
添加了第5個元素
添加了第6個元素
添加了第7個元素
添加了第8個元素
添加了第9個元素
線程b被喚醒 
java.lang.InterruptedException
	at com.feng.example.MyThreadB.run(MyThreadB.java:25)

上述程序須要保證是notify發生在wait以後,若是notify先發生,那麼wait將永遠等待

上述實驗也證實了執行notify後是不釋放鎖的,執行完同步代碼塊以後才能釋放鎖。

2.2wait/notify使用前必須獲取鎖對象

觀察下面代碼,下面代碼中的MyObject類爲上例中的MyObject類

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */
	
	public static void main(String[] args) {
			
		MyObject object = new MyObject();
		try {
			object.wait();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

分析:object.wait()必需要有鎖對象纔可以調用,所以,這裏的調用是不對的,運行會拋出非法的監視器狀態違例

notify同理,這裏就不演示了。執行結果以下:

Exception in thread "main" java.lang.IllegalMonitorStateException
	at java.lang.Object.wait(Native Method)
	at java.lang.Object.wait(Object.java:485)
	at com.feng.example.ThreadTest.main(ThreadTest.java:13)

修改就是添加synchronized語句塊,修改以下:

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */
	
	public static void main(String[] args) {
			
		MyObject object = new MyObject();
		try {
			synchronized(object)
			{
				object.wait();
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

2.3wait直接釋放鎖,notify執行完同步代碼塊以後纔會釋放鎖

notify執行完同步代碼塊以後才能釋放鎖已經在2.1中證明了。下面驗證wait直接釋放鎖。

修改測試類代碼:

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */
	
	public static void main(String[] args) {
			
		MyObject object = new MyObject();
		
		Thread a = new MyThreadB(object);
		Thread b = new MyThreadB(object);
		
		b.start();
		a.start();
		
	}

}

運行結果以下:

線程b等待
線程b等待

能夠證明wait執行後直接釋放鎖

2.4 Interrupt wait中的線程,拋出異常

修改測試類:

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */
	
	public static void main(String[] args) {
			
		MyObject object = new MyObject();
		
		Thread a = new MyThreadB(object);
		
		a.start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		a.interrupt();
		
	}

}

運行結果以下:

線程b等待
java.lang.InterruptedException
	at java.lang.Object.wait(Native Method)
	at java.lang.Object.wait(Object.java:485)
	at com.feng.example.MyThreadB.run(MyThreadB.java:22)

2.5 wait(long)  

修改MyThreadB類:

package com.feng.example;

public class MyThreadB extends Thread {

	private MyObject object;

	public MyThreadB(MyObject object) {
		this.object = object;
	}

	@Override
	public void run() {

		
		// object.print();
		try {
			synchronized(object)
			{
				if(object.size() < 5)
				{
					System.out.println("線程b等待"+System.currentTimeMillis());
					object.wait(1000);
					System.out.println("線程b被喚醒 "+System.currentTimeMillis());
				}
			}		
			
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

修改測試類代碼:

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */
	
	public static void main(String[] args) {
			
		MyObject object = new MyObject();
		
		Thread a = new MyThreadB(object);
		
		a.start();
		
	}

}

運行結果以下:

線程b等待1449803493388
線程b被喚醒 1449803494402

此示例中並無notify方法喚醒線程a,是等待1s以後自動喚醒。

2.6 wait條件發生變化

定義操做類:

package com.feng.example;

import java.util.ArrayList;
import java.util.List;

public class MyObject {

	//此處必須是volatile,不然線程B中的object感應不到線程A中的object的變化
	volatile private List<String> list = new ArrayList<String>();   
	
	 public void add() {

		 list.add("hahaha");
	 }
	 
	 public int size()
	 {
		 return list.size();
	 }
	 
	 public void sub(){
		 //System.out.println("hahah");
		 list.remove(0);
	 }
}

定義兩個線程類,線程B添加元素,線程A刪除元素

package com.feng.example;

public class MyThreadA extends Thread {

	private MyObject object;

	public MyThreadA(MyObject object) {
		this.object = object;
	}

	@Override
	public void run() {
		try {
			synchronized (object) {
				if (object.size() <= 0) {

					System.out.println("等待===="+Thread.currentThread().getName());
					object.wait();
					
					System.out.println("被喚醒==="+Thread.currentThread().getName());
					
				}
				
				object.sub();
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
package com.feng.example;

public class MyThreadB extends Thread {

	private MyObject object;

	public MyThreadB(MyObject object) {
		this.object = object;
	}

	@Override
	public void run() {

		synchronized (object) {
			
			object.add();
			System.out.println("添加一個元素");
			object.notifyAll();
			System.out.println("喚醒全部");
		}

	}

}

測試類代碼:

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */
	
	public static void main(String[] args) {
			
		MyObject object = new MyObject();
		
		Thread b = new MyThreadB(object);
		Thread a = new MyThreadA(object);
		Thread c = new MyThreadA(object);
		
		a.start();
		c.start();
		
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		b.start();
		
	}

}

分析:a,c兩個線程都是執行的減操做,object中沒有數據,兩個線程都阻塞,線程b執行添加操做,喚醒a,c線程,只添加了一個元素,卻喚醒兩個元素去執行減操做,後減的線程會出現越界。

執行結果以下:

等待====Thread-1
等待====Thread-2
添加一個元素
Exception in thread "Thread-1" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	at java.util.ArrayList.RangeCheck(ArrayList.java:547)
	at java.util.ArrayList.remove(ArrayList.java:387)
	at com.feng.example.MyObject.sub(MyObject.java:23)
	at com.feng.example.MyThreadA.run(MyThreadA.java:24)
喚醒全部
被喚醒===Thread-2
被喚醒===Thread-1

修改類MyThreadA的代碼,在喚醒後都要從新檢查一遍是否知足減的條件,將if修改成while

package com.feng.example;

public class MyThreadA extends Thread {

	private MyObject object;

	public MyThreadA(MyObject object) {
		this.object = object;
	}

	@Override
	public void run() {
		try {
			synchronized (object) {
				while (object.size() <= 0) {

					System.out.println("等待===="+Thread.currentThread().getName());
					object.wait();
					
					System.out.println("被喚醒==="+Thread.currentThread().getName());
					
				}
				
				object.sub();
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

執行結果以下:

等待====Thread-1
等待====Thread-2
添加一個元素
喚醒全部
被喚醒===Thread-2
被喚醒===Thread-1
等待====Thread-1

3.管道通訊

3.1字節流

主要用的輸入輸出流爲PipedInputStream PipedOutputStream

定義兩個操做類WriteData用於寫數據, ReaderData用於讀數據,代碼以下:

package com.feng.example;

import java.io.IOException;
import java.io.PipedOutputStream;

public class WriteData {
	
	public void WriteMethod(PipedOutputStream out)
	{
		try {
			
			System.out.println("write:");
			for(int i=0; i< 10; i++)
			{
				String data = ""+i;
				out.write(data.getBytes());
				System.out.print(data);

			}
			System.out.println();
			out.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
package com.feng.example;

import java.io.IOException;
import java.io.PipedInputStream;

public class ReaderData {

	public void ReaderMethod(PipedInputStream in) {
		
		try {
			System.out.println("read:");
			byte[] data = new byte[11];
			int length = in.read(data);
			while (length != -1) {
				String newData = new String(data, 0, length);
				System.out.print(newData);
				length = in.read(data);

			}
			System.out.println();
			in.close();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

定義兩個線程類MyThreadA調用寫操做,MyThreadB調用讀操做,代碼以下:

package com.feng.example;

import java.io.PipedOutputStream;

public class MyThreadA extends Thread {

	private WriteData writeData;
	private PipedOutputStream out;
	
	public MyThreadA(WriteData writeData, PipedOutputStream out) {

		this.writeData = writeData;
		this.out = out;
	}

	@Override
	public void run() {
		
		writeData.WriteMethod(out);
	}
}
package com.feng.example;

import java.io.PipedInputStream;

public class MyThreadB extends Thread {

	private ReaderData readerData;
	private PipedInputStream in;

	public MyThreadB(ReaderData readerData, PipedInputStream in) {

		this.readerData = readerData;
		this.in = in;
	}

	@Override
	public void run() {

		readerData.ReaderMethod(in);

	}

}

測試類代碼以下:

package com.feng.example;

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

public class ThreadTest {

	/**
	 * @param args
	 */
	
	public static void main(String[] args) {
				
		
		try {
			WriteData wd = new WriteData();
			ReaderData rd = new ReaderData();
			
			PipedOutputStream out = new PipedOutputStream();
			PipedInputStream in = new PipedInputStream();
			out.connect(in);
			
			Thread a = new MyThreadA(wd, out);
			Thread b = new MyThreadB(rd, in);
			
			b.start();
			
			Thread.sleep(2000);
			
			a.start();
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		
	}

}

分析:在測試類中定義管道輸入流in管道輸出流out,使用in.connect(out)或者out.connect(in)將輸入輸出流綁定。也就是輸入流讀取的是輸出流寫入的數據。程序首先執行線程b,線程b執行讀操做,此時輸出流並無寫任何數據,所以線程b在in.read(data)處阻塞。線程a執行寫操做後,線程b才獲取到數據繼續執行。

結果以下:

read:
write:
0123456789
0123456789

3.2字符流

主要就是將處理字節改成處理字符,將PipedInputStream改成PipedReader,將PipedOutputStream改成PipedWriter

修改後的操做類以下:

package com.feng.example;

import java.io.IOException;
import java.io.PipedWriter;

public class WriteData {
	
	public void WriteMethod(PipedWriter out)
	{
		try {
			
			System.out.println("write:");
			for(int i=0; i< 10; i++)
			{
				String data = ""+i;
				out.write(data);
				System.out.print(data);

			}
			System.out.println();
			out.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
package com.feng.example;

import java.io.IOException;
import java.io.PipedReader;

public class ReaderData {

	public void ReaderMethod(PipedReader in) {
		
		try {
			System.out.println("read:");
			char[] data = new char[11];
			int length = in.read(data);
			while (length != -1) {
				String newData = new String(data, 0, length);
				System.out.print(newData);
				length = in.read(data);

			}
			System.out.println();
			in.close();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

線程類以下:

package com.feng.example;

import java.io.PipedWriter;

public class MyThreadA extends Thread {

	private WriteData writeData;
	private PipedWriter out;
	
	public MyThreadA(WriteData writeData, PipedWriter out) {

		this.writeData = writeData;
		this.out = out;
	}

	@Override
	public void run() {
		
		writeData.WriteMethod(out);
	}
}
package com.feng.example;

import java.io.PipedReader;

public class MyThreadB extends Thread {

	private ReaderData readerData;
	private PipedReader in;

	public MyThreadB(ReaderData readerData, PipedReader in) {

		this.readerData = readerData;
		this.in = in;
	}

	@Override
	public void run() {

		readerData.ReaderMethod(in);

	}

}

測試類以下:

package com.feng.example;

import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;

public class ThreadTest {

	/**
	 * @param args
	 */
	
	public static void main(String[] args) {
				
		
		try {
			WriteData wd = new WriteData();
			ReaderData rd = new ReaderData();
			
			PipedWriter out = new PipedWriter();
			PipedReader in = new PipedReader();
			out.connect(in);
			
			Thread a = new MyThreadA(wd, out);
			Thread b = new MyThreadB(rd, in);
			
			b.start();
			
			Thread.sleep(2000);
			
			a.start();
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		
	}

}

運行結果:

read:
write:
0123456789
0123456789

4.join

join方法用於等待線程對象銷燬,執行a.join()的線程阻塞,等待線程對象a執行完成後繼續執行。

定義線程類:

package com.feng.example;

public class MyThread extends Thread {
	

	@Override
	public void run() {
			
		try {
			System.out.println(Thread.currentThread().getName()+"===="+System.currentTimeMillis());
			Thread.sleep(2000);
			System.out.println(Thread.currentThread().getName()+"===="+System.currentTimeMillis());
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}

定義測試類:

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */
	
	public static void main(String[] args) {
					
		try {
			
			Thread a = new MyThread();
			a.start();
			a.join();
			Thread.sleep(100);
			System.out.println("線程a執行完了"+System.currentTimeMillis());
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}	
		
	}

}

運行結果:

Thread-0====1449821481676
Thread-0====1449821483689
線程a執行完了1449821483799

從結果能夠看出,main函數中最後的輸出語句是在線程a執行完成後輸出的。

在join過程當中,若是線程(執行join的線程好比上述的主線程)被中斷,拋出異常

join(long)  等待有限時間,注意與sleep(long)的區別

5.ThreadLocal的使用

ThreadLocal主要解決線程間變量的隔離性,它會爲每個線程保存一份本身的值。在Struts中使用的就是ThreadLocal

查看下面代碼:

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */
	private static ThreadLocal tl = new ThreadLocal();
	
	public static void main(String[] args) {
			
		if(tl.get() == null)
		{
			System.out.println("沒有放過值");
			tl.set("newData");
		}
		
		System.out.println(tl.get());
		System.out.println(tl.get());
	}

}

運行結果以下:

沒有放過值
newData
newData

說明,沒有值時取出的爲null

下面來驗證ThreadLocal的隔離性,新建兩個線程,兩個線程向ThreadLocal中添加數據,觀察數據會不會出現混亂。

定義工具類:

package com.feng.example;

public class Tools {

	public static ThreadLocal t = new ThreadLocal();
}

定義兩個線程類:

package com.feng.example;


public class MyThreadA extends Thread {

	@Override
	public void run() {

		try {
			for (int i = 0; i < 10; i++) {
				Tools.t.set(Thread.currentThread().getName() +" "+ i);
				Thread.sleep(1000);
				System.out.println(Thread.currentThread().getName()+"==="+Tools.t.get());
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
package com.feng.example;


public class MyThreadB extends Thread {


	@Override
	public void run() {

		try {
			for (int i = 0; i < 10; i++) {
				Tools.t.set(Thread.currentThread().getName() +" "+ i);
				Thread.sleep(1000);
				System.out.println(Thread.currentThread().getName()+"==="+Tools.t.get());
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}

測試類:

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */
	
	public static void main(String[] args) {
			
		Thread a = new MyThreadA();
		Thread b = new MyThreadB();
		
		a.start();
		b.start();
		
	}

}

運行結果:

Thread-1===Thread-1 0
Thread-0===Thread-0 0
Thread-1===Thread-1 1
Thread-0===Thread-0 1
Thread-1===Thread-1 2
Thread-0===Thread-0 2
Thread-1===Thread-1 3
Thread-0===Thread-0 3
Thread-1===Thread-1 4
Thread-0===Thread-0 4
Thread-1===Thread-1 5
Thread-0===Thread-0 5
Thread-1===Thread-1 6
Thread-0===Thread-0 6
Thread-1===Thread-1 7
Thread-0===Thread-0 7
Thread-1===Thread-1 8
Thread-0===Thread-0 8
Thread-1===Thread-1 9
Thread-0===Thread-0 9

從結果中能夠看出,數據並無混亂,能夠得知t.get(),是根據執行這條語句的currentThread來提取的值,t.set()也是根絕currentThread來存的值,也就是說每個線程都會有一個數據的備份,都有本身的值,相互不會影響。

若是想修改初始值,能夠重寫ThreadLocal值的initialValue方法;

定義本身的類繼承ThreadLocal

package com.feng.example;

public class MyThreadLocal extends ThreadLocal{

	@Override
	protected Object initialValue() {
		// TODO Auto-generated method stub
		return "本身設置的初始值";
	}
	
	

}

測試:

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */
	
	private static MyThreadLocal t = new MyThreadLocal();
	
	public static void main(String[] args) {
			
		System.out.println(t.get());
		
	}

}

執行結果:

本身設置的初始值

可見本身設置的值生效了

6.InheritableThradLocal的使用

此類主要能夠獲取到父線程的值,(父線程並非繼承關係的,是誰建立的誰就是父線程)

定義本身的ThreadLocal類繼承InheritableThreadLocal類,重寫initialValue方法,這裏返回時間,主要看何時初始化的。從而判斷是子線程初始化仍是父線程初始化的。

代碼以下:

package com.feng.example;

import java.util.Date;

public class MyThreadLocal extends InheritableThreadLocal{

	@Override
	protected Object initialValue() {
		// TODO Auto-generated method stub
		return new Date();
	}

}

定義工具類:

package com.feng.example;

public class Tools {

	public static MyThreadLocal t = new MyThreadLocal();
}

定義線程類:

package com.feng.example;


public class MyThreadA extends Thread {

	@Override
	public void run() {

		System.out.println(Tools.t.get());
	}
}

定義測試類:

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */

	
	public static void main(String[] args) {
			
		System.out.println(Tools.t.get());
		Thread a = new MyThreadA();
		a.start();
		
	}

}

運行結果:

Fri Dec 11 16:49:48 CST 2015
Fri Dec 11 16:49:48 CST 2015

可見main線程中和a線程中的值是同樣的,時間同樣說明子線程繼承了main線程中的值。

若是想在繼承的時候修改,能夠重寫childValue方法

修改自定義ThreadLocal類,代碼以下:

package com.feng.example;

import java.util.Date;

public class MyThreadLocal extends InheritableThreadLocal{

	@Override
	protected Object initialValue() {
		// TODO Auto-generated method stub
		return new Date();
	}

	@Override
	protected Object childValue(Object parentValue) {
		// TODO Auto-generated method stub
		return parentValue +"==="+new Date();
	}
	
	

}

運行代碼以下:

Fri Dec 11 16:51:46 CST 2015
Fri Dec 11 16:51:46 CST 2015===Fri Dec 11 16:51:46 CST 2015

若是父線程中的值修改了,那麼子線程獲取的是在取值時候的值。

相關文章
相關標籤/搜索