ThreadLocal、ConcurrentHashMap、CopyOnWriteArrayList


ThreadLocal使用场景:

在一个线程中传递同一个对象即适用于变量在线程间隔离,而在方法或类间共享的场景。(横跨若干方法调用,需要传递的对象,我们通常称之为上下文(Context),它是一种状态,可以是用户身份、任务信息等)

例如:可以在ThreadLocal中缓存数据,避免了同一参数在所有方法中传递

public class OrderContext implements AutoCloseable {

    static final ThreadLocal ctx = 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;
        }
    }

相关