ThreadLocal、ConcurrentHashMap、CopyOnWriteArrayList
ThreadLocal使用场景:
在一个线程中传递同一个对象即适用于变量在线程间隔离,而在方法或类间共享的场景。(横跨若干方法调用,需要传递的对象,我们通常称之为上下文(Context),它是一种状态,可以是用户身份、任务信息等)
例如:可以在ThreadLocal中缓存数据,避免了同一参数在所有方法中传递
public class OrderContext implements AutoCloseable { static final ThreadLocalctx = new ThreadLocal<>(); public OrderContext(Order order) { ctx.set(order); } public static Order currentOrder() { return ctx.get(); } @Override public void close() throws Exception { ctx.remove(); } }
@Service @Slf4j public class OrderServiceImpl implements OrderService { @Override public void saveOrder(Order order) { try (OrderContext ctx = new OrderContext(order)) { notifyToPay(); notifyUser(); } catch (Exception e) { e.printStackTrace(); } } private void notifyToPay() { log.info("需要同步的订单为:{}", OrderContext.currentOrder()); } private void notifyUser() { log.info("已订购!{}", OrderContext.currentOrder()); } }
private void wrong() { log.info("wrong!{}", OrderContext.currentOrder()); } public static void main(String[] args) { String uuid = UUID.randomUUID().toString(); String orderId = uuid.replaceAll("-", ""); Long userId = 123l; Order order = new Order(userId,orderId,2.0d,1,"订单!"); OrderServiceImpl test = new OrderServiceImpl(); test.saveOrder(order); test.wrong(); }
//输出:
//需要同步的订单为:Order(userId=123, orderId=076dae2ae69e4646b5c0f679427aaa64, price=2.0, status...
//已订购!Order(userId=123, orderId=076dae2ae69e4646b5c0f679427aaa64, price=2.0, status=1, d...
//wrong!null
注意:
1. 使用类似 ThreadLocal 工具来存放一些数据时,需要特别注意在代码运行完后,显式地去清空设置的数据;
2. 在上下文类中的ctx.remove(),再次强调这里使用完需要清除数据,否则在多线程情况下,会出现跨用户/跨订单访问的情况
3. 例如在同一个线程下,第一个订单用完没有清理,第二个订单过来首次访问会访问到上一个订单。
ConcurrentHashMap使用场景:
只能保证提供的原子性读写操作是线程安全的,所以在多个操作情况下需要整体加锁。
CopyOnWriteArrayList使用场景:
首先它是线程安全的,但因实现方式每次修改数据时都会复制一份数据出来,所以有明显的适用场景,即读多写少或者说希望无锁读的场景。
/** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return {@code true} (as specified by {@link Collection#add}) */ public boolean add(E e) { synchronized (lock) { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; setArray(newElements); return true; } }