常見線程安全類 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(); 一鍵獲取完整項目代碼 java 它們的每個方法是原子的
但注意它們多個方法的組合不是原子的,見後面分析
線程安全類方法的組合
分析下面代碼是否線程安全?
Hashtable table = new Hashtable(); // 線程1,線程2 if( table.get("key") == null) { table.put("key", value); } 一鍵獲取完整項目代碼 java
不可變類線程安全性
String、Integer 等都是不可變類,因為其內部的狀態不可以改變,因此它們的方法都是線程安全的 有同學或許有疑問,String 有 replace,substring 等方法【可以】改變值啊,那麼這些方法又是如何保證線程安 全的呢?
String是不可變的,底層是創建了一個新的字符串,因此它會比stringBuilder和StringBuffer效率低.
public class Immutable{ private int value = 0; public Immutable(int value){ this.value = value; } public int getValue(){ return this.value; } } 一鍵獲取完整項目代碼 java 如果想增加一個增加的方法呢?
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); } } 一鍵獲取完整項目代碼 java
實例分析 例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) { // 使用上述變量 } } 一鍵獲取完整項目代碼 java
例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++; } } 一鍵獲取完整項目代碼 java
例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)); } } 一鍵獲取完整項目代碼 java
例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) { // ... } } } 一鍵獲取完整項目代碼 java
例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(); } } 一鍵獲取完整項目代碼 java
例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(); } } 一鍵獲取完整項目代碼 java
例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(); } } 一鍵獲取完整項目代碼 java
其中 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(); } } 一鍵獲取完整項目代碼 java
請比較 JDK 中 String 類的實現
string類加final是為了避免父類的方法被重寫,從而導致安全的類變得不安全
比如我寫了一個類,別人拿我這個類去用,如果不限制自己這個類被重寫的話,就可能會出現線程不安全的問題.
例8:
private static Integer i = 0; public static void main(String[] args) throws InterruptedException { List list = new ArrayList<>(); for (int j = 0; j < 2; j++) { Thread thread = new Thread(() -> { for (int k = 0; k < 5000; k++) { synchronized (i) { i++; } } }, "" + j); list.add(thread); } list.stream().forEach(t -> t.start()); list.stream().forEach(t -> { try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } }); log.debug("{}", i); } 一鍵獲取完整項目代碼 java
———————————————— 版權聲明:本文為CSDN博主「向着五星的方向」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。 原文鏈接:https://blog.csdn.net/qq_69748833/article/details/136924375