JUC学习笔记——共享模型之管程( 四 )

常见题型分析我们直接给出代码来进行题型分析:
/*题目1*/// MyServlet是Servlet类,应用于Tomcat的多线程上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) {// 使用上述变量}}/*题目2*/public class MyServlet extends HttpServlet {// 是否安全?不是 , 底层使用Impl,里面包含了count这个共享数据 , 且没有使用锁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++;}}/*题目3*/// 这里是Spring的@Aspect,属于单例,也是共享@Aspect@Componentpublic class MyAspect {// 是否安全? 不是,start属于共享数据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));}}/*题目4*/public class MyServlet extends HttpServlet {// 是否安全 是,调用Dao,不具有可变参数private UserService userService = new UserServiceImpl();public void doGet(HttpServletRequest request, HttpServletResponse response) {userService.update(...);}}public class UserServiceImpl implements UserService {// 是否安全 是,调用Dao , 不具有可变参数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) {// ...}}}/*题目5*/public class MyServlet extends HttpServlet {// 是否安全 不是,具有conn可变参数private UserService userService = new UserServiceImpl();public void doGet(HttpServletRequest request, HttpServletResponse response) {userService.update(...);}}public class UserServiceImpl implements UserService {// 是否安全 不是,具有conn可变参数private UserDao userDao = new UserDaoImpl();public void update() {userDao.update();}}public class UserDaoImpl implements UserDao {// 是否安全 不是,具有conn可变参数private Connection conn = null;public void update() throws SQLException {String sql = "update user set password = ? where username = ?";conn = DriverManager.getConnection("","","");// ...conn.close();}}/*题目6*/public class MyServlet extends HttpServlet {// 是否安全 但是这里是安全的// 虽然底层将conn创建在堆里 , 但是impl层创建了多个userDao对象,导致产生了多个conn不产生安全问题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 {// 是否安全 不是,具有conn可变参数private Connection = null;public void update() throws SQLException {String sql = "update user set password = ? where username = ?";conn = DriverManager.getConnection("","","");// ...conn.close();}}/*题目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();}}经典习题分析我们给出两个经典习题分析:
/*卖票问题*/// 源码展示:public class ExerciseSell {public static void main(String[] args) {TicketWindow ticketWindow = new TicketWindow(2000);List<Thread> list = new ArrayList<>();// 用来存储买出去多少张票List<Integer> sellCount = new Vector<>();for (int i = 0; i < 2000; i++) {Thread t = new Thread(() -> {// 分析这里的竞态条件int count = ticketWindow.sell(randomAmount());sellCount.add(count);});list.add(t);t.start();}list.forEach((t) -> {try {t.join();} catch (InterruptedException e) {e.printStackTrace();}});// 买出去的票求和log.debug("selled count:{}",sellCount.stream().mapToInt(c -> c).sum());// 剩余票数log.debug("remainder count:{}", ticketWindow.getCount());}// Random 为线程安全static Random random = new Random();// 随机 1~5public static int randomAmount() {return random.nextInt(5) + 1;}}// 卖票窗口class TicketWindow {// 票数,属于共享数据private int count;public TicketWindow(int count) {this.count = count;}public int getCount() {return count;}public int sell(int amount) {if (this.count >= amount) {this.count -= amount;return amount;} else {return 0;}}}// 问题分析:TicketWindow卖票窗口的票属于共享数据,对共享数据的修改需要进行上锁处理,所以我们需要对sell使用synchronized// 修改展示:public class ExerciseSell {public static void main(String[] args) {TicketWindow ticketWindow = new TicketWindow(2000);List<Thread> list = new ArrayList<>();List<Integer> sellCount = new Vector<>();for (int i = 0; i < 2000; i++) {Thread t = new Thread(() -> {int count = ticketWindow.sell(randomAmount());sellCount.add(count);});list.add(t);t.start();}list.forEach((t) -> {try {t.join();} catch (InterruptedException e) {e.printStackTrace();}});log.debug("selled count:{}",sellCount.stream().mapToInt(c -> c).sum());log.debug("remainder count:{}", ticketWindow.getCount());}static Random random = new Random();public static int randomAmount() {return random.nextInt(5) + 1;}}class TicketWindow {private int count;public TicketWindow(int count) {this.count = count;}public int getCount() {return count;}//在方法上加一个synchronized即可public synchronized int sell(int amount) {if (this.count >= amount) {this.count -= amount;return amount;} else {return 0;}}}

推荐阅读