- 浏览: 685226 次
- 性别:
- 来自: 上海
文章分类
- 全部博客 (254)
- java分布式应用架构 (22)
- SSH框架整合 (6)
- java web 学习笔记 (49)
- java 学习笔记 (56)
- struts 2 学习 (6)
- Hibernate学习 (10)
- spring 学习 (2)
- 客户端编程(javascript) (4)
- IDE使用 (13)
- 生命 人生 (6)
- 系统维护 (3)
- 技术篇 (10)
- MySql (2)
- J2ME (1)
- java网络编程 (4)
- 数据库 (5)
- C/C++ (8)
- Oracle (7)
- 软件测试 (0)
- 软件的安装和部署 (0)
- Java快讯 (1)
- swt (1)
- Flex (1)
- 软件工程 (1)
- PostgreSQL (1)
- sql server2000 (2)
- 嵌入式数据库sqlite (5)
- J2EE (1)
- XML (1)
- ibatis3(MyBatis) (6)
- Linux&Unix (1)
- velocity (1)
- 回报社会 (4)
- 软件项目管理 (3)
- android研究 (3)
- C# (2)
- Objective-C (1)
- 音乐 (0)
- webx (1)
- JMS (1)
- maven软件项目管理 (1)
- 分布式服务 (0)
- 云平台 (0)
- 分布式存储 (1)
- 分布式系统架构 (0)
- 移动互联网 (1)
- ZooKeeper (1)
最新评论
-
liyys:
楼主,可不可以发这个项目的源码工程出来分享一下,少了几个类。楼 ...
仿照Hibernate实现一个SQLite的ORM框架 -
liyys:
少了一些类的源码没有粘贴出来
仿照Hibernate实现一个SQLite的ORM框架 -
honglei0412:
我使用的是这种方式获取db文件的目录但是 URL p = Fi ...
使用sqlite注意事项 -
honglei0412:
大侠 能不能说明下DbFile您是怎么做的吗?
使用sqlite注意事项 -
ahack:
刚写完mapping才发现早就有人写好了。仔细一看还都是针对的 ...
仿照Hibernate实现一个SQLite的ORM框架
ThreadLocal与synchronized
Java良好的支持多线程。使用java,我们可以很轻松的编程一个多线程程序。但是使用多线程可能会引起并发访问的问题。 synchronized和ThreadLocal都是用来解决多线程并发访问的问题。大家可能对synchronized较为熟悉,而对 ThreadLocal就要陌生得多了。
并发问题。当一个对象被两个线程同时访问时,可能有一个线程会得到不可预期的结果。
一个简单的java类Studnet
Java 代码
1. public class Student {
2. private int age=0;
3.
4. public int getAge() {
5. return this.age;
6.
7. }
8.
9. public void setAge(int age) {
10. this.age = age;
11. }
12. }
public class Student {
private int age=0;
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
}
一个多线程类ThreadDemo.
这个类有一个Student的私有变量,在run方法中,它随机产生一个整数。然后设置到student变量中,从student中读取设置后的值。然后睡眠5秒钟,最后再次读student的age值。
Java 代码
1. public class ThreadDemo implements Runnable{
2. Student student = new Student();
3. public static void main(String[] agrs) {
4. ThreadDemo td = new ThreadDemo();
5. Thread t1 = new Thread(td,"a");
6. Thread t2 = new Thread(td,"b");
7. t1.start();
8. t2.start();
9.
10. }
11. /* (non-Javadoc)
12. * @see java.lang.Runnable#run()
13. */
14. public void run() {
15. accessStudent();
16. }
17.
18. public void accessStudent() {
19. String currentThreadName = Thread.currentThread().getName();
20. System.out.println(currentThreadName+" is running!");
21. // System.out.println("first read age is:"+this.student.getAge());
22. Random random = new Random();
23. int age = random.nextInt(100);
24. System.out.println("thread "+currentThreadName +" set age to:"+age);
25.
26. this.student.setAge(age);
27. System.out.println("thread "+currentThreadName+" first read age is:"+this.student.getAge());
28. try {
29. Thread.sleep(5000);
30. }
31. catch(InterruptedException ex) {
32. ex.printStackTrace();
33. }
34. System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
35.
36. }
37.
38. }
public class ThreadDemo implements Runnable{
Student student = new Student();
public static void main(String[] agrs) {
ThreadDemo td = new ThreadDemo();
Thread t1 = new Thread(td,"a");
Thread t2 = new Thread(td,"b");
t1.start();
t2.start();
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public void run() {
accessStudent();
}
public void accessStudent() {
String currentThreadName = Thread.currentThread().getName();
System.out.println(currentThreadName+" is running!");
// System.out.println("first read age is:"+this.student.getAge());
Random random = new Random();
int age = random.nextInt(100);
System.out.println("thread "+currentThreadName +" set age to:"+age);
this.student.setAge(age);
System.out.println("thread "+currentThreadName+" first read age is:"+this.student.getAge());
try {
Thread.sleep(5000);
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
}
}
运行这个程序,屏幕输出如下:
a is running!
b is running!
thread b set age to:33
thread b first read age is:33
thread a set age to:81
thread a first read age is:81
thread b second read age is:81
thread a second read age is:81
需要注意的是,线程a在同一个方法中,第一次读取student的age值与第二次读取值不一致。这就是出现了并发问题。
synchronized
上面的例子,我们模似了一个并发问题。Java提供了同步机制来解决并发问题。synchonzied关键字可以用来同步变量,方法,甚至同步一个代码块。
使用了同步后,一个线程正在访问同步对象时,另外一个线程必须等待。
Synchronized同步方法
现在我们可以对accessStudent方法实施同步。
public synchronized void accessStudent()
再次运行程序,屏幕输出如下:
a is running!
thread a set age to:49
thread a first read age is:49
thread a second read age is:49
b is running!
thread b set age to:17
thread b first read age is:17
thread b second read age is:17
加上了同步后,线程b必须等待线程a执行完毕后,线程b才开始执行。
对方法进行同步的代价是非常昂贵的。特别是当被同步的方法执行一个冗长的操作。这个方法执行会花费很长的时间,对这样的方法进行同步可能会使系统性能成数量级的下降。
Synchronized同步块
在accessStudent方法中,我们真实需要保护的是student变量,所以我们可以进行一个更细粒度的加锁。我们仅仅对student相关的代码块进行同步。
Java 代码
1. synchronized(this) {
2. Random random = new Random();
3. int age = random.nextInt(100);
4. System.out.println("thread "+currentThreadName +" set age to:"+age);
5.
6. this.student.setAge(age);
7.
8. System.out.println("thread "+currentThreadName+" first read age is:"+this.student.getAge());
9. try {
10. Thread.sleep(5000);
11. }
12. catch(InterruptedException ex) {
13. ex.printStackTrace();
14. }
15. }
synchronized(this) {
Random random = new Random();
int age = random.nextInt(100);
System.out.println("thread "+currentThreadName +" set age to:"+age);
this.student.setAge(age);
System.out.println("thread "+currentThreadName+" first read age is:"+this.student.getAge());
try {
Thread.sleep(5000);
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
}
运行方法后,屏幕输出:
a is running!
thread a set age to:18
thread a first read age is:18
b is running!
thread a second read age is:18
thread b set age to:62
thread b first read age is:62
thread b second read age is:62
需要特别注意这个输出结果。
这个执行过程比上面的方法同步要快得多了。
只有对student进行访问的代码是同步的,而其它与部份代码却是异步的了。而student的值并没有被错误的修改。如果是在一个真实的系统中,accessStudent方法的操作又比较耗时的情况下。使用同步的速度几乎与没有同步一样快。
使用同步锁
稍微把上面的例子改一下,在ThreadDemo中有一个私有变量count,。
private int count=0;
在accessStudent()中, 线程每访问一次,count都自加一次, 用来记数线程访问的次数。
Java 代码
1. try {
2. this.count++;
3. Thread.sleep(5000);
4. }catch(InterruptedException ex) {
5. ex.printStackTrace();
6. }
try {
this.count++;
Thread.sleep(5000);
}catch(InterruptedException ex) {
ex.printStackTrace();
}
为了模拟线程,所以让它每次自加后都睡眠5秒。
accessStuden()方法的完整代码如下:
Java 代码
1. String currentThreadName = Thread.currentThread().getName();
2. System.out.println(currentThreadName+" is running!");
3. try {
4. this.count++;
5. Thread.sleep(5000);
6. }catch(InterruptedException ex) {
7. ex.printStackTrace();
8. }
9. System.out.println("thread "+currentThreadName+" read count:"+this.count);
10.
11.
12. synchronized(this) {
13. Random random = new Random();
14. int age = random.nextInt(100);
15. System.out.println("thread "+currentThreadName +" set age to:"+age);
16.
17. this.student.setAge(age);
18.
19. System.out.println("thread "+currentThreadName+" first read age is:"+this.student.getAge());
20. try {
21. Thread.sleep(5000);
22. }
23. catch(InterruptedException ex) {
24. ex.printStackTrace();
25. }
26. }
27. System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
String currentThreadName = Thread.currentThread().getName();
System.out.println(currentThreadName+" is running!");
try {
this.count++;
Thread.sleep(5000);
}catch(InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("thread "+currentThreadName+" read count:"+this.count);
synchronized(this) {
Random random = new Random();
int age = random.nextInt(100);
System.out.println("thread "+currentThreadName +" set age to:"+age);
this.student.setAge(age);
System.out.println("thread "+currentThreadName+" first read age is:"+this.student.getAge());
try {
Thread.sleep(5000);
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
}
System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
运行程序后,屏幕输出:
a is running!
b is running!
thread a read count:2
thread a set age to:49
thread a first read age is:49
thread b read count:2
thread a second read age is:49
thread b set age to:7
thread b first read age is:7
thread b second read age is:7
我们仍然对student对象以synchronized(this)操作进行同步。
我们需要在两个线程中共享count失败。
所以仍然需要对count的访问进行同步操作。
Java 代码
1. synchronized(this) {
2. try {
3. this.count++;
4. Thread.sleep(5000);
5. }catch(InterruptedException ex) {
6. ex.printStackTrace();
7. }
8. }
9. System.out.println("thread "+currentThreadName+" read count:"+this.count);
10.
11.
12. synchronized(this) {
13. Random random = new Random();
14. int age = random.nextInt(100);
15. System.out.println("thread "+currentThreadName +" set age to:"+age);
16.
17. this.student.setAge(age);
18.
19. System.out.println("thread "+currentThreadName+" first read age is:"+this.student.getAge());
20. try {
21. Thread.sleep(5000);
22. }
23. catch(InterruptedException ex) {
24. ex.printStackTrace();
25. }
26. }
27. System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
28. long endTime = System.currentTimeMillis();
29. long spendTime = endTime - startTime;
30. System.out.println("花费时间:"+spendTime +"毫秒");
synchronized(this) {
try {
this.count++;
Thread.sleep(5000);
}catch(InterruptedException ex) {
ex.printStackTrace();
}
}
System.out.println("thread "+currentThreadName+" read count:"+this.count);
synchronized(this) {
Random random = new Random();
int age = random.nextInt(100);
System.out.println("thread "+currentThreadName +" set age to:"+age);
this.student.setAge(age);
System.out.println("thread "+currentThreadName+" first read age is:"+this.student.getAge());
try {
Thread.sleep(5000);
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
}
System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
long endTime = System.currentTimeMillis();
long spendTime = endTime - startTime;
System.out.println("花费时间:"+spendTime +"毫秒");
程序运行后,屏幕输出
a is running!
b is running!
thread a read count:1
thread a set age to:97
thread a first read age is:97
thread a second read age is:97
花费时间:10015毫秒
thread b read count:2
thread b set age to:47
thread b first read age is:47
thread b second read age is:47
花费时间:20124毫秒
我们在同一个方法中,多次使用synchronized(this)进行加锁。有可能会导致太多额外的等待。
应该使用不同的对象锁进行同步。
设置两个锁对象,分别用于student和count的访问加锁。
Java 代码
1. private Object studentLock = new Object();
2. private Object countLock = new Object();
3.
4. accessStudent() 方法如下:
5. long startTime = System.currentTimeMillis();
6. String currentThreadName = Thread.currentThread().getName();
7. System.out.println(currentThreadName+" is running!");
8. // System.out.println("first read age is:"+this.student.getAge());
9.
10. synchronized(countLock) {
11. try {
12. this.count++;
13. Thread.sleep(5000);
14. }catch(InterruptedException ex) {
15. ex.printStackTrace();
16. }
17. }
18. System.out.println("thread "+currentThreadName+" read count:"+this.count);
19.
20.
21. synchronized(studentLock) {
22. Random random = new Random();
23. int age = random.nextInt(100);
24. System.out.println("thread "+currentThreadName +" set age to:"+age);
25.
26. this.student.setAge(age);
27.
28. System.out.println("thread "+currentThreadName+" first read age is:"+this.student.getAge());
29. try {
30. Thread.sleep(5000);
31. }
32. catch(InterruptedException ex) {
33. ex.printStackTrace();
34. }
35. }
36. System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
37. long endTime = System.currentTimeMillis();
38. long spendTime = endTime - startTime;
39. System.out.println("花费时间:"+spendTime +"毫秒");
private Object studentLock = new Object();
private Object countLock = new Object();
accessStudent()方法如下:
long startTime = System.currentTimeMillis();
String currentThreadName = Thread.currentThread().getName();
System.out.println(currentThreadName+" is running!");
// System.out.println("first read age is:"+this.student.getAge());
synchronized(countLock) {
try {
this.count++;
Thread.sleep(5000);
}catch(InterruptedException ex) {
ex.printStackTrace();
}
}
System.out.println("thread "+currentThreadName+" read count:"+this.count);
synchronized(studentLock) {
Random random = new Random();
int age = random.nextInt(100);
System.out.println("thread "+currentThreadName +" set age to:"+age);
this.student.setAge(age);
System.out.println("thread "+currentThreadName+" first read age is:"+this.student.getAge());
try {
Thread.sleep(5000);
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
}
System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
long endTime = System.currentTimeMillis();
long spendTime = endTime - startTime;
System.out.println("花费时间:"+spendTime +"毫秒");
这样对count和student加上了两把不同的锁。
运行程序后,屏幕输出:
a is running!
b is running!
thread a read count:1
thread a set age to:48
thread a first read age is:48
thread a second read age is:48
花费时间:10016毫秒
thread b read count:2
thread b set age to:68
thread b first read age is:68
thread b second read age is:68
花费时间:20046毫秒
与两次使用synchronized(this)相比,使用不同的对象锁,在性能上可以得到更大的提升。
由此可见synchronized是实现java的同步机制。同步机制是为了实现同步多线程对相同资源的并发访问控制。保证多线程之间的通信。
可见,同步的主要目的是保证多线程间的数据共享。同步会带来巨大的性能开销,所以同步操作应该是细粒度的。如果同步使用得当,带来的性能开销是微不足道的。使用同步真正的风险是复杂性和可能破坏资源安全,而不是性能。
ThreadLocal
由上面可以知道,使用同步是非常复杂的。并且同步会带来性能的降低。Java提供了另外的一种方式,通过ThreadLocal可以很容易的编写多线程程序。从字面上理解,很容易会把ThreadLocal误解为一个线程的本地变量。其它ThreadLocal并不是代表当前线程,ThreadLocal 其实是采用哈希表的方式来为每个线程都提供一个变量的副本。从而保证各个线程间数据安全。每个线程的数据不会被另外线程访问和破坏。
我们把第一个例子用ThreadLocal来实现,但是我们需要些许改变。
Student并不是一个私有变量了,而是需要封装在一个ThreadLocal对象中去。调用ThreadLocal的set方法,ThreadLocal会为每一个线程都保持一份Student变量的副本。所以对student的读取操作都是通过ThreadLocal来进行的。
Java 代码
1. protected Student getStudent() {
2. Student student = (Student)studentLocal.get();
3. if(student == null) {
4. student = new Student();
5. studentLocal.set(student);
6. }
7. return student;
8. }
9.
10. protected void setStudent(Student student) {
11. studentLocal.set(student);
12. }
protected Student getStudent() {
Student student = (Student)studentLocal.get();
if(student == null) {
student = new Student();
studentLocal.set(student);
}
return student;
}
protected void setStudent(Student student) {
studentLocal.set(student);
}
accessStudent()方法需要做一些改变。通过调用getStudent()方法来获得当前线程的Student变量,如果当前线程不存在一个Student变量,getStudent方法会创建一个新的Student变量,并设置在当前线程中。
Student student = getStudent();
student.setAge(age);
accessStudent()方法中无需要任何同步代码。
完整的代码清单如下:
TreadLocalDemo.java
Java 代码
1. public class TreadLocalDemo implements Runnable {
2. private final static ThreadLocal studentLocal = new ThreadLocal();
3.
4. public static void main(String[] agrs) {
5. TreadLocalDemo td = new TreadLocalDemo();
6. Thread t1 = new Thread(td,"a");
7. Thread t2 = new Thread(td,"b");
8.
9. t1.start();
10. t2.start();
11.
12.
13.
14.
15. }
16.
17. /* (non-Javadoc)
18. * @see java.lang.Runnable#run()
19. */
20. public void run() {
21. accessStudent();
22. }
23.
24. public void accessStudent() {
25.
26. String currentThreadName = Thread.currentThread().getName();
27. System.out.println(currentThreadName+" is running!");
28. Random random = new Random();
29. int age = random.nextInt(100);
30. System.out.println("thread "+currentThreadName +" set age to:"+age);
31. Student student = getStudent();
32. student.setAge(age);
33. System.out.println("thread "+currentThreadName+" first read age is:"+student.getAge());
34. try {
35. Thread.sleep(5000);
36. }
37. catch(InterruptedException ex) {
38. ex.printStackTrace();
39. }
40. System.out.println("thread "+currentThreadName +" second read age is:"+student.getAge());
41.
42. }
43.
44. protected Student getStudent() {
45. Student student = (Student)studentLocal.get();
46. if(student == null) {
47. student = new Student();
48. studentLocal.set(student);
49. }
50. return student;
51. }
52.
53. protected void setStudent(Student student) {
54. studentLocal.set(student);
55. }
56. }
public class TreadLocalDemo implements Runnable {
private final static ThreadLocal studentLocal = new ThreadLocal();
public static void main(String[] agrs) {
TreadLocalDemo td = new TreadLocalDemo();
Thread t1 = new Thread(td,"a");
Thread t2 = new Thread(td,"b");
t1.start();
t2.start();
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public void run() {
accessStudent();
}
public void accessStudent() {
String currentThreadName = Thread.currentThread().getName();
System.out.println(currentThreadName+" is running!");
Random random = new Random();
int age = random.nextInt(100);
System.out.println("thread "+currentThreadName +" set age to:"+age);
Student student = getStudent();
student.setAge(age);
System.out.println("thread "+currentThreadName+" first read age is:"+student.getAge());
try {
Thread.sleep(5000);
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("thread "+currentThreadName +" second read age is:"+student.getAge());
}
protected Student getStudent() {
Student student = (Student)studentLocal.get();
if(student == null) {
student = new Student();
studentLocal.set(student);
}
return student;
}
protected void setStudent(Student student) {
studentLocal.set(student);
}
}
运行程序后,屏幕输出:
b is running!
thread b set age to:0
thread b first read age is:0
a is running!
thread a set age to:17
thread a first read age is:17
thread b second read age is:0
thread a second read age is:17
可见,使用ThreadLocal后,我们不需要任何同步代码,却能够保证我们线程间数据的安全。
而且,ThreadLocal的使用也非常的简单。
我们仅仅需要使用它提供的两个方法
void set(Object obj) 设置当前线程的变量的副本的值。
Object get() 返回当前线程的变量副本
另外ThreadLocal还有一个protected的initialValue()方法。返回变量副本在当前线程的初始值。默认为null
ThreadLocal是怎么做到为每个线程都维护一个变量的副本的呢?
我们可以猜测到ThreadLocal的一个简单实现
Java 代码
1. public class ThreadLocal
2. {
3. private Map values = Collections.synchronizedMap(new HashMap());
4. public Object get()
5. {
6. Thread curThread = Thread.currentThread();
7. Object o = values.get(curThread);
8. if (o == null && !values.containsKey(curThread))
9. {
10. o = initialValue();
11. values.put(curThread, o);
12. }
13. return o;
14. }
15.
16. public void set(Object newValue)
17. {
18. values.put(Thread.currentThread(), newValue);
19. }
20.
21. public Object initialValue()
22. {
23. return null;
24. }
25. }
public class ThreadLocal
{
private Map values = Collections.synchronizedMap(new HashMap());
public Object get()
{
Thread curThread = Thread.currentThread();
Object o = values.get(curThread);
if (o == null && !values.containsKey(curThread))
{
o = initialValue();
values.put(curThread, o);
}
return o;
}
public void set(Object newValue)
{
values.put(Thread.currentThread(), newValue);
}
public Object initialValue()
{
return null;
}
}
由此可见,ThreadLocal通过一个Map来为每个线程都持有一个变量副本。这个map以当前线程为key。与synchronized相比,ThreadLocal是以空间换时间的策略来实现多线程程序。
Synchronized还是ThreadLocal?
ThreadLocal以空间换取时间,提供了一种非常简便的多线程实现方式。因为多个线程并发访问无需进行等待,所以使用 ThreadLocal会获得更大的性能。虽然使用ThreadLocal会带来更多的内存开销,但这点开销是微不足道的。因为保存在 ThreadLocal中的对象,通常都是比较小的对象。另外使用ThreadLocal不能使用原子类型,只能使用Object类型。 ThreadLocal的使用比synchronized要简单得多。
ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。
Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比 ThreadLocal更加复杂。
Java良好的支持多线程。使用java,我们可以很轻松的编程一个多线程程序。但是使用多线程可能会引起并发访问的问题。 synchronized和ThreadLocal都是用来解决多线程并发访问的问题。大家可能对synchronized较为熟悉,而对 ThreadLocal就要陌生得多了。
并发问题。当一个对象被两个线程同时访问时,可能有一个线程会得到不可预期的结果。
一个简单的java类Studnet
Java 代码
1. public class Student {
2. private int age=0;
3.
4. public int getAge() {
5. return this.age;
6.
7. }
8.
9. public void setAge(int age) {
10. this.age = age;
11. }
12. }
public class Student {
private int age=0;
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
}
一个多线程类ThreadDemo.
这个类有一个Student的私有变量,在run方法中,它随机产生一个整数。然后设置到student变量中,从student中读取设置后的值。然后睡眠5秒钟,最后再次读student的age值。
Java 代码
1. public class ThreadDemo implements Runnable{
2. Student student = new Student();
3. public static void main(String[] agrs) {
4. ThreadDemo td = new ThreadDemo();
5. Thread t1 = new Thread(td,"a");
6. Thread t2 = new Thread(td,"b");
7. t1.start();
8. t2.start();
9.
10. }
11. /* (non-Javadoc)
12. * @see java.lang.Runnable#run()
13. */
14. public void run() {
15. accessStudent();
16. }
17.
18. public void accessStudent() {
19. String currentThreadName = Thread.currentThread().getName();
20. System.out.println(currentThreadName+" is running!");
21. // System.out.println("first read age is:"+this.student.getAge());
22. Random random = new Random();
23. int age = random.nextInt(100);
24. System.out.println("thread "+currentThreadName +" set age to:"+age);
25.
26. this.student.setAge(age);
27. System.out.println("thread "+currentThreadName+" first read age is:"+this.student.getAge());
28. try {
29. Thread.sleep(5000);
30. }
31. catch(InterruptedException ex) {
32. ex.printStackTrace();
33. }
34. System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
35.
36. }
37.
38. }
public class ThreadDemo implements Runnable{
Student student = new Student();
public static void main(String[] agrs) {
ThreadDemo td = new ThreadDemo();
Thread t1 = new Thread(td,"a");
Thread t2 = new Thread(td,"b");
t1.start();
t2.start();
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public void run() {
accessStudent();
}
public void accessStudent() {
String currentThreadName = Thread.currentThread().getName();
System.out.println(currentThreadName+" is running!");
// System.out.println("first read age is:"+this.student.getAge());
Random random = new Random();
int age = random.nextInt(100);
System.out.println("thread "+currentThreadName +" set age to:"+age);
this.student.setAge(age);
System.out.println("thread "+currentThreadName+" first read age is:"+this.student.getAge());
try {
Thread.sleep(5000);
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
}
}
运行这个程序,屏幕输出如下:
a is running!
b is running!
thread b set age to:33
thread b first read age is:33
thread a set age to:81
thread a first read age is:81
thread b second read age is:81
thread a second read age is:81
需要注意的是,线程a在同一个方法中,第一次读取student的age值与第二次读取值不一致。这就是出现了并发问题。
synchronized
上面的例子,我们模似了一个并发问题。Java提供了同步机制来解决并发问题。synchonzied关键字可以用来同步变量,方法,甚至同步一个代码块。
使用了同步后,一个线程正在访问同步对象时,另外一个线程必须等待。
Synchronized同步方法
现在我们可以对accessStudent方法实施同步。
public synchronized void accessStudent()
再次运行程序,屏幕输出如下:
a is running!
thread a set age to:49
thread a first read age is:49
thread a second read age is:49
b is running!
thread b set age to:17
thread b first read age is:17
thread b second read age is:17
加上了同步后,线程b必须等待线程a执行完毕后,线程b才开始执行。
对方法进行同步的代价是非常昂贵的。特别是当被同步的方法执行一个冗长的操作。这个方法执行会花费很长的时间,对这样的方法进行同步可能会使系统性能成数量级的下降。
Synchronized同步块
在accessStudent方法中,我们真实需要保护的是student变量,所以我们可以进行一个更细粒度的加锁。我们仅仅对student相关的代码块进行同步。
Java 代码
1. synchronized(this) {
2. Random random = new Random();
3. int age = random.nextInt(100);
4. System.out.println("thread "+currentThreadName +" set age to:"+age);
5.
6. this.student.setAge(age);
7.
8. System.out.println("thread "+currentThreadName+" first read age is:"+this.student.getAge());
9. try {
10. Thread.sleep(5000);
11. }
12. catch(InterruptedException ex) {
13. ex.printStackTrace();
14. }
15. }
synchronized(this) {
Random random = new Random();
int age = random.nextInt(100);
System.out.println("thread "+currentThreadName +" set age to:"+age);
this.student.setAge(age);
System.out.println("thread "+currentThreadName+" first read age is:"+this.student.getAge());
try {
Thread.sleep(5000);
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
}
运行方法后,屏幕输出:
a is running!
thread a set age to:18
thread a first read age is:18
b is running!
thread a second read age is:18
thread b set age to:62
thread b first read age is:62
thread b second read age is:62
需要特别注意这个输出结果。
这个执行过程比上面的方法同步要快得多了。
只有对student进行访问的代码是同步的,而其它与部份代码却是异步的了。而student的值并没有被错误的修改。如果是在一个真实的系统中,accessStudent方法的操作又比较耗时的情况下。使用同步的速度几乎与没有同步一样快。
使用同步锁
稍微把上面的例子改一下,在ThreadDemo中有一个私有变量count,。
private int count=0;
在accessStudent()中, 线程每访问一次,count都自加一次, 用来记数线程访问的次数。
Java 代码
1. try {
2. this.count++;
3. Thread.sleep(5000);
4. }catch(InterruptedException ex) {
5. ex.printStackTrace();
6. }
try {
this.count++;
Thread.sleep(5000);
}catch(InterruptedException ex) {
ex.printStackTrace();
}
为了模拟线程,所以让它每次自加后都睡眠5秒。
accessStuden()方法的完整代码如下:
Java 代码
1. String currentThreadName = Thread.currentThread().getName();
2. System.out.println(currentThreadName+" is running!");
3. try {
4. this.count++;
5. Thread.sleep(5000);
6. }catch(InterruptedException ex) {
7. ex.printStackTrace();
8. }
9. System.out.println("thread "+currentThreadName+" read count:"+this.count);
10.
11.
12. synchronized(this) {
13. Random random = new Random();
14. int age = random.nextInt(100);
15. System.out.println("thread "+currentThreadName +" set age to:"+age);
16.
17. this.student.setAge(age);
18.
19. System.out.println("thread "+currentThreadName+" first read age is:"+this.student.getAge());
20. try {
21. Thread.sleep(5000);
22. }
23. catch(InterruptedException ex) {
24. ex.printStackTrace();
25. }
26. }
27. System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
String currentThreadName = Thread.currentThread().getName();
System.out.println(currentThreadName+" is running!");
try {
this.count++;
Thread.sleep(5000);
}catch(InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("thread "+currentThreadName+" read count:"+this.count);
synchronized(this) {
Random random = new Random();
int age = random.nextInt(100);
System.out.println("thread "+currentThreadName +" set age to:"+age);
this.student.setAge(age);
System.out.println("thread "+currentThreadName+" first read age is:"+this.student.getAge());
try {
Thread.sleep(5000);
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
}
System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
运行程序后,屏幕输出:
a is running!
b is running!
thread a read count:2
thread a set age to:49
thread a first read age is:49
thread b read count:2
thread a second read age is:49
thread b set age to:7
thread b first read age is:7
thread b second read age is:7
我们仍然对student对象以synchronized(this)操作进行同步。
我们需要在两个线程中共享count失败。
所以仍然需要对count的访问进行同步操作。
Java 代码
1. synchronized(this) {
2. try {
3. this.count++;
4. Thread.sleep(5000);
5. }catch(InterruptedException ex) {
6. ex.printStackTrace();
7. }
8. }
9. System.out.println("thread "+currentThreadName+" read count:"+this.count);
10.
11.
12. synchronized(this) {
13. Random random = new Random();
14. int age = random.nextInt(100);
15. System.out.println("thread "+currentThreadName +" set age to:"+age);
16.
17. this.student.setAge(age);
18.
19. System.out.println("thread "+currentThreadName+" first read age is:"+this.student.getAge());
20. try {
21. Thread.sleep(5000);
22. }
23. catch(InterruptedException ex) {
24. ex.printStackTrace();
25. }
26. }
27. System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
28. long endTime = System.currentTimeMillis();
29. long spendTime = endTime - startTime;
30. System.out.println("花费时间:"+spendTime +"毫秒");
synchronized(this) {
try {
this.count++;
Thread.sleep(5000);
}catch(InterruptedException ex) {
ex.printStackTrace();
}
}
System.out.println("thread "+currentThreadName+" read count:"+this.count);
synchronized(this) {
Random random = new Random();
int age = random.nextInt(100);
System.out.println("thread "+currentThreadName +" set age to:"+age);
this.student.setAge(age);
System.out.println("thread "+currentThreadName+" first read age is:"+this.student.getAge());
try {
Thread.sleep(5000);
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
}
System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
long endTime = System.currentTimeMillis();
long spendTime = endTime - startTime;
System.out.println("花费时间:"+spendTime +"毫秒");
程序运行后,屏幕输出
a is running!
b is running!
thread a read count:1
thread a set age to:97
thread a first read age is:97
thread a second read age is:97
花费时间:10015毫秒
thread b read count:2
thread b set age to:47
thread b first read age is:47
thread b second read age is:47
花费时间:20124毫秒
我们在同一个方法中,多次使用synchronized(this)进行加锁。有可能会导致太多额外的等待。
应该使用不同的对象锁进行同步。
设置两个锁对象,分别用于student和count的访问加锁。
Java 代码
1. private Object studentLock = new Object();
2. private Object countLock = new Object();
3.
4. accessStudent() 方法如下:
5. long startTime = System.currentTimeMillis();
6. String currentThreadName = Thread.currentThread().getName();
7. System.out.println(currentThreadName+" is running!");
8. // System.out.println("first read age is:"+this.student.getAge());
9.
10. synchronized(countLock) {
11. try {
12. this.count++;
13. Thread.sleep(5000);
14. }catch(InterruptedException ex) {
15. ex.printStackTrace();
16. }
17. }
18. System.out.println("thread "+currentThreadName+" read count:"+this.count);
19.
20.
21. synchronized(studentLock) {
22. Random random = new Random();
23. int age = random.nextInt(100);
24. System.out.println("thread "+currentThreadName +" set age to:"+age);
25.
26. this.student.setAge(age);
27.
28. System.out.println("thread "+currentThreadName+" first read age is:"+this.student.getAge());
29. try {
30. Thread.sleep(5000);
31. }
32. catch(InterruptedException ex) {
33. ex.printStackTrace();
34. }
35. }
36. System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
37. long endTime = System.currentTimeMillis();
38. long spendTime = endTime - startTime;
39. System.out.println("花费时间:"+spendTime +"毫秒");
private Object studentLock = new Object();
private Object countLock = new Object();
accessStudent()方法如下:
long startTime = System.currentTimeMillis();
String currentThreadName = Thread.currentThread().getName();
System.out.println(currentThreadName+" is running!");
// System.out.println("first read age is:"+this.student.getAge());
synchronized(countLock) {
try {
this.count++;
Thread.sleep(5000);
}catch(InterruptedException ex) {
ex.printStackTrace();
}
}
System.out.println("thread "+currentThreadName+" read count:"+this.count);
synchronized(studentLock) {
Random random = new Random();
int age = random.nextInt(100);
System.out.println("thread "+currentThreadName +" set age to:"+age);
this.student.setAge(age);
System.out.println("thread "+currentThreadName+" first read age is:"+this.student.getAge());
try {
Thread.sleep(5000);
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
}
System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
long endTime = System.currentTimeMillis();
long spendTime = endTime - startTime;
System.out.println("花费时间:"+spendTime +"毫秒");
这样对count和student加上了两把不同的锁。
运行程序后,屏幕输出:
a is running!
b is running!
thread a read count:1
thread a set age to:48
thread a first read age is:48
thread a second read age is:48
花费时间:10016毫秒
thread b read count:2
thread b set age to:68
thread b first read age is:68
thread b second read age is:68
花费时间:20046毫秒
与两次使用synchronized(this)相比,使用不同的对象锁,在性能上可以得到更大的提升。
由此可见synchronized是实现java的同步机制。同步机制是为了实现同步多线程对相同资源的并发访问控制。保证多线程之间的通信。
可见,同步的主要目的是保证多线程间的数据共享。同步会带来巨大的性能开销,所以同步操作应该是细粒度的。如果同步使用得当,带来的性能开销是微不足道的。使用同步真正的风险是复杂性和可能破坏资源安全,而不是性能。
ThreadLocal
由上面可以知道,使用同步是非常复杂的。并且同步会带来性能的降低。Java提供了另外的一种方式,通过ThreadLocal可以很容易的编写多线程程序。从字面上理解,很容易会把ThreadLocal误解为一个线程的本地变量。其它ThreadLocal并不是代表当前线程,ThreadLocal 其实是采用哈希表的方式来为每个线程都提供一个变量的副本。从而保证各个线程间数据安全。每个线程的数据不会被另外线程访问和破坏。
我们把第一个例子用ThreadLocal来实现,但是我们需要些许改变。
Student并不是一个私有变量了,而是需要封装在一个ThreadLocal对象中去。调用ThreadLocal的set方法,ThreadLocal会为每一个线程都保持一份Student变量的副本。所以对student的读取操作都是通过ThreadLocal来进行的。
Java 代码
1. protected Student getStudent() {
2. Student student = (Student)studentLocal.get();
3. if(student == null) {
4. student = new Student();
5. studentLocal.set(student);
6. }
7. return student;
8. }
9.
10. protected void setStudent(Student student) {
11. studentLocal.set(student);
12. }
protected Student getStudent() {
Student student = (Student)studentLocal.get();
if(student == null) {
student = new Student();
studentLocal.set(student);
}
return student;
}
protected void setStudent(Student student) {
studentLocal.set(student);
}
accessStudent()方法需要做一些改变。通过调用getStudent()方法来获得当前线程的Student变量,如果当前线程不存在一个Student变量,getStudent方法会创建一个新的Student变量,并设置在当前线程中。
Student student = getStudent();
student.setAge(age);
accessStudent()方法中无需要任何同步代码。
完整的代码清单如下:
TreadLocalDemo.java
Java 代码
1. public class TreadLocalDemo implements Runnable {
2. private final static ThreadLocal studentLocal = new ThreadLocal();
3.
4. public static void main(String[] agrs) {
5. TreadLocalDemo td = new TreadLocalDemo();
6. Thread t1 = new Thread(td,"a");
7. Thread t2 = new Thread(td,"b");
8.
9. t1.start();
10. t2.start();
11.
12.
13.
14.
15. }
16.
17. /* (non-Javadoc)
18. * @see java.lang.Runnable#run()
19. */
20. public void run() {
21. accessStudent();
22. }
23.
24. public void accessStudent() {
25.
26. String currentThreadName = Thread.currentThread().getName();
27. System.out.println(currentThreadName+" is running!");
28. Random random = new Random();
29. int age = random.nextInt(100);
30. System.out.println("thread "+currentThreadName +" set age to:"+age);
31. Student student = getStudent();
32. student.setAge(age);
33. System.out.println("thread "+currentThreadName+" first read age is:"+student.getAge());
34. try {
35. Thread.sleep(5000);
36. }
37. catch(InterruptedException ex) {
38. ex.printStackTrace();
39. }
40. System.out.println("thread "+currentThreadName +" second read age is:"+student.getAge());
41.
42. }
43.
44. protected Student getStudent() {
45. Student student = (Student)studentLocal.get();
46. if(student == null) {
47. student = new Student();
48. studentLocal.set(student);
49. }
50. return student;
51. }
52.
53. protected void setStudent(Student student) {
54. studentLocal.set(student);
55. }
56. }
public class TreadLocalDemo implements Runnable {
private final static ThreadLocal studentLocal = new ThreadLocal();
public static void main(String[] agrs) {
TreadLocalDemo td = new TreadLocalDemo();
Thread t1 = new Thread(td,"a");
Thread t2 = new Thread(td,"b");
t1.start();
t2.start();
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public void run() {
accessStudent();
}
public void accessStudent() {
String currentThreadName = Thread.currentThread().getName();
System.out.println(currentThreadName+" is running!");
Random random = new Random();
int age = random.nextInt(100);
System.out.println("thread "+currentThreadName +" set age to:"+age);
Student student = getStudent();
student.setAge(age);
System.out.println("thread "+currentThreadName+" first read age is:"+student.getAge());
try {
Thread.sleep(5000);
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("thread "+currentThreadName +" second read age is:"+student.getAge());
}
protected Student getStudent() {
Student student = (Student)studentLocal.get();
if(student == null) {
student = new Student();
studentLocal.set(student);
}
return student;
}
protected void setStudent(Student student) {
studentLocal.set(student);
}
}
运行程序后,屏幕输出:
b is running!
thread b set age to:0
thread b first read age is:0
a is running!
thread a set age to:17
thread a first read age is:17
thread b second read age is:0
thread a second read age is:17
可见,使用ThreadLocal后,我们不需要任何同步代码,却能够保证我们线程间数据的安全。
而且,ThreadLocal的使用也非常的简单。
我们仅仅需要使用它提供的两个方法
void set(Object obj) 设置当前线程的变量的副本的值。
Object get() 返回当前线程的变量副本
另外ThreadLocal还有一个protected的initialValue()方法。返回变量副本在当前线程的初始值。默认为null
ThreadLocal是怎么做到为每个线程都维护一个变量的副本的呢?
我们可以猜测到ThreadLocal的一个简单实现
Java 代码
1. public class ThreadLocal
2. {
3. private Map values = Collections.synchronizedMap(new HashMap());
4. public Object get()
5. {
6. Thread curThread = Thread.currentThread();
7. Object o = values.get(curThread);
8. if (o == null && !values.containsKey(curThread))
9. {
10. o = initialValue();
11. values.put(curThread, o);
12. }
13. return o;
14. }
15.
16. public void set(Object newValue)
17. {
18. values.put(Thread.currentThread(), newValue);
19. }
20.
21. public Object initialValue()
22. {
23. return null;
24. }
25. }
public class ThreadLocal
{
private Map values = Collections.synchronizedMap(new HashMap());
public Object get()
{
Thread curThread = Thread.currentThread();
Object o = values.get(curThread);
if (o == null && !values.containsKey(curThread))
{
o = initialValue();
values.put(curThread, o);
}
return o;
}
public void set(Object newValue)
{
values.put(Thread.currentThread(), newValue);
}
public Object initialValue()
{
return null;
}
}
由此可见,ThreadLocal通过一个Map来为每个线程都持有一个变量副本。这个map以当前线程为key。与synchronized相比,ThreadLocal是以空间换时间的策略来实现多线程程序。
Synchronized还是ThreadLocal?
ThreadLocal以空间换取时间,提供了一种非常简便的多线程实现方式。因为多个线程并发访问无需进行等待,所以使用 ThreadLocal会获得更大的性能。虽然使用ThreadLocal会带来更多的内存开销,但这点开销是微不足道的。因为保存在 ThreadLocal中的对象,通常都是比较小的对象。另外使用ThreadLocal不能使用原子类型,只能使用Object类型。 ThreadLocal的使用比synchronized要简单得多。
ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。
Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比 ThreadLocal更加复杂。
发表评论
-
Spring MVC集成velocity扩展
2013-07-23 17:18 32571、扩展velocity的视图 [code=" ... -
Java获取客户端信息
2011-09-07 14:48 1650String agent = request.getHeade ... -
获取IP地址
2011-09-07 13:41 2422public String getIpAddrByReques ... -
netty telnet 应用实例server代码
2011-09-07 12:21 1882public class TelnetServer { ... -
Netty中使用Apache Common FileUpload
2011-09-07 12:19 1277/** * 用Netty来实现上传 */ publi ... -
java管理windows进程
2011-08-29 17:34 1780package org.zzuli.xmsb; /** ... -
java反射工具
2011-08-29 17:30 5444package org.liufei.jweb.reflect ... -
java html工具
2011-08-29 17:26 1085package org.liufei.jweb.util; ... -
java将汉字转化为全拼
2011-08-29 17:24 1233package org.liufei.jweb.util; ... -
JSTL API
2011-08-29 15:13 1783JSTL API -
jdbc操作大观园
2011-08-09 17:22 1385最近公司使用jdbc和mybatis比较多,于是自己试着写了一 ... -
Java处理UTF-8带BOM的文本的读写
2011-08-01 11:28 2999什么是BOM BOM(byte-order mark),即字 ... -
Session和Cookie的区别
2011-06-27 16:34 8781、session保存在服务器,客户端不知道其中的信息;coo ... -
ajax应用时html响应生成工具
2011-05-02 19:00 1135package org.zzuli.xmsb.util; ... -
setTimeout和setInterval的使用
2011-05-01 16:00 1006这两个方法都可以用来 ... -
javasript 经典技巧
2011-03-04 21:30 14581. oncontextmenu="window.e ... -
javascript窗口
2011-03-04 16:31 1037【1、最基本的弹出窗口 ... -
get and post
2011-01-07 17:22 9981. get 是从服务器上获取数据,post 是向服务器传送数 ... -
web开发人员必学的五堂课
2010-12-20 14:42 989越来越多的Web开发人员 ... -
spring整合MyBatis
2010-11-21 15:08 10560MyBatis Spring 1.0.0-RC2 参考文档 M ...
相关推荐
### Synchronized与ThreadLocal #### 一、Synchronized机制详解 **Synchronized** 是 Java 中一个非常重要的关键字,主要用于实现线程同步。它通过在对象上加锁来确保多个线程能够安全地访问共享资源。 - **作用...
ThreadLocal通常被用来解决线程共享数据时可能出现的并发问题,避免了使用synchronized关键字进行同步控制的复杂性。 在Java中,ThreadLocal的工作原理是为每个线程创建一个单独的存储空间,每个线程可以独立地读写...
ThreadLocal的生命周期与创建它的线程紧密相关。一旦线程结束,其ThreadLocal变量及其存储的值也会自动被清理。但是,如果线程持续存在且不调用`remove()`,ThreadLocal变量可能会导致内存泄漏,因为它们占用的内存...
ThreadLocal一般称为线程本地变量,它是一种特殊的线程绑定机制,将变量与线程绑定在一起,为每一个线程维护一个独立的变量副本。通过ThreadLocal可以将对象的可见范围限制在同一个线程内。 跳出误区 需要...
总结来说,Java中的临界区通过`synchronized`关键字或Lock对象来保证多线程环境下的数据一致性,而ThreadLocal则通过为每个线程提供变量的独立副本,避免了共享资源的并发问题。在实际编程中,开发者需要根据具体...
private Map valueMap=Collections.synchronizedMap(new HashMap()); public void set(Object newValue){ valueMap.put(Thread.currentThread(),newValue);//键为线程对象,值为本线程的变量副本 } public ...
- 在面试中,可能会遇到关于 `ThreadLocal` 生命周期管理、内存泄漏、与 `synchronized` 的比较以及在实际应用中的场景分析等问题。 通过以上介绍,我们可以了解到 `ThreadLocal` 在处理多线程环境中提供了独特的...
与传统的使用`synchronized`关键字或`Lock`接口来实现线程同步不同,ThreadLocal提供了另一种解决线程安全问题的思路——即为每个线程创建独立的变量副本,避免了线程间的变量共享所带来的同步问题,从而提高了程序...
`ThreadLocal`类内部维护了一个`ThreadLocalMap`结构,该结构存储了线程与线程局部变量之间的映射关系。每当一个新的线程创建并首次访问某个`ThreadLocal`实例时,都会在该线程的`ThreadLocalMap`中添加一个新的键值...
传统的线程同步,如使用`synchronized`关键字,会使得多个线程在访问共享资源时需要排队执行,确保了线程安全,但牺牲了并发性能。而ThreadLocal则通过为每个线程创建单独的变量副本,消除了并发冲突,提升了程序的...
当我们创建一个ThreadLocal实例并调用其set方法时,实际上是将值与当前线程关联起来。每个线程都有一个ThreadLocalMap,这是ThreadLocal内部的一个静态内部类,用来存储ThreadLocal对象和它们对应的值。 在...
Java面试中,ThreadLocal和Synchronized是经常被讨论的话题,它们是Java并发编程中的关键概念。ThreadLocal,顾名思义,线程局部变量,它为每个线程提供了一个独立的变量副本,使得每个线程都可以独立地改变自己的...
Java 非线程安全类变线程安全类 Java 中的非线程安全类是指有状态的类,即有属性的类,这些类在多线程...Java 中的非线程安全类可以通过使用 ThreadLocal 或 synchronized 关键字来实现线程安全,避免线程安全问题。
传统的解决方案包括使用`Atomic`类、`volatile`关键字以及`synchronized`关键字来保证多线程环境下的数据一致性。然而,这些同步机制并不总是最优解,特别是在需要线程内共享变量且避免线程间干扰的情况下。此时,`...
Java ThreadLocal使用案例详解 Java ThreadLocal是Java语言中的一种机制,用于为每个线程提供一个独立的变量副本,以解决多线程环境下共享变量的线程安全问题。在本文中,我们将详细介绍Java ThreadLocal的使用案例...
在 Java 中,ThreadLocal 实例通常是 private static 字段,位于希望与线程关联状态的类中。例如,在 SerialNum 类中,我们定义了一个私有静态的 ThreadLocal 实例(serialNum),用于维护每个线程的序列号。 ```...