线程安全

it2022-05-09  30

一、类的线程安全定义

(Doung Lee)如果多线程下使用这个类,不管多线程如何使用和调度这个类,这个类总是表示出正确的行为,这个类就是线程安全的。

类的线程安全表现为:

  操作的原子性

  内存的可见性

不做正确的同步,在多个线程之间共享状态的时候,就会出现线程不安全。

二、怎么才能做到类的线程安全

1.栈封闭

所有的变量都是在方法内部声明的这些变量都处于栈封闭状态(这样的话,就是安全的,因为方法是在栈针里面的,栈针又是线程级别的)

2.无状态

没有任何成员变量的类就叫无状态的类。

 

3.让类不可变

 

让状态不可变,两种方式:

 

1). final关键字,对于一个类,所有的成员变量应该是私有的,同样的只要有可能,所有的成员变量应该加上final关键字,但是加上final,要注意如果成员变量又是一个对象时,这个对象所对应的类也要是不可变,才能保证整个类是不可变的。

我们需要注意到的是,当成员变量被final修饰的时候,就只能有get()方法,没有set()方法;(例子:比如ConcurrentHashMap中的Node类)

看一个代码事例:

 

public class ImmutableFinalRef { private final int a; private final int b; private final User user;//这里,就不能保证线程安全啦 public ImmutableFinalRef(int a, int b) { super(); this.a = a; this.b = b; this.user = new User(); } public int getA() { return a; } public int getB() { return b; } public User getUser() { return user; } public static class User{ private int age; public User(int age) { super(); this.age = age; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } public static void main(String[] args) { ImmutableFinalRef ref = new ImmutableFinalRef(12,23); User u = ref.getUser(); //u.setAge(35); } } View Code

 

看到上面的代码的时候,我们注意到在成员变量的对象,还是会被赋值的。只要下面的方式才行:

package com.xiangxue.ch7.safeclass; /** *@author Mark老师 享学课堂 https://enjoy.ke.qq.com * *类说明:看起来不可变的类,实际是可变的 */ public class ImmutableFinalRef { private final int a; private final int b; private final User user;//这里,就不能保证线程安全啦 public ImmutableFinalRef(int a, int b) { super(); this.a = a; this.b = b; this.user = new User(); } public int getA() { return a; } public int getB() { return b; } public User getUser() { return user; } public static class User{ private final int age; public User(int age) { super(); this.age = age; } public int getAge() { return age; } } public static void main(String[] args) { ImmutableFinalRef ref = new ImmutableFinalRef(12,23); User u = ref.getUser(); //u.setAge(35); } } View Code

 

 

2). 根本就不提供任何可供修改成员变量的地方,同时成员变量也不作为方法的返回值。

看例子。其实在成员变量加上final,是最好的

 

public class ImmutetableToo { private List<Integer> list = new ArrayList<>(3); public ImmutetableToo() { list.add(1); list.add(2); list.add(3); } public boolean isContains(int i) { return list.contains(i); } } View Code

 

4.使用不可变的类

不可变类(Immutable Objects):当类的实例一经创建,其内容便不可改变,即无法修改其成员变量。

Java 中八个基本类型的包装类和 String 类都属于不可变类,而其他的大多数类都属于可变类。

 

5.volatile

 

保证类的可见性,最适合一个线程写,多个线程读的情景,(比如ConcurrentHashMap)

6.安全的发布

这个时候我们可以和 3 中的2 进行对比

public class UnsafePublish { //要么用线程的容器替换 //要么发布出去的时候,提供副本,深度拷贝 private List<Integer> list = new ArrayList<>(3); public UnsafePublish() { list.add(1); list.add(2); list.add(3); } //讲list不安全的发布出去了 public List<Integer> getList() { return list; } //也是安全的,加了锁-------------------------------- public synchronized int getList(int index) { return list.get(index); } public synchronized void set(int index,int val) { list.set(index,val); } } View Code

7.使用TheadLocal

 

转载于:https://www.cnblogs.com/lys-lyy/p/11107117.html


最新回复(0)