Thread原理+实例说明

来源:互联网 发布:国际网络公开课 编辑:程序博客网 时间:2024/05/17 09:45

Kaiwii 读书笔记+实践总结:

1ThreadLocal与同步都是解决多线程中的冲突问题的。但是,两者的解决思路不太一样。

同步,是每个线程一起共享这个资源;而ThreadLocal,使得每个线程只是各自获得这个资源的副本,所以,某个线程对这个资源作修改只是在这个线程中有效,对其他线程的这个资源是没有影响的。

2、每个线程获得资源,都是在initialValue() 中初始化的,其他线程对这个资源的修改不影响这个线程中的这个资源的值。

JDK 1.2的版本中就提供java.lang.ThreadLocalThreadLocal为解决多线程程序的局部变量独立问题...

 

查看API我们可以查看ThreadLocal的定义与方法:

 

该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。

例如,以下类生成对每个线程唯一的局部标识符。线程 ID 是在第一次调用UniqueThreadIdGenerator.getCurrentThreadId() 时分配的,在后续调用中不会更改。

 

构造方法摘要

ThreadLocal() 
          创建一个线程本地变量。

 方法摘要

 T

get() 
          返回此线程局部变量的当前线程副本中的值。

protected  T

initialValue() 
          返回此线程局部变量的当前线程的初始值

 void

remove() 
          移除此线程局部变量当前线程的值。

 void

set(T value) 
          将此线程局部变量的当前线程副本中的值设置为指定值。

 

remove()

移除此线程局部变量当前线程的值,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

 

initialValue()

返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。如果泛型对象是Integer,可以定义内部内,重写initialValue() 返回0Integer类型的数据!

 

ThreadLocal并非一个所谓的 "local thread (本地线程)",而是处理线程中的局部变量的.其可以理解为:"local thread variable(线程局部变量)"!

 

线程局部变量已经有很多语言使用了,并非是Java独创...很多语言(如IBM IBM XL FORTRAN)在语法层面就提供线程局部变量。

 

ThreadLocal的具体实现原理大致如下:

 

[java] view plaincopy

package cn.vicky;  

  

import java.util.Collections;  

import java.util.HashMap;  

import java.util.Map;  

  

public class RealizeThreadLocal {  

  

    private Map<String, Object> map = Collections.synchronizedMap(new HashMap<String, Object>());  

10   

11     public void set(Object value) {  

12         map.put(Thread.currentThread().getName(), value);   

13     }  

14   

15     public Object get() {  

16         String threadName = Thread.currentThread().getName();  

17         Object o = map.get(threadName);  

18         if (o == null && !map.containsKey(threadName)) {  

19             o = initialValue();  

20             map.put(threadName, o);  

21         }  

22         return o;  

23     }  

24   

25     public void remove() {  

26         map.remove(Thread.currentThread().getName());  

27     }  

28   

29     public Object initialValue() {  

30         return null;  

31     }  

32   

33 }  

 

JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>其实现原理大致如下:

 

[java] view plaincopy

34 package cn.vicky;  

35   

36 import java.util.Collections;  

37 import java.util.HashMap;  

38 import java.util.Map;  

39   

40 public class RealizeThreadLocal2<T> {  

41   

42     private Map<Long, T> map = Collections.synchronizedMap(new HashMap<Long, T>());  

43   

44     public void set(T value) {  

45         map.put(Thread.currentThread().getId(), value);  

46     }  

47   

48     public T get() {  

49         long threadID = Thread.currentThread().getId();  

50         T o = map.get(threadID);  

51         if (o == null && !map.containsKey(threadID)) {  

52             o = initialValue();  

53             map.put(threadID, o);  

54         }  

55         return o;  

56     }  

57   

58     public void remove() {  

59         map.remove(Thread.currentThread().getName());  

60     }  

61   

62     public T initialValue() {  

63         return null;  

64     }  

65 }  

 

 

如何使用:

 

[java] view plaincopy

66 package cn.vicky;  

67   

68 public class SequenceNumber {  

69   

70     // 通过匿名内部类覆盖RealizeThreadLocal2initialValue()方法,根据泛型类型指定对应初始值  

71     private static RealizeThreadLocal2<Integer> seqNum = new RealizeThreadLocal2<Integer>() {  

72         @Override  

73         public Integer initialValue() {  

74             return 0;  

75         }  

76     };  

77   

78     // 获取下一个序列值  

79     public int getNextNum() {  

80         seqNum.set(seqNum.get() + 1);  

81         return seqNum.get();  

82     }  

83   

84     public static void main(String[] args) {  

85         SequenceNumber sn = new SequenceNumber();  

86         // 3个线程共享sn,各自产生序列号  

87         TestThread tt1 = new TestThread(sn);  

88         TestThread tt2 = new TestThread(sn);  

89         TestThread tt3 = new TestThread(sn);  

90         Thread t1 = new Thread(tt1);  

91         Thread t2 = new Thread(tt2);  

92         Thread t3 = new Thread(tt3);  

93         t1.start();  

94         t2.start();  

95         t3.start();  

96     }  

97 }  

 

