在通过例子分析线程安全之前先 看了解这些 知识
在去看例子会清晰一点
Java有三大变量 静态变量:在方法区。 实例变量:在堆当中 局部变量:在栈中。 以上三大变量中: 局部变量永远都不会存在线程安全问题。 因为局部变量不共享。(一个线程一个栈。) 局部变量在栈中。所以局部变量永远都不会共享。 实例变量在堆中,堆只有1个。 静态变量在方法区中,方法区只有1个. 堆和方法区都是多线程共享的,所以可能存在线程安全问题。 局部变量+常量:不会有线程安全问题。 成员变量:可能会有线程安全问题。 【成员变量包括实例变量(存储在堆内存)和静态变量(存储在方法区内存)】 局部变量存储在栈内存当中,栈不共享,不会有线程安全的问题
常见线程安全类
String Integer Random Vector Hashtable java.util.concurrent 包下的类 ArrayList是非线程安全的。vector是线程安全的。 HashMap Hashset是非线程安全的。Hashtable是线程安全的。 如果使用局部变量的话: 建议使用: stringBuilder. 因为局部变量不存在线程安全问题。 选择stringBuilder.stringBuffer效率比较低。
总结: 实现线程安全有三种方式:1.无共享变量2.共享变量不可变3.同步
下面请跟我分析一下代码是否存在线程安全的问题,全部看完相信会有很大帮助
例一:
public class MyServlet extends HttpServlet { // 是否安全? Mapmap = new HashMap<>(); //不安全 因为HashMap当中没有synchronized修饰 是非安全的 // 是否安全? String S1 = "..."; //安全 因为String的不可变类 属于共享变量不可变 // 是否安全? final String S2 = "..."; //安全的 本身String就是线程安全的 加上final就更是了 // 是否安全? Date D1 = new Date(); //不安全 日期类不属于线程安全的 // 是否安全? final Date D2 = new Date(); //不安全 虽然final Date D2 的 final修饰的 //但是他new Date(); 里面 的年月日会变 总结 日期是可变类 而字符串是不可变的 }
例二
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++; } }
例三
spring IOC @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)); //计算耗时 通过切面的功能,把耗时功能给抽离出来 } }
例四
//MVC的三层调用 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) { // ... } } }
例五
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(); } }
例七
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(); } } //其中 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(); } }
看完相信你一定有所收获,一起加油!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)