一、概述
二、API说明
ThreadLocal()
T get()
protected T initialValue()
void remove()
void set(T value)
三、典型实例
1、Hiberante的Session 工具类HibernateUtil
try {
// 通过默认配置文件hibernate.cfg.xml创建SessionFactory
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
log.error("初始化SessionFactory失败!", ex);
throw new ExceptionInInitializerError(ex);
}
}
//创建线程局部变量session,用来保存Hibernate的Session
public static final ThreadLocal session = new ThreadLocal();
* 获取当前线程中的Session
* @return Session
* @throws HibernateException
*/
public static Session currentSession() throws HibernateException {
Session s = (Session) session.get();
// 如果Session还没有打开,则新开一个Session
if (s == null) {
s = sessionFactory.openSession();
session.set(s); //将新开的Session保存到线程局部变量中
}
return s;
}
//获取线程局部变量,并强制转换为Session类型
Session s = (Session) session.get();
session.set(null);
if (s != null)
s.close();
}
}
2、另外一个实例
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2007-11-23
* Time: 10:45:02
* 学生
*/
public class Student {
private int age = 0; //年龄
return this.age;
}
this.age = age;
}
}
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2007-11-23
* Time: 10:53:33
* 多线程下测试程序
*/
public class ThreadLocalDemo implements Runnable {
//创建线程局部变量studentLocal,在后面你会发现用来保存Student对象
private final static ThreadLocal studentLocal = new ThreadLocal();
ThreadLocalDemo td = new ThreadLocalDemo();
Thread t1 = new Thread(td, "a");
Thread t2 = new Thread(td, "b");
t1.start();
t2.start();
}
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(500);
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("thread " + currentThreadName + " second read age is:" + student.getAge());
}
//获取本地线程变量并强制转换为Student类型
Student student = (Student) studentLocal.get();
//线程首次执行此方法的时候,studentLocal.get()肯定为null
if (student == null) {
//创建一个Student对象,并保存到本地线程变量studentLocal中
student = new Student();
studentLocal.set(student);
}
return student;
}
}
thread a set age to:76
b is running!
thread b set age to:27
thread a first read age is:76
thread b first read age is:27
thread a second read age is:76
thread b second read age is:27
四、总结
五、ThreadLocal使用的一般步骤
多个进程或线程同时(或着说在同一段时间内)访问同一资源会产生并发问题。
银行两操作员同时操作同一账户就是典型的例子。比如A、B操作员同时读取一余额为1000元的账户,A操作员为该账户增加100元,B操作员同时为该账户减去 50元,A先提交,B后提交。 最后实际账户余额为1000-50=950元,但本该为 1000+100-50=1050。这就是典型的并发问题。如何解决?可以用锁。
- 用法1
public class Test{ public synchronized void print(){ ....; } }
某线程执行print()方法,则该对象将加锁。其它线程将无法执行该对象的所有synchronized块。
- 用法2
public class Test{ public void print(){ synchronized(this){//锁住本对象 ...; } } }
同用法1, 但更能体现synchronized用法的本质。
- 用法3
public class Test{ private String a = "test"; public void print(){ synchronized(a){//锁住a对象 ...; } } public synchronized void t(){ ...; //这个同步代码块不会因为print()而锁定. } }
执行print(),会给对象a加锁,注意不是给Test的对象加锁,也就是说 Test对象的其它synchronized方法不会因为print()而被锁。同步代码块执行完,则释放对a的锁。
为了锁住一个对象的代码块而不影响该对象其它 synchronized块的高性能写法:
public class Test{ private byte[] lock = new byte[0]; public void print(){ synchronized(lock){ ...; } } public synchronized void t(){ ...; } }
- 静态方法的锁
public class Test{ public synchronized static void execute(){ ...; } }
效果同
public class Test{ public static void execute(){ synchronized(TestThread.class){ ...; } } }
3 Java中的锁与排队上厕所。
锁就是阻止其它进程或线程进行资源访问的一种方式,即锁住的资源不能被其它请求访问。在JAVA中,sychronized关键字用来对一个对象加锁。比如:
public class MyStack { int idx = 0; char [] data = new char[6]; public synchronized void push(char c) { data[idx] = c; idx++; } public synchronized char pop() { idx--; return data[idx]; } public static void main(String args[]){ MyStack m = new MyStack(); /** 下面对象m被加锁。严格的说是对象m的所有synchronized块被加锁。 如果存在另一个试图访问m的线程T,那么T无法执行m对象的push和 pop方法。 */ m.pop();//对象m被加锁。 } }
Java的加锁解锁跟多个人排队等一个公共厕位完全一样。第一个人进去后顺手把门从里面锁住,其它人只好排队等。第一个人结束后出来时,门才会打开(解锁)。轮到第二个人进去,同样他又会把门从里面锁住,其它人继续排队等待。
用厕所理论可以很容易明白: 一个人进了一个厕位,这个厕位就会锁住,但不会导致另一个厕位也被锁住,因为一个人不能同时蹲在两个厕位里。对于Java 就是说:Java中的锁是针对同一个对象的,不是针对class的。看下例:
MyStatck m1 = new MyStack(); MyStatck m2 = new Mystatck(); m1.pop(); m2.pop();
m1对象的锁是不会影响m2的锁的,因为它们不是同一个厕位。就是说,假设有 3线程t1,t2,t3操作m1,那么这3个线程只可能在m1上排队等,假设另2个线程 t8,t9在操作m2,那么t8,t9只会在m2上等待。而t2和t8则没有关系,即使m2上的锁释放了,t1,t2,t3可能仍要在m1上排队。原因无它,不是同一个厕位耳。
Java不能同时对一个代码块加两个锁,这和数据库锁机制不同,数据库可以对一条记录同时加好几种不同的锁,请参见:
http://hi.baidu.com/dapplehou/blog/item/b341a97744fe6616b151b9a3.html
4 何时释放锁?
一般是执行完毕同步代码块(锁住的代码块)后就释放锁,也可以用wait()方式半路上释放锁。wait()方式就好比蹲厕所到一半,突然发现下水道堵住了,不得已必须出来站在一边,好让修下水道师傅(准备执行notify的一个线程)进去疏通马桶,疏通完毕,师傅大喊一声: "已经修好了"(notify),刚才出来的同志听到后就重新排队。注意啊,必须等师傅出来啊,师傅不出来,谁也进不去。也就是说notify后,不是其它线程马上可以进入封锁区域活动了,而是必须还要等notify代码所在的封锁区域执行完毕从而释放锁以后,其它线程才可进入。
这里是wait与notify代码示例:
public synchronized char pop() { char c; while (buffer.size() == 0) { try { this.wait(); //从厕位里出来 } catch (InterruptedException e) { // ignore it... } } c = ((Character)buffer.remove(buffer.size()-1)). charValue(); return c; } public synchronized void push(char c) { this.notify(); //通知那些wait()的线程重新排队。注意:仅仅是通知它们重新排队。 Character charObj = new Character(c); buffer.addElement(charObj); }//执行完毕,释放锁。那些排队的线程就可以进来了。
再深入一些。
由于wait()操作而半路出来的同志没收到notify信号前是不会再排队的,他会在旁边看着这些排队的人(其中修水管师傅也在其中)。注意,修水管的师傅不能插队,也得跟那些上厕所的人一样排队,不是说一个人蹲了一半出来后,修水管师傅就可以突然冒出来然后立刻进去抢修了,他要和原来排队的那帮人公平竞争,因为他也是个普通线程。如果修水管师傅排在后面,则前面的人进去后,发现堵了,就wait,然后出来站到一边,再进去一个,再wait,出来,站到一边,只到师傅进去执行notify. 这样,一会儿功夫,排队的旁边就站了一堆人,等着notify.
终于,师傅进去,然后notify了,接下来呢?
1. 有一个wait的人(线程)被通知到。 2. 为什么被通知到的是他而不是另外一个wait的人?取决于JVM.我们无法预先 判断出哪一个会被通知到。也就是说,优先级高的不一定被优先唤醒,等待 时间长的也不一定被优先唤醒,一切不可预知!(当然,如果你了解该JVM的 实现,则可以预知)。 3. 他(被通知到的线程)要重新排队。 4. 他会排在队伍的第一个位置吗?回答是:不一定。他会排最后吗?也不一定。 但如果该线程优先级设的比较高,那么他排在前面的概率就比较大。 5. 轮到他重新进入厕位时,他会从上次wait()的地方接着执行,不会重新执行。 恶心点说就是,他会接着拉巴巴,不会重新拉。 6. 如果师傅notifyAll(). 则那一堆半途而废出来的人全部重新排队。顺序不可知。
Java DOC 上说,The awakened threads will not be able to proceed until the current thread relinquishes the lock on this object(当前线程释放锁前,唤醒的线程不能去执行)。
这用厕位理论解释就是显而易见的事。
5 Lock的使用
用synchronized关键字可以对资源加锁。用Lock关键字也可以。它是JDK1.5中新增内容。用法如下:
class BoundedBuffer { final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100]; int putptr, takeptr, count; public void put(Object x) throws InterruptedException { lock.lock(); try { while (count == items.length) notFull.await(); items[putptr] = x; if (++putptr == items.length) putptr = 0; ++count; notEmpty.signal(); } finally { lock.unlock(); } } public Object take() throws InterruptedException { lock.lock(); try { while (count == 0) notEmpty.await(); Object x = items[takeptr]; if (++takeptr == items.length) takeptr = 0; --count; notFull.signal(); return x; } finally { lock.unlock(); } } }
(注:这是JavaDoc里的例子,是一个阻塞队列的实现例子。所谓阻塞队列,就是一个队列如果满了或者空了,都会导致线程阻塞等待。Java里的 ArrayBlockingQueue提供了现成的阻塞队列,不需要自己专门再写一个了。)
一个对象的lock.lock()和lock.unlock()之间的代码将会被锁住。这种方式比起synchronize好在什么地方?简而言之,就是对wait的线程进行了分类。用厕位理论来描述,则是那些蹲了一半而从厕位里出来等待的人原因可能不一样,有的是因为马桶堵了,有的是因为马桶没水了。通知(notify)的时候,就可以喊:因为马桶堵了而等待的过来重新排队(比如马桶堵塞问题被解决了),或者喊,因为马桶没水而等待的过来重新排队(比如马桶没水问题被解决了)。这样可以控制得更精细一些。不像synchronize里的wait和notify,不管是马桶堵塞还是马桶没水都只能喊:刚才等待的过来排队!假如排队的人进来一看,发现原来只是马桶堵塞问题解决了,而自己渴望解决的问题(马桶没水)还没解决,只好再回去等待(wait),白进来转一圈,浪费时间与资源。
Lock方式与synchronized对应关系:
Lock | await | signal | signalAll |
synchronized | wait | notify | notifyAll |
注意:不要在Lock方式锁住的块里调用wait、notify、notifyAll
相关推荐
小实验三:根据同步机制的Peterson软件解决方案尝试自己编程实现线程同步机制和用于上述线程并发问题的解决,并基于程序运行时间长短将其与基于Windows互斥信号量的线程同步机制的效率展开比较。 实验要求:线程主体...
视频课程下载——C#多线程与线程同步机制高级实战课程
操作系统实验 线程同步机制 Nachos 操作系统实验报告中,主要实现了锁机制和条件变量,并利用这些同步机制实现几个基础工具类。下面是该实验报告的详细知识点: 一、锁机制 锁机制是操作系统中最基本的同步机制...
Java多线程同步机制研究分析 Java多线程同步机制是Java编程语言中的一种机制,它允许多个线程同时执行,提高了系统资源的利用率和安全性。但是,多线程中最重要的问题是线程的同步和共享资源的访问保护。本文通过对...
操作系统线程同步机制实验报告 一、线程同步机制的重要性 在操作系统中,多线程编程是非常常见的。然而,如果不采取任何线程同步措施,多线程之间将会出现数据不一致的问题。通过实验,我们可以看到,如果不采取...
操作系统中的线程同步机制是确保多个线程在访问共享资源时能够有序进行,避免数据竞争和混乱的关键技术。本实验通过模拟银行账户转账操作来演示线程同步的重要性,并使用不同的同步机制进行对比。 实验目的旨在让...
"Java多线程同步机制的应用分析" Java多线程同步机制的应用分析是指在Java语言中,如何使用同步机制来保护临界区,以避免多线程之间的冲突和错误。该机制通过管程机制和同步语法来保护临界区,使得多线程可以安全...
Posix线程同步机制及其在电网监控系统中的应用借鉴 Posix线程同步机制是指在 Unix/Linux 平台上,使用多线程技术来实现多计算任务的并发处理,并解决任务间需要大量数据通信的问题。本文对 Posix 线程同步机制进行...
Java 线程同步机制中 synchronized 关键字的理解 Java 的线程同步机制是为了解决多个线程共享同一片存储空间所带来的访问冲突问题。其中,synchronized 关键字是 Java 语言中解决这种冲突的重要机制。 ...
Windows线程同步机制实验报告 Windows线程同步机制是操作系统中的一种重要机制,用于解决多线程程序中的同步问题。该机制可以确保线程安全地访问共享资源,避免因线程之间的竞争和冲突而导致的错误。 在本实验中,...
在多线程环境中,当涉及到与本地代码(如C/C++)的交互时,同步机制显得尤为重要,以防止数据竞争和不一致的情况。本文将深入探讨如何使用JNI实现多线程同步,并通过源码来解析这一过程。 1. **JNI基础知识** JNI...
本篇文章将深入探讨线程同步机制以及如何使用事件对象来实现这一目标。 首先,线程同步的目的是确保在同一时刻只有一个线程可以访问特定的共享资源或执行某段关键代码。这有助于避免竞态条件和死锁等并发问题。常见...
Java多线程同步是指在Java语言中,如何使用synchronized关键字和其他同步机制来确保多线程程序的正确执行。在Java语言中,synchronized关键字用于对方法或者代码块进行同步,但是仅仅使用synchronized关键字还不能...
Windows 线程同步机制实验报告 操作系统实验报告—Windows 线程同步机制主要涵盖了 Windows 操作系统中的线程同步机制,包括互斥体、事件、关键区、信号量等。实验报告通过 C++/C# 语言在 Microsoft Visual Studio ...
多线程同步机制在软件开发中扮演着至关重要的角色,特别是在多处理器系统或者并发执行的任务中,确保线程间的正确协作和数据一致性是必不可少的。VC++中提供了多种同步机制来处理多线程间的同步问题,其中Event是...
操作系统线程同步机制是计算机科学中一个非常重要的概念,它是保证操作系统正确高效运行的关键技术之一。在多任务操作系统中,线程同步机制能够保证多个线程按照预定顺序执行,避免出现数据不一致或者资源竞争等问题...
线程同步机制是 Java 编程中的一种重要机制,用于控制多个线程之间的资源访问顺序,以避免线程之间的冲突和数据不一致。开发者需要牢牢记住线程同步的四点要素,即线程同步就是线程排队,共享资源需要同步,变量需要...
在提供的文件列表中,如`RWLock.cpp`,可能涉及到了读写锁(Read-Write Lock),这是一种更为复杂的线程同步机制,允许多个线程同时进行读操作,但只允许一个线程进行写操作,从而提高了并发性能。 `Thread.cpp`和`...