java notas de programação simultânea cinco

variáveis ​​de análise thread-safe

variáveis ​​de membro e variáveis ​​estáticas são thread-safe?

  • Se eles não compartilham, o fio de segurança
  • Se eles são compartilhados, consoante o estado do mesmo pode ser alterado, ele é dividido em dois casos
    • Se apenas a operação de leitura, o fio de segurança
    • Se você tem ler e operações de gravação, então esta é uma seção crítica do código, é preciso considerar a segurança do thread

As variáveis ​​locais são thread-safe?

  • As variáveis ​​locais são thread-safe
  • Mas o objeto não é necessariamente uma referência variável local
    • Se o objeto não escapa o papel de métodos de acesso, é thread-safe
    • Se o objeto é fugir método escopo, é preciso considerar a segurança do thread

thread-safe comum classe

  • Corda
  • número inteiro
  • StringBuffer
  • Aleatória
  • Vetor
  • Hashtable
  • O pacote de classe java.util.concurrent

Quando eles dizem aqui são meios thread-safe que vários segmentos chamando-os de um método para a mesma instância, são thread-safe. Ele pode ser entendido como

  • Cada um deles é um átomo de métodos
  • Mas note que eles não são composição atômica de uma pluralidade de métodos.

olhar directo sobre o destaque do mesmo.

Estudo de caso

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

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

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

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

Exemplo 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;要做成私有的局部变量,而不是共享的成员变量
  • Um meio que serão criadas na memória de um quadro de pilha por thread.
  • Se várias cópias são criadas na memória do quadro de pilha para cada thread, portanto, não há partilha

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

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

Onde comportamento foo é incerta e pode levar à ocorrência inseguro, conhecido como método alienígena

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 atingir a classe JDK Cordas

private final e, em certa medida para proteger o fio de segurança. O princípio de abertura e fechamento do fechamento, deixe subclasses para influenciar o comportamento da classe pai
Publicado 93 artigos originais · Louvor obteve 31 · vê 30000 +

Acho que você gosta

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