线程间通信

北战南征 提交于 2019-12-09 20:00:20

1.轮询机制

通过不断访问共享对象的状态,来判断是否满足了线程执行要求

定义操作类MyObject,内部有一个volatile关键字修饰的list成员变量,使用volatile主要是为了让操作类对象的改变可以让每个线程都能感应到。代码如下:

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资源。

执行结果如下:

添加了第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阻塞线程

(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

如果父线程中的值修改了,那么子线程获取的是在取值时候的值。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!