线程安全分析
线程安全分析
成员变量和静态变量是否线程安全?
- 如果它们没有共享,则线程安全
- 如果它们被共享了,根据它们的状态是否能够改变,又分两种情况
- 如果只有读操作,则线程安全
- 如果有读写操作,则这段代码是临界区,需要考虑线程安全
例如成员变量list,被两个线程访问,并进行读写操作,没有措施导致线程不安全
public class Example {
static final int THREADNUMBER = 2;//创建线程个数
static final int LOOPNUMBER = 2000;//循环调用次数,次数多了,问题就能显现出来
public static void main(String[] args) throws InterruptedException {
ThreadUnSafe threadUnSafe = new ThreadUnSafe();
//创建两个线程,调用method1
for (int i = 0; i < THREADNUMBER; i++) {
new Thread(() -> {
threadUnSafe.method1(LOOPNUMBER);
}).start();
}
}
}
class ThreadUnSafe{
ArrayList<Integer> list = new ArrayList<>();
public void method1(int loopNumber){
for (int i = 0; i < loopNumber; i++) {
//临界区 产生竟态条件
method2();
method3();
//临界区
}
}
public void method2() {
list.add(2);
}
public void method3() {
list.remove(0);
}
}
局部变量是否线程安全?
- 局部变量是线程安全的
- 但局部变量引用的对象则未必
- 如果该对象没有逃离方法的作用访问,它是线程安全的
- 如果该对象逃离方法的作用范围,需要考虑线程安全
举例,局部变量逃离方法的作用范围,容易产生线程不安全
public class Example {
static final int THREADNUMBER = 2;
static final int LOOPNUMBER = 2000;
public static void main(String[] args) throws InterruptedException {
ThreadUnSafe threadUnSafe = new ThreadUnSafe();
//调用method1
new Thread(() -> {
threadUnSafe.method1(LOOPNUMBER);
}).start();
}
}
class ThreadUnSafe{
public void method1(int loopNumber){
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < loopNumber; i++) {
//临界区 产生竟态条件
method2(list);
method3(list);
//临界区
}
}
public void method2(ArrayList<Integer> list) {
list.add(2);
}
public void method3(ArrayList<Integer> list) {
//新开一个线程,访问list对象
new Thread(() -> {
list.remove(0);
}).start();
}
}
常见线程安全类
- String
- Integer
- StringBuffer
- Random
- Vector
- Hashtable
- java.util.concurredt包下的类
这里说它们是线程安全的是指,多个线程调用它们同一个实例的某个方法时,是线程安全的。也可以理解为它们的每个方法是原子的
但注意它们多个方法的组合不是原子的
String如何保证线程安全?原因是每次修改String类型的对象,都会创建一个新的对象,保证了线程安全,但是效率很低
例如Hashtable,两个线程执行以下操作
if(table.get("key") == null){
table.put("key", value);
}
得出来的结果不一致
sequenceDiagram
participant t1 as 线程1
participant t2 as 线程2
participant table
t1 ->> table : get("key")==null
t2 ->> table : get("key")==null
t1 ->> table : put("key", value)
t2 ->> table : put("key", value)
sequenceDiagram
participant t1 as 线程1
participant t2 as 线程2
participant table
t1 ->> table : get("key")==null
t2 ->> table : get("key")==null
t2 ->> table : put("key", value)
t1 ->> table : put("key", value)
public static void main(String[] args) throws InterruptedException {
Hashtable<String, Integer> table = new Hashtable<>();
Thread t1 = new Thread(() -> {
if (table.get("key") == null) {
table.put("key", 1);
}
});
Thread t2 = new Thread(() -> {
if (table.get("key") == null) {
table.put("key", 2);
}
});
t1.start();
t2.start();
TimeUnit.MILLISECONDS.sleep(300);
System.out.println("key的值为:" + table.get("key"));
}
计算key得出结果为1 或 2
本文来自博客园,作者:如梦幻泡影,转载请注明原文链接:https://www.cnblogs.com/WangJiQing/p/17003860.html