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 不安全
1、final 固定了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一样的不分析
2、private 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