- 浏览: 184599 次
- 性别:
- 来自: 深圳
最新评论
-
mengfei86:
你们讨论的时候我刚上大学,。。。。、、现在都过去好多年了,。 ...
J2EE项目异常处理 -
di1984HIT:
文章不错,学习了
Ibatis读写CLOB数据 -
wulixiaodao:
main{
metodA();
}
详解spring事务属性 -
wulixiaodao:
Main{
Connection con=null;
...
详解spring事务属性 -
tao_gun:
感谢,有点懂了
详解spring事务属性
ThreadLocal与synchronized
Java良好的支持多线程。使用java,我们可以很轻松的编程一个多线程程序。但是使用多线程可能会引起并发访问的问题。synchronized和ThreadLocal都是用来解决多线程并发访问的问题。大家可能对synchronized较为熟悉,而对ThreadLocal就要陌生得多了。
并发问题。当一个对象被两个线程同时访问时,可能有一个线程会得到不可预期的结果。
一个简单的java类Studnet
一个多线程类ThreadDemo.
这个类有一个Student的私有变量,在run方法中,它随机产生一个整数。然后设置到student变量中,从student中读取设置后的值。然后睡眠5秒钟,最后再次读student的age值。
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相关的代码块进行同步。
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都自加一次, 用来记数线程访问的次数。
accessStuden()方法的完整代码如下:
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的访问进行同步操作。
程序运行后,屏幕输出
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的访问加锁。
这样对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来进行的。
accessStudent()方法需要做一些改变。通过调用getStudent()方法来获得当前线程的Student变量,如果当前线程不存在一个Student变量,getStudent方法会创建一个新的Student变量,并设置在当前线程中。
Student student = getStudent();
student.setAge(age);
accessStudent()方法中无需要任何同步代码。
完整的代码清单如下:
TreadLocalDemo.java
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的一个简单实现
由此可见,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更加复杂。
这里 ”变量的副本“ 的意思, 我想应该是: 在程序里面看起来是同一个变量(使用的是相同的变量名), 但是实际上各个线程使用的是不用的实例.
lz的文章并没有去讨论ThreadLocal就是ThreadLocal有什么关系,只是在说如何保证多线程环境下对共享资源访问的数据安全。文章看完了,简单明了,很到位,是篇好文章。
麻烦你解释一下,ThreadLocal是怎么“保证多线程环境下对共享资源访问的数据安全”的?ThreadLocal使各个线程使用各自不同的数据,这种情况下哪来的“共享资源”?
lz的文章并没有去讨论ThreadLocal就是ThreadLocal有什么关系,只是在说如何保证多线程环境下对共享资源访问的数据安全。文章看完了,简单明了,很到位,是篇好文章。
说句没有意义的话,事实上。ThreadLocal实现中,作为key的不是线程,而是ThreadLocal本身。
从整体上来看好吧。
不要看到ThreadLocalMap的key是ThreadLocal,就认为寻找线程本地变量实例的key就是ThreadLocal了。
要知道每个线程都会持有一个ThreadLocalMap的实例,我说的key就是这个意思。
key只是找到目标的钥匙,不是只有map才有key。
你的话也有道理,但是,说到这个细节,我还是要说一下,ThreadLocal要比我们简单的想象复杂的多,不单是一个变量存储,它的数据方Thread里面,很明显,在线程销毁时的清理任务。
可以说ThreadLocal是个系统级的API,比我们想象的要复杂一点。
其实用来区别线程间实例的key是线程本身,用来区分线程内部实例的key是ThreadLocal,因为每个线程不止一个内部实例。至于清理工作,那就是线程退出时清除对ThreadLocalMap引用,自然也就释放了实例。
你说的复杂指的是?
说句没有意义的话,事实上。ThreadLocal实现中,作为key的不是线程,而是ThreadLocal本身。
从整体上来看好吧。
不要看到ThreadLocalMap的key是ThreadLocal,就认为寻找线程本地变量实例的key就是ThreadLocal了。
要知道每个线程都会持有一个ThreadLocalMap的实例,我说的key就是这个意思。
key只是找到目标的钥匙,不是只有map才有key。
你的话也有道理,但是,说到这个细节,我还是要说一下,ThreadLocal要比我们简单的想象复杂的多,不单是一个变量存储,它的数据方Thread里面,很明显,在线程销毁时的清理任务。
可以说ThreadLocal是个系统级的API,比我们想象的要复杂一点。
说句没有意义的话,事实上。ThreadLocal实现中,作为key的不是线程,而是ThreadLocal本身。
从整体上来看好吧。
不要看到ThreadLocalMap的key是ThreadLocal,就认为寻找线程本地变量实例的key就是ThreadLocal了。
要知道每个线程都会持有一个ThreadLocalMap的实例,我说的key就是这个意思。
key只是找到目标的钥匙,不是只有map才有key。
我想就没必要咬文嚼字了。相信你说的这些概念大家都明白。
或许楼主用词不准确,但是相信大家还是能够分辨。例子可能也不是很好,这里主要是为了展示一个现象。
synchronized, ThreadLocal是可以用来解决某些类似问题的。关键在于如何设计。当能也不是完全可替换的方案。还在于程序员自己去取舍。
说句没有意义的话,事实上。ThreadLocal实现中,作为key的不是线程,而是ThreadLocal本身。
服务器一般都有线程池,线程资源可以重复利用的,你2000个用户在线,不见得能又200个用户同时访问,再说,只要对象不是太大,我宁愿用200个拷贝,也不想让用户在这个200个任务的队列里等待。
ThreadLocal在线程内共享资源,是个不错的方法。ww就是用这个存储请求信息,一下子就明朗多了,不像struts1那样参数一堆一堆的。
投了个良好,平衡一下:)
呵呵 楼主说的是多线程中解决 资源共享的问题 sync 和 threadlocal 是两种思路 一个是把单一资源加锁 一种是把资源做成副本。
可以申诉一下,这个帖子好像不应该被投新手。
Java良好的支持多线程。使用java,我们可以很轻松的编程一个多线程程序。但是使用多线程可能会引起并发访问的问题。synchronized和ThreadLocal都是用来解决多线程并发访问的问题。大家可能对synchronized较为熟悉,而对ThreadLocal就要陌生得多了。
并发问题。当一个对象被两个线程同时访问时,可能有一个线程会得到不可预期的结果。
一个简单的java类Studnet
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值。
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相关的代码块进行同步。
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都自加一次, 用来记数线程访问的次数。
try { this.count++; Thread.sleep(5000); }catch(InterruptedException ex) { ex.printStackTrace(); }为了模拟线程,所以让它每次自加后都睡眠5秒。
accessStuden()方法的完整代码如下:
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的访问进行同步操作。
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的访问加锁。
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来进行的。
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
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的一个简单实现
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更加复杂。
评论
31 楼
glassprogrammer
2007-05-25
BirdGu 写道
首先请楼主明确,你想说的是java.lang.ThreadLocal的实现和使用,还是你自己写了一个ThreadLocal类?
如果是说java.lang.ThreadLocal,那么比如我们使用java.lang.ThreadLocal保存数据库连接java.sql.Connection对象,以使每个线程对数据库的访问不会混杂。此时每个线程中的Connection对象又是什么变量的副本呢?
在Java语言中”变量的副本“这个概念本身就有点含义不清。Java中所有的变量都是对对象的引用,那么”变量的副本“到底是指”被引用的对象的副本“还是”引用本身的副本“呢?
例子程序中,每个线程拿到的都是不同的Student对象,此时根本不存在并发访问的问题。就算不是用new Student,而是用Student.clone,拿到同一个对象的拷贝,这样某个线程对这个拷贝的修改别的线程是看不到的。ThreadLocal可以使多个线程读取同一个对象(或拷贝),但不能使多个线程修改同一个对象。而如果多个线程只是同时读一个对象的情况,本来就不需要用synchronized。
所以,能够用ThreadLocal的情况,不需要synchronized。需要用synchronized, ThreadLocal也用不上。这两个并不是互相替代的关系。
如果是说java.lang.ThreadLocal,那么比如我们使用java.lang.ThreadLocal保存数据库连接java.sql.Connection对象,以使每个线程对数据库的访问不会混杂。此时每个线程中的Connection对象又是什么变量的副本呢?
在Java语言中”变量的副本“这个概念本身就有点含义不清。Java中所有的变量都是对对象的引用,那么”变量的副本“到底是指”被引用的对象的副本“还是”引用本身的副本“呢?
例子程序中,每个线程拿到的都是不同的Student对象,此时根本不存在并发访问的问题。就算不是用new Student,而是用Student.clone,拿到同一个对象的拷贝,这样某个线程对这个拷贝的修改别的线程是看不到的。ThreadLocal可以使多个线程读取同一个对象(或拷贝),但不能使多个线程修改同一个对象。而如果多个线程只是同时读一个对象的情况,本来就不需要用synchronized。
所以,能够用ThreadLocal的情况,不需要synchronized。需要用synchronized, ThreadLocal也用不上。这两个并不是互相替代的关系。
这里 ”变量的副本“ 的意思, 我想应该是: 在程序里面看起来是同一个变量(使用的是相同的变量名), 但是实际上各个线程使用的是不用的实例.
30 楼
Godlikeme
2007-05-25
我觉得这个帖子是在没有什么价值,threadlocal基本概念搞得不清楚,在加上一个根本是在误导的例子。什么共享、同步全都是瞎掰。
29 楼
shaucle
2007-05-25
楼主申诉后俺才评的新手帖.
不过没想...
不过没想...
28 楼
BirdGu
2007-05-25
spiritfrog 写道
XMLDB 写道
LZ把概念搞错了,ThreadLocal就是ThreadLocal,和同步完全没有关系。
ThreadLocal解决的是同一个线程内的资源共享问题,而synchronized 解决的是多个线程间的资源共享问题,两个问题没有可比性。
ThreadLocal解决的是同一个线程内的资源共享问题,而synchronized 解决的是多个线程间的资源共享问题,两个问题没有可比性。
lz的文章并没有去讨论ThreadLocal就是ThreadLocal有什么关系,只是在说如何保证多线程环境下对共享资源访问的数据安全。文章看完了,简单明了,很到位,是篇好文章。
麻烦你解释一下,ThreadLocal是怎么“保证多线程环境下对共享资源访问的数据安全”的?ThreadLocal使各个线程使用各自不同的数据,这种情况下哪来的“共享资源”?
27 楼
diandidemeng
2007-05-25
不管怎么样,楼主还是蛮努力的,辛苦了
26 楼
spiritfrog
2007-05-25
XMLDB 写道
LZ把概念搞错了,ThreadLocal就是ThreadLocal,和同步完全没有关系。
ThreadLocal解决的是同一个线程内的资源共享问题,而synchronized 解决的是多个线程间的资源共享问题,两个问题没有可比性。
ThreadLocal解决的是同一个线程内的资源共享问题,而synchronized 解决的是多个线程间的资源共享问题,两个问题没有可比性。
lz的文章并没有去讨论ThreadLocal就是ThreadLocal有什么关系,只是在说如何保证多线程环境下对共享资源访问的数据安全。文章看完了,简单明了,很到位,是篇好文章。
25 楼
阳光晒晒
2007-05-24
汗。。。如果有其它的建议先看看别的。。
这翩文章很有争议性。
这翩文章很有争议性。
24 楼
那年夏天
2007-05-24
终于找到详细的ThreadLocal介绍了
23 楼
Godlikeme
2007-05-24
糟糕的理解,糟糕的例子,基本同意birdGu的意见,不配评良好。
22 楼
weiqingfei
2007-05-24
jindw 写道
weiqingfei 写道
jindw 写道
weiqingfei 写道
其实两个本来就没有关系,我想楼主只是想说如何在多线程中活用ThreadLocal。
ThreadLocal的实现本来就比较简单,只是用线程来作为key来寻找本线程中所使用的一个实例,它解决的最主要的问题应该就是减少参数的传递。
ThreadLocal的实现本来就比较简单,只是用线程来作为key来寻找本线程中所使用的一个实例,它解决的最主要的问题应该就是减少参数的传递。
说句没有意义的话,事实上。ThreadLocal实现中,作为key的不是线程,而是ThreadLocal本身。
从整体上来看好吧。
不要看到ThreadLocalMap的key是ThreadLocal,就认为寻找线程本地变量实例的key就是ThreadLocal了。
要知道每个线程都会持有一个ThreadLocalMap的实例,我说的key就是这个意思。
key只是找到目标的钥匙,不是只有map才有key。
你的话也有道理,但是,说到这个细节,我还是要说一下,ThreadLocal要比我们简单的想象复杂的多,不单是一个变量存储,它的数据方Thread里面,很明显,在线程销毁时的清理任务。
可以说ThreadLocal是个系统级的API,比我们想象的要复杂一点。
其实用来区别线程间实例的key是线程本身,用来区分线程内部实例的key是ThreadLocal,因为每个线程不止一个内部实例。至于清理工作,那就是线程退出时清除对ThreadLocalMap引用,自然也就释放了实例。
你说的复杂指的是?
21 楼
jindw
2007-05-24
weiqingfei 写道
jindw 写道
weiqingfei 写道
其实两个本来就没有关系,我想楼主只是想说如何在多线程中活用ThreadLocal。
ThreadLocal的实现本来就比较简单,只是用线程来作为key来寻找本线程中所使用的一个实例,它解决的最主要的问题应该就是减少参数的传递。
ThreadLocal的实现本来就比较简单,只是用线程来作为key来寻找本线程中所使用的一个实例,它解决的最主要的问题应该就是减少参数的传递。
说句没有意义的话,事实上。ThreadLocal实现中,作为key的不是线程,而是ThreadLocal本身。
从整体上来看好吧。
不要看到ThreadLocalMap的key是ThreadLocal,就认为寻找线程本地变量实例的key就是ThreadLocal了。
要知道每个线程都会持有一个ThreadLocalMap的实例,我说的key就是这个意思。
key只是找到目标的钥匙,不是只有map才有key。
你的话也有道理,但是,说到这个细节,我还是要说一下,ThreadLocal要比我们简单的想象复杂的多,不单是一个变量存储,它的数据方Thread里面,很明显,在线程销毁时的清理任务。
可以说ThreadLocal是个系统级的API,比我们想象的要复杂一点。
20 楼
weiqingfei
2007-05-24
jindw 写道
weiqingfei 写道
其实两个本来就没有关系,我想楼主只是想说如何在多线程中活用ThreadLocal。
ThreadLocal的实现本来就比较简单,只是用线程来作为key来寻找本线程中所使用的一个实例,它解决的最主要的问题应该就是减少参数的传递。
ThreadLocal的实现本来就比较简单,只是用线程来作为key来寻找本线程中所使用的一个实例,它解决的最主要的问题应该就是减少参数的传递。
说句没有意义的话,事实上。ThreadLocal实现中,作为key的不是线程,而是ThreadLocal本身。
从整体上来看好吧。
不要看到ThreadLocalMap的key是ThreadLocal,就认为寻找线程本地变量实例的key就是ThreadLocal了。
要知道每个线程都会持有一个ThreadLocalMap的实例,我说的key就是这个意思。
key只是找到目标的钥匙,不是只有map才有key。
19 楼
jindw
2007-05-24
BirdGu 写道
首先请楼主明确,你想说的是java.lang.ThreadLocal的实现和使用,还是你自己写了一个ThreadLocal类?
如果是说java.lang.ThreadLocal,那么比如我们使用java.lang.ThreadLocal保存数据库连接java.sql.Connection对象,以使每个线程对数据库的访问不会混杂。此时每个线程中的Connection对象又是什么变量的副本呢?
在Java语言中”变量的副本“这个概念本身就有点含义不清。Java中所有的变量都是对对象的引用,那么”变量的副本“到底是指”被引用的对象的副本“还是”引用本身的副本“呢?
例子程序中,每个线程拿到的都是不同的Student对象,此时根本不存在并发访问的问题。就算不是用new Student,而是用Student.clone,拿到同一个对象的拷贝,这样某个线程对这个拷贝的修改别的线程是看不到的。ThreadLocal可以使多个线程读取同一个对象(或拷贝),但不能使多个线程修改同一个对象。而如果多个线程只是同时读一个对象的情况,本来就不需要用synchronized。
所以,能够用ThreadLocal的情况,不需要synchronized。需要用synchronized, ThreadLocal也用不上。这两个并不是互相替代的关系。
如果是说java.lang.ThreadLocal,那么比如我们使用java.lang.ThreadLocal保存数据库连接java.sql.Connection对象,以使每个线程对数据库的访问不会混杂。此时每个线程中的Connection对象又是什么变量的副本呢?
在Java语言中”变量的副本“这个概念本身就有点含义不清。Java中所有的变量都是对对象的引用,那么”变量的副本“到底是指”被引用的对象的副本“还是”引用本身的副本“呢?
例子程序中,每个线程拿到的都是不同的Student对象,此时根本不存在并发访问的问题。就算不是用new Student,而是用Student.clone,拿到同一个对象的拷贝,这样某个线程对这个拷贝的修改别的线程是看不到的。ThreadLocal可以使多个线程读取同一个对象(或拷贝),但不能使多个线程修改同一个对象。而如果多个线程只是同时读一个对象的情况,本来就不需要用synchronized。
所以,能够用ThreadLocal的情况,不需要synchronized。需要用synchronized, ThreadLocal也用不上。这两个并不是互相替代的关系。
我想就没必要咬文嚼字了。相信你说的这些概念大家都明白。
或许楼主用词不准确,但是相信大家还是能够分辨。例子可能也不是很好,这里主要是为了展示一个现象。
synchronized, ThreadLocal是可以用来解决某些类似问题的。关键在于如何设计。当能也不是完全可替换的方案。还在于程序员自己去取舍。
weiqingfei 写道
其实两个本来就没有关系,我想楼主只是想说如何在多线程中活用ThreadLocal。
ThreadLocal的实现本来就比较简单,只是用线程来作为key来寻找本线程中所使用的一个实例,它解决的最主要的问题应该就是减少参数的传递。
ThreadLocal的实现本来就比较简单,只是用线程来作为key来寻找本线程中所使用的一个实例,它解决的最主要的问题应该就是减少参数的传递。
说句没有意义的话,事实上。ThreadLocal实现中,作为key的不是线程,而是ThreadLocal本身。
18 楼
BirdGu
2007-05-24
发现这个贴被评为良好了。这文章中有不少不说错误,至少也是含糊不清的说法,
比如:
synchronized和ThreadLocal都是用来解决多线程并发访问的问题。
变量的副本
ThreadLocal和Synchonized都用于解决多线程并发访问。
可见,使用ThreadLocal后,我们不需要任何同步代码,却能够保证我们线程间数据的安全。
使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。
总之,作者一再说”ThreadLocal......用于解决多线程并发访问“。但是如果程序确实需要多线程并发访问,则不能使用ThreadLocal,而如果不需要多线程并发访问,那么ThreadLocal就是解决了一个并不存在的问题。
另外,作者说:
但是前后两个程序所实现的功能是完全不一样的。
这样的帖子就算不隐藏,也绝不应该评为良好,否则误导多多。
比如:
引用
synchronized和ThreadLocal都是用来解决多线程并发访问的问题。
变量的副本
ThreadLocal和Synchonized都用于解决多线程并发访问。
可见,使用ThreadLocal后,我们不需要任何同步代码,却能够保证我们线程间数据的安全。
使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。
总之,作者一再说”ThreadLocal......用于解决多线程并发访问“。但是如果程序确实需要多线程并发访问,则不能使用ThreadLocal,而如果不需要多线程并发访问,那么ThreadLocal就是解决了一个并不存在的问题。
另外,作者说:
引用
我们把第一个例子用ThreadLocal来实现,但是我们需要些许改变。
但是前后两个程序所实现的功能是完全不一样的。
这样的帖子就算不隐藏,也绝不应该评为良好,否则误导多多。
17 楼
weiqingfei
2007-05-24
其实两个本来就没有关系,我想楼主只是想说如何在多线程中活用ThreadLocal。
ThreadLocal的实现本来就比较简单,只是用线程来作为key来寻找本线程中所使用的一个实例,它解决的最主要的问题应该就是减少参数的传递。
ThreadLocal的实现本来就比较简单,只是用线程来作为key来寻找本线程中所使用的一个实例,它解决的最主要的问题应该就是减少参数的传递。
16 楼
BirdGu
2007-05-24
首先请楼主明确,你想说的是java.lang.ThreadLocal的实现和使用,还是你自己写了一个ThreadLocal类?
如果是说java.lang.ThreadLocal,那么比如我们使用java.lang.ThreadLocal保存数据库连接java.sql.Connection对象,以使每个线程对数据库的访问不会混杂。此时每个线程中的Connection对象又是什么变量的副本呢?
在Java语言中”变量的副本“这个概念本身就有点含义不清。Java中所有的变量都是对对象的引用,那么”变量的副本“到底是指”被引用的对象的副本“还是”引用本身的副本“呢?
例子程序中,每个线程拿到的都是不同的Student对象,此时根本不存在并发访问的问题。就算不是用new Student,而是用Student.clone,拿到同一个对象的拷贝,这样某个线程对这个拷贝的修改别的线程是看不到的。ThreadLocal可以使多个线程读取同一个对象(或拷贝),但不能使多个线程修改同一个对象。而如果多个线程只是同时读一个对象的情况,本来就不需要用synchronized。
所以,能够用ThreadLocal的情况,不需要synchronized。需要用synchronized, ThreadLocal也用不上。这两个并不是互相替代的关系。
如果是说java.lang.ThreadLocal,那么比如我们使用java.lang.ThreadLocal保存数据库连接java.sql.Connection对象,以使每个线程对数据库的访问不会混杂。此时每个线程中的Connection对象又是什么变量的副本呢?
在Java语言中”变量的副本“这个概念本身就有点含义不清。Java中所有的变量都是对对象的引用,那么”变量的副本“到底是指”被引用的对象的副本“还是”引用本身的副本“呢?
例子程序中,每个线程拿到的都是不同的Student对象,此时根本不存在并发访问的问题。就算不是用new Student,而是用Student.clone,拿到同一个对象的拷贝,这样某个线程对这个拷贝的修改别的线程是看不到的。ThreadLocal可以使多个线程读取同一个对象(或拷贝),但不能使多个线程修改同一个对象。而如果多个线程只是同时读一个对象的情况,本来就不需要用synchronized。
所以,能够用ThreadLocal的情况,不需要synchronized。需要用synchronized, ThreadLocal也用不上。这两个并不是互相替代的关系。
15 楼
jindw
2007-05-24
magice 写道
那如果当前有2000个用户登陆,那么就会有2000个变量副本吧,如果不是存放什么大的对象到是没什么问题,要是存放大的对象,该怎么办呢?
服务器一般都有线程池,线程资源可以重复利用的,你2000个用户在线,不见得能又200个用户同时访问,再说,只要对象不是太大,我宁愿用200个拷贝,也不想让用户在这个200个任务的队列里等待。
ThreadLocal在线程内共享资源,是个不错的方法。ww就是用这个存储请求信息,一下子就明朗多了,不像struts1那样参数一堆一堆的。
投了个良好,平衡一下:)
14 楼
someone
2007-05-23
感觉楼主有误导之嫌。虽然写的字很多,但是评19个新手贴也不算冤枉。
13 楼
Chamjoneu
2007-05-23
shaucle 写道
ThreadLocal是同线程的单例
TransactionSynchronizationManager(ThreadLocal)和java线程中的synchronized是两码事.
文章好长...
TransactionSynchronizationManager(ThreadLocal)和java线程中的synchronized是两码事.
文章好长...
呵呵 楼主说的是多线程中解决 资源共享的问题 sync 和 threadlocal 是两种思路 一个是把单一资源加锁 一种是把资源做成副本。
12 楼
robbin
2007-05-23
klyuan 写道
竟然被评了19个新手贴!!!没天理啊
可以申诉一下,这个帖子好像不应该被投新手。
发表评论
-
一个特殊的异常处理
2008-12-13 23:59 1404一个特殊的异常处理 文:袁光东 一、业务需求说明 前段时间接 ... -
程序员为什么不写单元测试
2007-07-04 11:31 29239程序员为什么不写单 ... -
Spring JavaConfig开发指南(下)
2007-06-03 10:56 6559... -
Spring JavaConfig开发指南(上)
2007-06-03 10:25 7788Spring JavaConfig开发指南 作者:袁光东 1. ... -
倒底该怎么写DAO的单元测试?
2007-05-17 16:17 14061public void testAddUserInfo() ... -
详解spring事务属性
2007-05-10 22:55 20459Spring声明式事务让我们从复杂的事务处理中得到解脱。使得我 ... -
Ibatis读写CLOB数据
2007-04-25 16:43 22893Ibatis是一个高效,方便,易于学习的数据访问组件,在性能上 ... -
细说框架风云 JSF能否拯救WEB江湖
2007-04-24 18:08 2084细说框架风云 JSF能否拯救WEB江湖 Java ... -
模板方法模式实现探讨
2007-04-23 18:30 4433模板方法(Template Method) ... -
Spring架构设计-增强MultiActionController
2007-04-20 12:04 4839Spring架构设计-增强MultiActionControl ... -
让Spring架构减化事务配置
2007-04-19 12:20 4694让Spring架构减化事务配置 注:原创文章,本文曾发表于it ... -
J2EE项目异常处理
2007-04-18 12:19 16423J2EE项目异常处理 ...
相关推荐
### 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),用于维护每个线程的序列号。 ```...