允许多个线程同时运行的代码就是线程安全的代码。下面讲的是需要注意的线程安全的几种情况:
1.局部原始变量
局部原始变量都是线程安全的,因为这种变量在每个线程都有一个副本。例如:对于局部变量int x=0;那么每个线程在栈中都会有一个自己的x。
package com.concurenny.chapter.four;
/**
* 创建者:Mr lebron 创建时间:2017年11月16日 下午7:05:12
*/
public class LocalVariableDemo {
public static void main(String[] args) {
new Thread(() -> {
// 获取原始类型的局部变量,并修改.原始类型的变量不会改变。
// 因为每个线程执行getVariable()都会保存一个x副本,即每个线程拥有一个自己的x变量。
// 所以线程安全
int y = getVariable() + 1;
System.out.println("modified:" + y + " origin:" + getVariable());
}).start();
new Thread(() -> {
// 获取原始类型的局部变量,并修改.原始类型的变量不会改变。
// 因为每个线程执行getVariable()都会保存一个x副本,即每个线程拥有一个自己的x变量。
// 所以线程安全
int z = getVariable() + 1;
System.out.println("modified:" + z + " origin:" + getVariable());
}).start();
}
public static int getVariable() {
int x = 100;
return x;
}
}
局部的对象引用
这种很容易出现线程安全问题。因为一个线程创建的对象引用可能赋值给另外一个线程。
package com.concurenny.chapter.four;
/**
* 创建者:Mr lebron 创建时间:2017年11月16日 下午7:16:49
*/
public class LocalRefererDemo {
public static void main(String[] args) {
new Thread(() -> {
// 获取一个局部Counter变量的引用,并赋值=100
final Counter counter = escapeReferer();
counter.setCount(100);
// 另外一个线程对该counter引用修改
new Thread(() -> {
counter.setCount(101);
}).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 局部变量counter被另外一个线程修改了,所以这不是线程安全的局部引用变量
System.out.println("100 == " + counter.getCount() + "?");
}).start();
}
public static Counter escapeReferer() {
Counter counter = new Counter();
return counter;
}
static class Counter {
private int count = 0;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
}
对象成员
一个对象的成员属于多个线程的共享数据,必然不是线程安全的。
package com.concurenny.chapter.four;
/**
* 创建者:Mr lebron 创建时间:2017年11月16日 下午7:36:26
*/
public class ObjectMemberDemo {
// 共享变量
static Worker worker = new Worker();
public static void main(String[] args) {
// 开启一个线程读取共享变量的值
new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("salary:" + worker.getSalary());
}).start();
// 修改共享变量的值,开启30个线程,每个线程对salary+1,那么最后应该得到1000030?结果是如此吗?,多运行几次就知道了
for (int i = 0; i < 30; i++) {
new Thread(() -> {
worker.setSalary(worker.getSalary() + 1);
}).start();
}
}
static class Worker {
private double salary = 1_000_000;
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
}
本文探讨了线程安全问题,分析了局部原始变量、局部对象引用和对象成员等不同情况下线程安全的特点。通过实例展示了如何识别及解决线程安全问题。

被折叠的 条评论
为什么被折叠?



