当前位置:网站首页>并发之线程安全
并发之线程安全
2022-06-26 16:50:00 【七国的天下,我要九十九】
1 线程安全
1 成员变量和静态变量
- 如果它们没有共享,则线程安全
- 如果它们被共享了,根据它们的状态是否能够改变,又分两种情况
- 如果只有读操作,则线程安全
- 如果有读写操作,则这段代码是临界区,需要考虑线程安全
2 局部变量
- 局部变量是线程安全的
- 局部变量引用的对象则未必
- 如果该对象没有逃离方法的作用访问,它是线程安全的
- 如果该对象逃离方法的作用范围,需要考虑线程安全
局部变量线程安全分析
public static void test1() {
int i = 10;
i++;
}
每个线程调用 test1() 方法时局部变量 i,会在每个线程的栈帧内存中被创建多份,因此不存在共享.
成员变量
class ThreadUnsafe {
ArrayList<String> list = new ArrayList<>();
public void method1(int loopNumber) {
for (int i = 0; i < loopNumber; i++) {
// { 临界区, 会产生竞态条件
method2();
method3();
// } 临界区
}
}
private void method2() {
list.add("1");
}
private void method3() {
list.remove(0);
}
}
static final int THREAD_NUMBER = 2;
static final int LOOP_NUMBER = 200;
public static void main(String[] args) {
ThreadUnsafe test = new ThreadUnsafe();
for (int i = 0; i < THREAD_NUMBER; i++) {
new Thread(() -> {
test.method1(LOOP_NUMBER);
}, "Thread" + i).start();
}
}
/* 运行报错: Exception in thread "Thread1" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 */
说明:
- 无论哪个线程中的 method2 引用的都是同一个对象中的 list 成员变量
- method3 与 method2 运行相同
解决方法
将list改为局部变量
class ThreadSafe {
public final void method1(int loopNumber) {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < loopNumber; i++) {
method2(list);
method3(list);
}
}
private void method2(ArrayList<String> list) {
list.add("1");
}
private void method3(ArrayList<String> list) {
list.remove(0);
}
}
说明:
- list 是局部变量,每个线程调用时会创建其不同实例,没有共享
- method2 的参数是从 method1 中传递过来的,与 method1 中引用同一个对象
- method3 的参数分析与 method2 相同

3 常见线程安全类
String, Integer, StringBuffer, Random, Vector, Hashtable, java.util.concurrent包下的类
多个线程调用它们同一个实例的某个方法时,是线程安全的,即
Hashtable table = new Hashtable();
new Thread(()->{
table.put("key", "value1");
}).start();
new Thread(()->{
table.put("key", "value2");
}).start();
说明:
- 它们的每个方法是原子的
- 多个方法的组合不是原子的
线程安全类方法的组合
Hashtable table = new Hashtable();
// 线程1,线程2
if( table.get("key") == null) {
table.put("key", value);
}

