java concurrent programming notes five

Thread-safe analysis variables

Member variables and static variables are thread-safe?

  • If they do not share, the security thread
  • If they are shared, according to whether the state thereof can be changed, it is divided into two cases
    • If only the read operation, the security thread
    • If you have read and write operations, then this is a critical section of code, we need to consider thread safety

Local variables are thread-safe?

  • Local variables are thread-safe
  • But the object is not necessarily a local variable reference
    • If the object does not escape the role of access methods, it is thread safe
    • If the object is to flee scope method, we need to consider thread safety

Common thread-safe class

  • String
  • Integer
  • StringBuffer
  • Random
  • Vector
  • Hashtable
  • The class java.util.concurrent package

When they say here are thread-safe means that multiple threads calling them a method to the same instance, are thread-safe. It can be understood as

  • Each of them is a method atoms
  • But note that they are not atomic composition of a plurality of methods.

Direct look at the highlight of it.

Case Analysis

Example 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) {
  		// 使用上述变量
	 }
}
Map不安全
String安全
final String 安全
 Date 不安全 因为可修改
 final Date d2 不安全 
 	1final 固定了Data的引用值
 	2、Data其他属性 比如:年月日可变

Example 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++;
 }
}
private UserService userService = new UserServiceImpl(); 线程不安全
1、userService 是UserServiceImpl中的成员变量,所以会有多个线程共享使用userService 
2、UserServiceImpl中的count只有一份,所以是共享资源
3、UserServiceImpl中的update()属于临界区,多个线程可能对update()进行修改操作

Example 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));
 }
}
这段代码有线程安全问题
1、没有加@Scope(),所以他是单例
2、因为是单例,需要被共享,所以成员变量需要被共享
3、使用多例无法解决问题,因为使用前置通知可以是一个对象,然后后置通知有可能是另一个对象,无法统计时间
解决方法
1、使用环绕通知,可以把获取时间的变量做成局部变量

Example 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) {
		 // ...
	 }
 }
}
1、UserDaoImpl没有成员变量,线程安全
2、 Connection conn也是安全的,因为是局部变量。有多个线程同时访问,线程一创建conn1,线程二创建conn2,互不干扰
3、UserServiceImpl 使用userDao ,线程安全   因为UserDao虽然被共享,但是没有可修改的属性(无状态,没有成员变量)
4、MyServlet 使用userService 线程安全  因为userService 虽然有userDao 成员变量,但是他是private,而且userDao不能被修改

Example 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();
 	}
}
1、跟例4一样的不分析
2private Connection conn = null;作成了UserDaoImpl 的成员变量
3、然而UserDaol ,UserService,MyServlet 都只有一份,所以UserDaoImpl 会被多个线程共享,所以conn被多个线程共享
4、所以private Connection conn = null;要做成私有的局部变量,而不是共享的成员变量
  • A means that will be created in a stack frame memory per thread.
  • If multiple copies are created in the stack frame memory for each thread, so there is no sharing

Example 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();
	 }
}
1、在UserServiceImpl每次都会创建新的userDao 作为方法内的局部变量,没有线程安全问题
2、线程一调用Service中的update,创建一个新的userDao,于是Connection也是新的;线程二调用后也是新的。
3、不推荐这种做法,在此方法没有问题,其他例子里可能有隐患。把Connection做成线程内的局部变量最佳

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

Where foo behavior is uncertain and may lead to unsafe occurrence, known as alien method

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();
	 }
}
1、sdf 是局部变量,传递给抽象方法,子类可能做不恰当的事情
2、子类中的父方法在一个新线程被使用,造成并发访问同一个对象

Compare achieve the JDK String class

private final and to some extent to protect the security thread. The principle of opening and closing of the closing, let subclasses to influence the behavior of the parent class
Published 93 original articles · won praise 31 · views 30000 +

Guess you like

Origin blog.csdn.net/weixin_43866567/article/details/104544087