java notas de programación concurrente cinco

variables de análisis seguras para subprocesos

variables miembro y variables estáticas son hilos de proceso seguro?

  • Si no se comparten, el hilo de seguridad
  • Si se comparten, en función de si el estado de los mismos se puede cambiar, se divide en dos casos
    • Si sólo la operación de lectura, el hilo de seguridad
    • Si usted ha leído y operaciones de escritura, entonces esta es una sección crítica de código, tenemos que considerar la seguridad hilo

Las variables locales son flujos seguros?

  • Las variables locales son seguros para subprocesos
  • Pero el objeto no es necesariamente una referencia variable local
    • Si el objeto no se escape el papel de los métodos de acceso, es seguro para hilos
    • Si el objeto es huir método de alcance, tenemos que considerar la seguridad hilo

thread-safe común de clase

  • Cuerda
  • Entero
  • StringBuffer
  • Aleatorio
  • Vector
  • Tabla de picadillo
  • El paquete de la clase java.util.concurrent

Cuando dicen que aquí están los medios compatibles con el proceso que varios subprocesos ellos llaman a un método para la misma instancia, son seguros para subprocesos. Se puede entender como

  • Cada uno de ellos es un átomo de método
  • Pero tenga en cuenta que no son la composición atómica de una pluralidad de métodos.

mirada directa a lo más destacado de la misma.

Estudio de caso

Ejemplo 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其他属性 比如:年月日可变

Ejemplo 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()进行修改操作

Ejemplo 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、使用环绕通知,可以把获取时间的变量做成局部变量

Ejemplo 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不能被修改

Ejemplo 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;要做成私有的局部变量,而不是共享的成员变量
  • Un medio que se crearán en una memoria marco de pila por hilo.
  • Si se crean varias copias en la memoria del marco de pila para cada hilo, lo que no hay intercambio

Ejemplo 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做成线程内的局部变量最佳

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

Donde el comportamiento foo es incierto y puede conducir a la aparición inseguro, conocido como método extranjero

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、子类中的父方法在一个新线程被使用,造成并发访问同一个对象

Comparar conseguir el nivel de JDK Cadena

final privado y en cierta medida para proteger el hilo de seguridad. El principio de apertura y cierre del cierre, dejó subclases para influir en el comportamiento de la clase padre
Publicado 93 artículos originales · ganado elogios 31 · Vistas a 30000 +

Supongo que te gusta

Origin blog.csdn.net/weixin_43866567/article/details/104544087
Recomendado
Clasificación