目录
java 程序中怎么保证多线程的运行安全?
参考网址:java 程序中怎么保证多线程的运行安全
线程的安全性问题体现在:原子性、可见性、有序性。
体现方面 |
说明 |
导致原因 |
解决方法 |
原子性 |
一个或者多个操作在CPU执行的过程中不被中断的特性 |
线程切换 |
JDK Atomic开头的原子类(非阻塞CAS算法) synchronized LOCK |
可见性 |
一个线程对共享变量的修改,另外一个线程能够立刻看到 |
缓存 |
synchronized volatile LOCK |
有序性 |
程序执行的顺序按照代码的先后顺序执行 |
编译优化 |
Happens-Before规则 |
Happens-Before规则
参考网址:java内存模型以及happens-before规则 - 简书
1. happens-before含义
JMM(java 内存模型)可以通过happens-before关系向程序员提供跨线程的内存可见性保证(如果A线程的写操作a与B线程的读操作b之间存在happens-before关系,尽管a操作和b操作在不同的线程中执行,但JMM向程序员保证a操作将对b操作可见)。具体的定义为:
(1)如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
(2)两个操作之间存在happens-before关系,并不意味着Java平台的具体实现必须要按照happens-before关系指定的顺序来执行。如果重排序之后的执行结果,与按happens-before关系来执行的结果一致,那么这种重排序并不非法(也就是说,JMM允许这种重排序)。
上面的(1)是JMM对程序员的承诺。从程序员的角度来说,可以这样理解happens-before关系:如果A happens-before B,那么Java内存模型将向程序员保证——A操作的结果将对B可见,且A的执行顺序排在B之前。注意,这只是Java内存模型向程序员做出的保证!
上面的(2)是JMM对编译器和处理器重排序的约束原则。正如前面所言,JMM其实是在遵循一个基本原则:只要不改变程序的执行结果(指的是单线程程序和正确同步的多线程程序),编译器和处理器怎么优化都行。JMM这么做的原因是:程序员对于这两个操作是否真的被重排序并不关心,程序员关心的是程序执行时的语义不能被改变(即执行结果不能被改变)。因此,happens-before关系本质上和as-if-serial语义是一回事。
2.happens-before存在的意义
重排序原则有编译器重排序和处理器重排序等,如果让程序员再去了解这些底层的实现以及具体规则,那么程序员的负担就太重了,严重影响了并发编程的效率。因此,JMM为程序员在上层提供了六条规则,这样我们就可以根据规则去推论跨线程的内存可见性问题,而不用再去理解底层重排序的规则。
规则 |
说明 |
程序顺序规则 | 一个线程中的每个操作,happens-before于该线程中的任意后续操作 |
监视器锁规则 | 对一个锁的解锁,happens-before于随后对这个锁的加锁。 |
volatile变量规则 |
对一个volatile域的写,happens-before于任意后续对这个volatile域的读。 |
传递性规则 | 如果A happens-before B,且B happens-before C,那么A happens-before C。 |
start()规则 | 如果线程A执行操作ThreadB.start()(启动线程B),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作。 |
join()规则 | 如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。 |
线程中断规则 |
对线程interrupt()的调用 happen—before 发生于被中断线程的代码检测到中断时事件的发生。 |
对象终结规则 |
就是一个对象的初始化的完成(构造函数执行的结束) happens-before它的finalize()方法 |
ThreadLocal使用场景
数据库连接
public Connection initialValue() {
return DriverManager.getConnection(DB_URL);
}
public static Connection getConnection() {
return connectionHolder.get();
}
Session管理
public static Session getSession() throws InfrastructureException {
Session s = (Session) threadSession.get();
try {
if (s == null) {
s = getSessionFactory().openSession();
threadSession.set(s);
}
} catch (HibernateException ex) {
throw new InfrastructureException(ex);
}
return s;
}
多线程
public class Demo {
static ThreadLocal<String> localVariable = new ThreadLocal<>();
static void print(String str){
System.out.println(str + ":" + localVariable.get());
}
public static void main(String[] args) {
Thread threadOne = new Thread(new Runnable() {
@Override
public void run() {
localVariable.set("thread local variable");
print("threadOne");
System.out.println("threadOne remove after" + ":" + localVariable.get());
}
});
Thread threadTwo = new Thread(new Runnable() {
@Override
public void run() {
localVariable.set("thread local variable");
print("threadTwo");
System.out.println("threadTwo remove after" + ":" + localVariable.get());
}
});
threadOne.start();
threadTwo.start();
}
}
运行结果:
threadTwo:thread local variable
threadTwo remove after:thread local variable
threadOne:thread local variable
threadOne remove after:thread local variable