不可变类线程安全
String、Integer 等都是不可变类,因为其内部的状态不可以改变,因此它们的方法都是线程安全的. 但其部分方法,如replace可以改变值,怎么处理的?
下面案列,给该类添加一个方法,可以添加对象的内容信息.
public class Immutable{
private int value = 0;
public Immutable(int value){
this.value = value;
}
public int getValue(){
return this.value;
}
}
改造后
public class Immutable{
private int value = 0;
public Immutable(int value){
this.value = value;
}
public int getValue(){
return this.value;
}
public Immutable add(int v){
return new Immutable(this.value + v);
}
}
说明:
- 通过重新创建了一个新对象,来改变了不可变类信息
4 实例说明
案列1
// HttpServlet是Web容器访问中是单例, 即多个线程访问同一个对象
public class MyServlet extends HttpServlet {
// 是否安全? 否
Map<String,Object> map = new HashMap<>();
// 是否安全? 是
String S1 = "...";
// 是否安全? 是
final String S2 = "...";
// 是否安全? 否
Date D1 = new Date();
// 是否安全? 是
final Date D2 = new Date();
public void doGet(HttpServletRequest request, HttpServletResponse response) {
// 使用上述变量
}
}
案列2
public class MyServlet extends HttpServlet {
// 是否安全? 否 单例对象中存在可变成员变量
private UserService userService = new UserServiceImpl();
public void doGet(HttpServletRequest request, HttpServletResponse response) {
userService.update(...);
}
}
public class UserServiceImpl implements UserService {
// 记录调用次数
private int count = 0;
public void update() {
// ...
count++;
}
}
案列3
@Aspect
@Component
public class MyAspect {
// 是否安全?否 单例对象可变成员变量
private long start = 0L;
@Before("execution(* *(..))")
public void before() {
start = System.nanoTime();
}
@After("execution(* *(..))")
public void after() {
long end = System.nanoTime();
System.out.println("cost time:" + (end-start));
}
}
案列4
public class MyServlet extends HttpServlet {
// 是否安全? 是 单例对象没有可变成员变量,有一个线程安全方法
private UserService userService = new UserServiceImpl();
public void doGet(HttpServletRequest request, HttpServletResponse response) {
userService.update(...);
}
}
public class UserServiceImpl implements UserService {
// 是否安全? 是 单例对象没有可变成员变量,有一个线程安全方法
private UserDao userDao = new UserDaoImpl();
public void update() {
userDao.update();
}
}
public class UserDaoImpl implements UserDao {
public void update() {
String sql = "update user set password = ? where username = ?";
// 是否安全? 是,局部变量,访问没有导方法外,线程安全
try (Connection conn = DriverManager.getConnection("","","")){
// ...
} catch (Exception e) {
// ...
}
}
}
案列5
public class MyServlet extends HttpServlet {
// 是否安全 否, 该单例对象不安全
private UserService userService = new UserServiceImpl();
public void doGet(HttpServletRequest request, HttpServletResponse response) {
userService.update(...);
}
}
public class UserServiceImpl implements UserService {
// 是否安全 否, 该单例对象不安全
private UserDao userDao = new UserDaoImpl();
public void update() {
userDao.update();
}
}
public class UserDaoImpl implements UserDao {
// 是否安全 否, 单例对象存在可变成员变量
private Connection conn = null;
public void update() throws SQLException {
String sql = "update user set password = ? where username = ?";
conn = DriverManager.getConnection("","","");
// ...
conn.close();
}
}
案列6
public class MyServlet extends HttpServlet {
// 是否安全 是 该单例对象安全
private UserService userService = new UserServiceImpl();
public void doGet(HttpServletRequest request, HttpServletResponse response) {
userService.update(...);
}
}
public class UserServiceImpl implements UserService {
public void update() {
// 线程安全, 每次在方法内新建对象
UserDao userDao = new UserDaoImpl();
userDao.update();
}
}
public class UserDaoImpl implements UserDao {
// 是否安全 否, 单例对象存在可变成员变量
private Connection = null;
public void update() throws SQLException {
String sql = "update user set password = ? where username = ?";
conn = DriverManager.getConnection("","","");
// ...
conn.close();
}
}
案列7
public abstract class Test {
public void bar() {
// 是否安全
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
foo(sdf);
}
public abstract foo(SimpleDateFormat sdf);
public static void main(String[] args) {
new Test().bar();
}
}
其中 foo 的行为是不确定的,可能导致不安全的发生,称为外星方法.
public void foo(SimpleDateFormat sdf) {
String dateStr = "1999-10-11 00:00:00";
for (int i = 0; i < 20; i++) {
new Thread(() -> {
try {
sdf.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
}).start();
}
}
边栏推荐
- 数字藏品与NFT到底有何区别
- Screenshot of the answers to C language exercises
- Jouer avec Linux et installer et configurer MySQL facilement
- [understanding of opportunity -31]: Guiguzi - Daoyu [x ī] Crisis is the coexistence of danger and opportunity
- Leetcode topic [array] -283- move zero
- Romance of the Three Kingdoms: responsibility chain model
- Teach you to learn dapr - 6 Publish subscription
- Gui+sqlserver examination system
- Science | giant bacteria found in mangroves challenge the traditional concept of nuclear free membrane
- COMP5216 Mobile Computing Assignment 1 - Extending ToDoList app
猜你喜欢

Platform management background and merchant menu resource management: merchant registration management design

Redis' 43 serial cannons, try how many you can carry

Wechat app mall, review products, upload commodity pictures, and score Commodity Services

5G未平6G再启,中国引领无线通信,6G的最大优势在哪里?

Web3 decentralized storage ecological landscape
Teach you to learn dapr - 6 Publish subscription

Leetcode 1169. Query invalid transactions (if the amount of data is small, this problem still needs to be solved by violent enumeration)

Research on natural transition dubbing processing scheme based on MATLAB

玩轉Linux,輕松安裝配置MySQL

SQL injection for Web Security (3)
随机推荐
Teach you to learn dapr - 5 Status management
建立自己的网站(16)
What is the difference between digital collections and NFT
Microservice architecture practice: user login and account switching design, order query design of the mall
Basic requirements: 7 problems in singleton mode
20: Chapter 3: develop the pass service: 3: get through the redis server in the program; (it only connects with the redis server and does not involve specific business development)
Sandboxed container: container or virtual machine
Incomplete line spacing adjustment of formula display in word
Fire evacuation and self rescue... This safety production and fire training is full!
Experience in hierarchical debugging of boards and cards
Call the random function to generate 20 different integers and put them in the index group of institute a
Teach you to learn dapr - 4 Service invocation
7 views on NFT market prospect
Deployment and operation of mongodb partitioned cluster
Discussion: the next generation of stable coins
Gui+sqlserver examination system
直播预告|程序员进击,如何提升研发效能?6月21日晚视频号、B站同步直播,不见不散!
[qt learning notes]qt inter thread data communication and data sharing
She said she was tired! So tired! I want to change my career
Secrets of gear contract