大家好,我是北军。在本教程中,我们将讨论AtomicInteger和AtomicReference等Java原子类的方法set()和lazySet()之间的区别。原子变量Java中的原子变量使我们能够轻松地对类引用或字段执行线程安全操作,而无需添加并发原语(如监视器或互斥锁)。它们在java.util.concurrent.atomic包下定义,虽然它们的API因原子类型而异,但大多数都支持set()和lazySet()方法。为简单起见,我们将在本文中使用AtomicReference和AtomicInteger,但相同的原则适用于其他原子类型。当我们在调用set()后从不同的线程使用get()方法访问该字段时,set()方法立即可见。这意味着该值从CPU缓存刷新到所有CPU内核共用的内存层。为了演示上述功能,让我们创建一个最小的生产者-消费者控制台应用程序。公共类应用程序{AtomicIntegeratomic=newAtomicInteger(0);publicstaticvoidmain(String[]args){Applicationapp=newApplication();newThread(()->{for(inti=0;i<10;i++){app.atomic.set(i);System.out.println("Set:"+i);Thread.sleep(100);}})。开始();newThread(()->{for(inti=0;i<10;i++){synchronized(app.atomic){intcounter=app.atomic.get();System.out.println("Get:"+counter);}Thread.sleep(100);}}).start();在控制台中,我们应该看到一系列“设置”和“获取”消息。Set:3Set:4Get:4Get:5表示缓存一致性的是“Get”语句中的值总是等于或大于它上面的“Set”语句中的值。.此行为虽然非常有用,但会影响性能。如果我们可以在不需要缓存一致性的情况下避免它,那就太好了。lazySet()方法lazySet()方法与set()方法相同,但不刷新缓存。换句话说,我们的更改最终只对其他线程可见。这意味着从不同的线程在更新的AtomicReference上调用get()可能会给我们旧值。要看到这一点,让我们更改我们之前的控制台应用程序中第一个线程的Runnable。for(inti=0;i<10;i++){app.atomic.lazySet(i);System.out.println("集合:"+i);Thread.sleep(100);}新的“设置”和“获取”信息可能并不总是增量的。Set:4Set:5Get:4Get:5由于线程的性质,我们可能需要重新运行应用程序几次才能触发此行为。即使生产者线程已将AtomicInteger设置为5,消费者线程首先检索到值4,这意味着当使用lazySet()时,系统最终是一致的。在更专业的术语中,我们说lazySet()方法不充当代码中的先行边缘,而不是它们的set()对应物。什么时候使用lazySet()?不清楚什么时候应该使用lazySet(),因为它与set()有细微的差别。我们需要仔细分析这个问题,不仅要保证我们会得到一个性能的提升,还要保证在多线程环境下的正确性。我们可以做到这一点的一种方法是,一旦我们不再需要它,就用null替换对对象的引用。通过这种方式,我们表明该对象有资格进行垃圾回收,而不会造成任何性能损失。我们假设其他线程可以使用丢弃的值,直到它们看到AtomicReference为null。不过,一般来说,当我们想要对原子变量进行修改时,我们应该使用lazySet(),而且我们知道修改不需要立即对其他线程可见。总结在本文中,我们了解了原子类的set()和lazySet()方法之间的区别。我们还学习了何时使用哪种方法。