[java] view plaincopy

98 package cn.vicky;  

99   

100 public class TestThread implements Runnable {  

101     private SequenceNumber sn;  

102   

103     public TestThread(SequenceNumber sn) {  

104         this.sn = sn;  

105     }  

106   

107     public void run() {  

108         // 每个线程打出3个序列值  

109         for (int i = 0; i < 3; i++) {  

110             System.out.println("thread[" + Thread.currentThread().getName() + "] sn[" + sn.getNextNum() + "]");  

111         }  

112     }  

113 }  

 

打印:

 

thread[Thread-0] sn[1]
thread[Thread-0] sn[2]
thread[Thread-0] sn[3]
thread[Thread-2] sn[1]
thread[Thread-2] sn[2]
thread[Thread-2] sn[3]
thread[Thread-1] sn[1]
thread[Thread-1] sn[2]
thread[Thread-1] sn[3]

 

TestThread tt1 = new TestThread(sn);
TestThread tt2 = new TestThread(sn);
TestThread tt3 = new TestThread(sn);

输出的结果信息,我们发现每个线程所产生的序号虽然都共享同一个SequenceNumber (sn) 实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本!

 

 

 

让我们再看个简单的实例:

[java] view plaincopy

114 package cn.vicky.chapt05;  

115   

116 /** 

117  * 

118  * @author Vicky.H 

119  */  

120 public class ThreadLocalTest1 {  

121   

122     /** 实现了每个线程的静态变量不同 **/  

123     private static ThreadLocal<String> val = new ThreadLocal<String>();  

124   

125     public static class Thread1 extends Thread {  

126   

127         private String name;  

128   

129         public Thread1(String name) {  

130             this.name = name;  

131         }  

132   

133         public void run() {  

134             System.out.println(name + "初始值:" + val.get());  

135             val.set("v[" + name + "]");  

136             System.out.println(name + "设置后值:" + val.get());  

137         }  

138     }  

139   

140     public static class Thread2 extends Thread {  

141   

142         private String name;  

143   

144         public Thread2(String name) {  

145             this.name = name;  

146         }  

147   

148         public void run() {  

149             System.out.println(name + "初始值:" + val.get());  

150             val.set("v[" + name + "]");  

151             System.out.println(name + "设置后值:" + val.get());  

152         }  

153     }  

154     public static void main(String[] args) {  

155         val.set("1");  

156         System.out.println("主程序中设置值:" + val.get());  

157         (new Thread1("A1")).start();  

158         (new Thread1("A")).start();  

159         (new Thread2("B1")).start();  

160     }  

161 //主程序中设置值:1  

162 //A1初始值:null  

163 //A1设置后值:v[A1]  

164 //B1初始值:null  

165 //A初始值:null  

166 //A设置后值:v[A]  

167 //B1设置后值:v[B1]  

168       

169 //总结   

170 //ThreadLocal使用场合主要解决多线程中数据数据因并发产生不一致问题。  

171 //ThreadLocal为每个线程的中并发访问的数据提供一个副本,通过访问副本来运行业务,这样的结果是耗费了内存,单大大减少了线程同步所带来性能消耗,也减少了线程并发控制的复杂度。   

172 //ThreadLocal不能使用原子类型,只能使用Object类型。  

173 //ThreadLocal的使用比synchronized要简单得多。   

174 //ThreadLocalSynchonized都用于解决多线程并发访问。但是ThreadLocalsynchronized有本质的区别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。   

175 //Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。   

176 //当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂。   

177 }  


 

[java] view plaincopy

178 package cn.vicky.chapt05;  

179   

180 /** 

181  * 

182  * @author Vicky.H 

183  */  

184 public class ThreadLocalTest2 {  

185   

186     /** 实现了每个线程的静态变量不同 **/  

187     private static String val;  

188   

189     public static class Thread1 extends Thread {  

190   

191         private String name;  

192   

193         public Thread1(String name) {  

194             this.name = name;  

195         }  

196   

197         public void run() {  

198             System.out.println(name + "初始值:" + val);  

199             val = "v[" + name + "]";  

200             System.out.println(name + "设置后值:" + val);  

201         }  

202     }  

203   

204     public static class Thread2 extends Thread {  

205   

206         private String name;  

207   

208         public Thread2(String name) {  

209             this.name = name;  

210         }  

211   

212         public void run() {  

213             System.out.println(name + "初始值:" + val);  

214             val = "v[" + name + "]";  

215             System.out.println(name + "设置后值:" + val);  

216         }  

217     }  

218     public static void main(String[] args) {  

219         val = "1";  

220         System.out.println("主程序中设置值:" + val);  

221         (new Thread1("A1")).start();  

222         (new Thread1("A")).start();  

223         (new Thread2("B1")).start();  

224     }  

225       

226 //主程序中设置值:1  

227 //A1初始值:1  

228 //A1设置后值:v[A1]  

229 //A初始值:v[A1]  

230 //A设置后值:v[A]  

231 //B1初始值:v[A]  

232 //B1设置后值:v[B1]  

233 }  


 

原创粉丝点击