Java併發(十八)----常見線程安全類及實例分析

1、常見線程安全類

  • String

  • Integer

  • StringBuffer

  • Random

  • Vector

  • Hashtable

  • java.util.concurrent (JUC)包下的類

這裏說它們是線程安全的是指,多個線程調用它們同一個實例的某個方法時,是線程安全的。

Hashtable table = new Hashtable();
​
new Thread(()->{
    table.put("key", "value1");
}).start();
​
new Thread(()->{
    table.put("key", "value2");
}).start();

注意:

  • 它們的每個方法是原子的

  • 它們多個方法的組合不是原子的。

1.1 線程安全類方法的組合

分析下面代碼是否線程安全?

Hashtable table = new Hashtable();
// 線程1,線程2
if( table.get("key") == null) {
    table.put("key", value);
}

結論是線程不安全

1.2 不可變類線程安全性

String、Integer 等都是不可變類,因爲其內部的狀態不可以改變,因此它們的方法都是線程安全的

或許有疑問,String 有 replace,substring 等方法【可以】改變值啊,那麼這些方法又是如何保證線程安全的呢?

原因爲,原值並沒有被改變,而是創建了一個新值,其內部的狀態沒有改變,因此它們的方法都是線程安全的。

例如下面的代碼也是同理。

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);
  }  
}

2、線程安全實例分析

例1:

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() {
        // 是否安全  不安全 foo暴露出
        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();
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章