您的浏览器过于古老 & 陈旧。为了更好的访问体验, 请 升级你的浏览器
评论 二周 评论了Ready文章 · 2020年01月06日 11:53

面向开发者的 Web 应用安全入门指南(8):线程并发安全

> 卖票:可以使用`AtomicInteger`来避免同步或者加锁 ``` import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; public class TicketCounter { /** 剩余可售的票数 */ static AtomicInteger ticket = new AtomicInteger(100); public static void sellTicket() { while (true) { // 引用 ① int old = ticket.get(); if (old == 0) { break; } int update = old - 1; try { Thread.sleep(10); // 延迟 10ms 以模拟执行额外业务处理逻辑所需的时间,也给后续线程留下充足的启动时间 } catch (InterruptedException e) { // ignore exception e.printStackTrace(); break; // 发生异常时中断循环 } if (ticket.compareAndSet(old, update)) { System.out.println("售票点 [" + Thread.currentThread().getName() + "] 正在售出,当前售票序号:" + old); } } } public static void main(String[] args) { final int threadCount = 5; final ExecutorService service = Executors.newFixedThreadPool(threadCount); for (int i = 0; i < threadCount; i++) { // 此处代码基于 Java 8+,如果是低版本,请使用 Runnable 接口实现替代 service.execute(TicketCounter::sellTicket); } service.shutdown(); // 这里不是立即关闭线程池,而是不再接受新任务 } } ``` > 案例四的解决方案一存在的问题: 虽然`synchronized`关键字保证了`updateConfig`和`getConfig`这两个方法的并发安全性,但是由于`config`对象是应用传递,存在线程1拿到`config`对象以后线程2调用了`updateConfig`方法,比如线程2更新了`username`还未更新`password`线程1就会拿到新的`username`和旧的`password`。也就是说线程1拿到的数据可能不是完整的。

评论 二周 编辑了评论 · 2020年01月05日 15:04

面向开发者的 Web 应用安全入门指南(8):线程并发安全

> 卖票:可以使用`AtomicInteger`来避免同步或者加锁 ``` import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; public class TicketCounter { /** 剩余可售的票数 */ static AtomicInteger ticket = new AtomicInteger(100); public static void sellTicket() { while (true) { // 引用 ① int old = ticket.get(); if (old == 0) { break; } int update = old - 1; try { Thread.sleep(10); // 延迟 10ms 以模拟执行额外业务处理逻辑所需的时间,也给后续线程留下充足的启动时间 } catch (InterruptedException e) { // ignore exception e.printStackTrace(); break; // 发生异常时中断循环 } if (ticket.compareAndSet(old, update)) { System.out.println("售票点 [" + Thread.currentThread().getName() + "] 正在售出,当前售票序号:" + old); } } } public static void main(String[] args) { final int threadCount = 5; final ExecutorService service = Executors.newFixedThreadPool(threadCount); for (int i = 0; i < threadCount; i++) { // 此处代码基于 Java 8+,如果是低版本,请使用 Runnable 接口实现替代 service.execute(TicketCounter::sellTicket); } service.shutdown(); // 这里不是立即关闭线程池,而是不再接受新任务 } } ``` > 案例四的解决方案一存在的问题: 1. 对外暴露了私有的`config`对象,可以不经过`updateConfig`方法直接更新属性。 2. `updateConfig`方法可能引起空指针异常