并发安全
线程封闭
ad-hoc 线程封闭
这是完全靠实现者控制的线程封闭,他的线程封闭完全靠实现者实现。ad-hoc 线程封闭非常脆弱,应该尽量避免使用
栈封闭
使用局部变量,方法中的局部变量都会拷贝一份到线程栈中,不会被多个线程共享。
无状态的类
没有任何成员变量,这种类一定是线程安全的
public class StatelessClass {
public int service(int a,int b){
return a+b;
}
public void serviceUser(UserVo user){
//do sth user
}
}
让类不可变
- 加final关键字,但要注意如果成员变量为一个对象时,要保证对象也是不可变的
public class ImmutableClass {
private final int a;//这里是安全的
private final UserVo user = new UserVo();//这里是不安全的
public int getA() {
return a;
}
public UserVo getUser() {
return user;
}
public ImmutableClass(int a) {
this.a = a;
}
public static class User{
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
}
- 不提供任何可供修改成员变量的地方,同时成员变量也不作为方法的返回值
public class ImmutableClassToo {
private List<Integer> list = new ArrayList<>(3);
public ImmutableClassToo() {
list.add(1);
list.add(2);
list.add(3);
}
public boolean isContain(int i){
return list.contains(i);
}
}
volatile
并不能保证类的线程安全性,只能保证类的可见性,最适合一个线程写,多个线程读的情景。
加锁和CAS
最常用的手段,使用synchronized和各种显示锁,CAS机制等
安全的发布
- 基本类型的发布
public class SafePublish {
private int i;
public SafePublish() {
i = 2;
}
public int getI() {
return i;
}
public static void main(String[] args) {
SafePublish safePublish = new SafePublish();
int j = safePublish.getI();
System.out.println("before j="+j);
j = 3;
System.out.println("after j="+j);
System.out.println("getI = "+safePublish.getI());
}
}
- JDK的原生对象
public class SafePublishToo {
private List<Integer> list
= Collections.synchronizedList(new ArrayList<>(3));
public SafePublishToo() {
list.add(1);
list.add(2);
list.add(3);
}
public List getList() {
return list;
}
public static void main(String[] args) {
SafePublishToo safePublishToo = new SafePublishToo();
List<Integer> list = safePublishToo.getList();
System.out.println(list);
list.add(4);
System.out.println(list);
System.out.println(safePublishToo.getList());
}
}
- 不安全的发布
public class UnSafePublish {
private List<Integer> list = new ArrayList<>(3);
public UnSafePublish() {
list.add(1);
list.add(2);
list.add(3);
}
public List getList() {
return list;
}
public static void main(String[] args) {
UnSafePublish unSafePublish = new UnSafePublish();
List<Integer> list = unSafePublish.getList();
System.out.println(list);
list.add(4);
System.out.println(list);
System.out.println(unSafePublish.getList());
}
}
- 包装一个类,将内部成员对象进行线程安全的包装
/**
* @author lizhangbo
* @title: SoftPublicUser
* @projectName concurrent
* @description: 仿Collections对容器的包装,将内部成员对象进行线程安全包装
* @date 2019/5/15 21:06
*/
public class SoftPublicUser {
private final UserVo user;
public UserVo getUser() {
return user;
}
public SoftPublicUser(UserVo user) {
this.user = user;
}
public static class SynUser extends UserVo {
private final UserVo userVo;
private final Object lock = new Object();
public SynUser(UserVo userVo) {
this.userVo = userVo;
}
@Override
public int getAge() {
synchronized (lock) {
return userVo.getAge();
}
}
@Override
public void setAge(int age) {
synchronized (lock) {
userVo.setAge(age);
}
}
}
}
5.委托给线程安全的类来做
/**
* @author lizhangbo
* @title: SafePublicFinalUser
* @projectName concurrent
* @description: 委托给线程安全的类来做
* @date 2019/5/15 21:12
*/
public class SafePublicFinalUser {
private final SynFinalUser user;
public SynFinalUser getUser() {
return user;
}
public SafePublicFinalUser(FinalUserVo userVo) {
this.user = new SynFinalUser(userVo);
}
private static class SynFinalUser {
private final FinalUserVo userVo;
private final Object lock = new Object();
public SynFinalUser(FinalUserVo userVo) {
this.userVo = userVo;
}
public int getAge() {
synchronized (lock) {
return userVo.getAge();
}
}
public void setAge(int age) {
synchronized (lock) {
userVo.setAge(age);
}
}
}
}
ThreadLocal
实现线程封闭的最好方法
Servlet辨析
不是线程安全的类
平时感觉不出来是因为:
- 在需求上,很少有共享的需求;
- 接受请求,返回应答,一般都是有一个线程负责的
但是只要Sevlet中只要有成员变量,一旦有多线程的写,就有可能产生线程安全问题