线程安全
- 栈封闭:把对象封装在一个线程里,只有这个线程才能看到,无全局变量
- 无状态:没有任何成员变量的类,就叫无状态的类,这种类一定线程安全
- 让类不可变:类中的成员变量加上final关键字,不提供修改成员变量的地方,但是成员变量中如果有对象,final关键字也不能保证类的线程安全
- volatile:不能保证线程安全性,只能保证线程间的可见性(缓存一致性协议)和防止指令重排序(内存屏障,读屏障、写屏障)
- 加锁和CAS:最常用的线程安全手段,synchronized关键字,显示锁,使用各种原子变量,修改数据时使用无锁优化CAS机制
- 安全的发布:类的成员变量对外暴露,如果是基本类型,发布出去的是变量的一个副本,没有问题,如果是对象就要当心
- TheadLocal:ThreadLocal是实现线程封闭的最好方法。线程内部维护了一个ThreadLocal
Map
,Map
的key
是ThreadLocal,而Map
的值就是我们要封闭的对象。每个线程中的对象都对应着Map中一个值,也就是ThreadLocal利用Map实现了对象的线程封闭
Servlet是否线程安全
不是线程安全的类,为什么我们平时没感觉到:
1、在需求上,很少有共享的需求
2、接收到了请求,返回应答的时候,一般都是由一个线程来负责的
但是只要Servlet中有成员变量,一旦有多线程下的写,就很容易产生线程安全问题
死锁
示例代码:
/**
*类说明:演示普通账户的死锁和解决
*/
public class NormalDeadLock {
private static Object valueFirst = new Object();//第一个锁
private static Object valueSecond = new Object();//第二个锁
//先拿第一个锁,再拿第二个锁
private static void fisrtToSecond() throws InterruptedException {
synchronized (valueFirst){
synchronized (valueSecond){
}
}
}
//先拿第二个锁,再拿第一个锁
private static void SecondToFisrt() throws InterruptedException {
synchronized (valueFirst){
synchronized (valueSecond){
}
}
}
private static class TestThread extends Thread{
private String name;
public TestThread(String name) {
this.name = name;
}
public void run(){
Thread.currentThread().setName(name);
try {
SecondToFisrt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread.currentThread().setName("TestDeadLock");
TestThread testThread = new TestThread("SubTestThread");
testThread.start();
try {
fisrtToSecond();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
解决
定位
要解决死锁,当然要先找到死锁,怎么找?
通过jps 查询应用的 id,再通过jstack id 查看应用的锁的持有情况
修改
保证那锁的顺序一致,或者如果连个锁之间是有联系的,那么就合起来定义为一把锁
线程安全的单例模式
懒汉式-双重检查
/**
* 懒汉式-双重检查
*/
public class SingleDcl {
private volatile static SingleDcl singleDcl;
//私有化
private SingleDcl(){
}
public static SingleDcl getInstance(){
if (singleDcl == null){ //第一次检查,不加锁
System.out.println(Thread.currentThread()+" is null");
synchronized(SingleDcl.class){ //加锁
if (singleDcl == null){ //第二次检查,加锁情况下
System.out.println(Thread.currentThread()+" is null");
//内存中分配空间 1
//空间初始化 2
//把这个空间的地址给我们的引用 3
singleDcl = new SingleDcl();
}
}
}
return singleDcl;
}
}
注意:
示例对象Instence,一定要定义为volatile,防止指令重排序
饿汉式
/**
* 饿汉式
*/
public class SingleEHan {
private SingleEHan(){}
private static SingleEHan singleDcl = new SingleEHan();
}
注意:
饿汉式最为简单,也不容易错
懒汉式-延迟初始化占位类模式
/**
* 懒汉式-延迟初始化占位类模式
*/
public class SingleInit {
private SingleInit(){}
private static class InstanceHolder{
private static SingleInit instance = new SingleInit();
}
public static SingleInit getInstance(){
return InstanceHolder.instance;
}
}
注意:
静态内部类由JVM保证了初始化时的单例和线程安全
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。