Python 官方文档:入门教程 => 点击学习
目录一、线程间的共享1.1 ynchronized内置锁1.2 volatile关键字1.3 ThreadLocal1.4 spring的事务借助ThreadLocal类1.4.1
用处
对象锁和类锁
Spring会从数据库连接池中获得一个connection,然会把connection放进ThreadLocal中,也就和线程绑定了,事务需要提交或者回滚,只要从ThreadLocal中拿到connection进行操作。
以JDBC为例,正常的事务代码可能如下:
dbc = new DataBaseConnection();//第1行
Connection con = dbc.getConnection();//第2行
con.setAutoCommit(false);// //第3行
con.executeUpdate(...);//第4行
con.executeUpdate(...);//第5行
con.executeUpdate(...);//第6行
con.commit();第7行
上述代码,可以分成三个部分:
事务准备阶段:第1~3行
业务处理阶段:第4~6行
事务提交阶段:第7行
Connection conn = getConnection();
Dao1 dao1 = new Dao1(conn);
dao1.exec();
Dao2 dao2 = new Dao2(conn);
dao2.exec();
Dao3 dao3 = new Dao3(conn);
dao3.exec();
conn.commit();
void set(Object value)
public Object get()
public void remove()
protected Object initialValue()
public final static ThreadLocal RESOURCE = new ThreadLocal()
public class ThreadLocal<T> {
//get方法,其实就是拿到每个线程独有的ThreadLocalMap
//然后再用ThreadLocal的当前实例,拿到Map中的相应的Entry,然后就可以拿到相应的值返回出去。
//如果Map为空,还会先进行map的创建,初始化等工作。
public T get() {
//先取到当前线程,然后调用getMap方法获取对应线程的ThreadLocalMap
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
// Thread类中有一个 ThreadLocalMap 类型成员,所以getMap是直接返回Thread的成员
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
// ThreadLocalMap是ThreadLocal的静态内部类
static class ThreadLocalMap {
ThreadLocalMap(ThreadLocal<?> firsTKEy, Object firstValue) {
// 用数组保存 Entry , 因为可能有多个变量需要线程隔离访问,即声明多个 ThreadLocal 变量
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
// Entry 类似于 map 的 key-value 结构
// key 就是 ThreadLocal, value 就是需要隔离访问的变量
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
...
}
//Entry内部静态类,它继承了WeakReference,
//总之它记录了两个信息,一个是ThreadLocal<?>类型,一个是Object类型的值
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//getEntry方法则是获取某个ThreadLocal对应的值
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
//set方法就是更新或赋值相应的ThreadLocal对应的值
private void set(ThreadLocal<?> key, Object value) {
...
}
...
}
public class Thread implements Runnable {
ThreadLocal.ThreadLocalMap threadLocals = null;
...
}
将堆内存大小设置为-Xmx256m
启用一个线程池,大小固定为5个线程
//5M大小的数组
private static class LocalVariable {
private byte[] value = new byte[1024*1024*5];
}
// 创建线程池,固定为5个线程
private static ThreadPoolExecutor poolExecutor
= new ThreadPoolExecutor(5,5,1, TimeUnit.MINUTES,new LinkedBlockingQueue<>());
//ThreadLocal共享变量
private ThreadLocal<LocalVariable> data;
@Override
public void run() {
//场景1:不执行任何有意义的代码,当所有的任务提交执行完成后,查看内存占用情况,占用 25M 左右
//System.out.println("hello ThreadLocal...");
//场景2:创建 数据对象,执行完成后,查看内存占用情况,与场景1相同
//new LocalVariable();
//场景3:启用 ThreadLocal,执行完成后,查看内存占用情况,占用 100M 左右
ThreadLocalOOM obj = new ThreadLocalOOM();
obj.data = new ThreadLocal<>();
obj.data.set(new LocalVariable());
System.out.println("update ThreadLocal data value..........");
//场景4: 加入 remove(),执行完成后,查看内存占用情况,与场景1相同
//obj.data.remove();
//分析:在场景3中,当启用了ThreadLocal以后确实发生了内存泄漏
}
场景1:
场景2:
场景3:
场景4:
场景分析:
场景3分析:
从表面上看内存泄漏的根源在于使用了弱引用。为什么使用弱引用而不是强引用?下面我们分两种情况讨论:
因此,ThreadLocal内存泄漏的根源是:
总结:
错误使用ThreadLocal导致线程不安全:
存在如下问题:
是指一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B调用了对象O的notify()或者notifyAll()方法,线程A收到通知后从对象O的wait()方法返回,进而执行后续操作。
上述两个线程通过对象O来完成交互,而对象上的wait()和notify/notifyAll()的关系就如同开关信号一样,用来完成等待方和通知方之间的交互工作。
notify():
通知一个在对象上等待的线程,使其从wait方法返回,而返回的前提是该线程获取到了对象的锁,没有获得锁的线程重新进入WAITING状态。
notifyAll():
通知所有等待在该对象上的线程。
wait():
调用该方法的线程进入 WAITING状态,只有等待另外线程的通知或被中断才会返回.需要注意,调用wait()方法后,会释放对象的锁。
wait(long):
超时等待一段时间,这里的参数时间是毫秒,也就是等待长达n毫秒,如果没有通知就超时返回;
wait (long,int):
对于超时时间更细粒度的控制,可以达到纳秒;
等待方遵循如下原则:
synchronized(对象){
while(条件不满足){
对象.wait();
}
对应的逻辑
}
通知方遵循如下原则:
synchronized(对象){
改变条件
对象.notifyAll();
}
在调用wait()、notify()系列方法之前,线程必须要获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait() 方法、notify()系列方法;
notify() 和 notifyAll() 应该用谁?
调用场景:
调用yield() 、sleep()、wait()、notify()等方法对锁有何影响?
到此这篇关于Java并发编程之线程之间的共享和协作的文章就介绍到这了,更多相关java线程之间的共享和协作内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!
--结束END--
本文标题: Java并发编程之线程之间的共享和协作
本文链接: https://lsjlt.com/news/123966.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-03-01
2024-03-01
2024-03-01
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0