java - 覆盖同步锁中使用的变量是否会阻止其垃圾回收?

标签 java multithreading concurrency

我在我的服务中有一个缓存作为成员变量,我正在创建一个方法来通过 JMX MBean 公开它,这样我就可以在运行时使用新的缓存到期时间拆除并重新创建我的 vogon 缓存:

public class CachedVogonService implements CachedVogonServiceMBean {

    private LoadingCache<String, Vogon> cache;
    private long expiryInSeconds;
    private VogonService service;

    public CachedVogonService(VogonService newService,
                                long newExpiryInSeconds) {
        this.expiryInSeconds = newExpiryInSeconds;
        this.service = newService;
        this.cache = createCache(newService, newExpiryInSeconds);
    }

    private LoadingCache<String, Vogon> createCache(
            VogonService newService,
            long expiryInSeconds) {
        return CacheBuilder.newBuilder()
                .refreshAfterWrite(expiryInSeconds, TimeUnit.SECONDS)
                .build(new VogonCacheLoader(
                        Executors.newCachedThreadPool(), newService));
    }

    /**
     * This is the method I am exposing in JMX
     */    
    @Override
    public void setExpiryInSeconds(long newExpiryInSeconds) {
        this.expiryInSeconds = newExpiryInSeconds;
        synchronized (this.cache) {
            this.cache = createCache(service, expiryInSeconds);
        }
    }

我担心我的锁定技术会导致 JVM 保留对旧缓存的引用并防止它被垃圾收集。

如果我的服务对象丢失了对同步块(synchronized block)内旧缓存的引用,那么当执行退出该 block 时,它可能会留下旧缓存对象仍然标记为锁定 - 使其无法用于垃圾回收吗?

最佳答案

通过查看为类似情况生成的字节码,我们可以看到锁定字段的对象地址是重复的,用于获取和释放锁。所以原始锁定对象用于锁定。

当你在synchronized block 中用新对象改变locking字段后,另一个线程可以获得新对象的锁,并可以进入synchronized代码块。像这样使用同步,不提供线程之间的同步。您应该使用另一个对象进行锁定。 (比如 final Object cacheLock = new Object())

仅供引用,这种用法不会阻止垃圾收集。由于这里提到的对象地址都在这个方法的栈帧中,一旦方法执行完毕,栈帧就会被销毁,旧对象的引用也不会留下。 (但不要像这样使用同步。)

可以查看JVM指令集here

public class SyncTest {

    private Long value;

    public static void main(String[] args) {
        new SyncTest().testLock();
    }

    public SyncTest() {
        value = new Long(1);
    }

    private void testLock() {
        synchronized (this.value) {
            this.value = new Long(15);
        }
    }
}

  private void testLock();
     0  aload_0 [this]
     1  getfield t1.SyncTest.value : java.lang.Long [27] // push value field to operand stack
     4  dup // duplicate value field push it to operand stack
     5  astore_1 // store the top of the operand stack at [1] of local variable array (which is the duplicated value field)
     6  monitorenter // aquire lock on top of the operand stack 
     7  aload_0 [this]
     8  new java.lang.Long [22]
    11  dup
    12  ldc2_w <Long 15> [31]
    15  invokespecial java.lang.Long(long) [24]
    18  putfield t1.SyncTest.value : java.lang.Long [27]
    21  aload_1 // load the [1] of local variable array to the operand stack (which is the duplicated value field previously stored)
    22  monitorexit // release the lock on top of the operand stack
    23  goto 29
    26  aload_1
    27  monitorexit
    .....

关于java - 覆盖同步锁中使用的变量是否会阻止其垃圾回收?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54518024/

相关文章:

java - Spring REST - 正在下载损坏/空白文件

java - 可运行类对象

c# - 有人可以解释这里发生的Parallel.ForEach循环逻辑吗?

java - 类内部线程安全

java - ConcurrentHashMap 的迭代器给出奇怪的结果

java - Spring 安全: Encrypt password

java - eBay 沙箱用户无法出售元素

java - 线程中的异常 "main"java.lang.IllegalStateException : The driver executable does not exist while running Selenium Test on Ubuntu

java - 如何使用 Hibernate 控制 INSERT 语句中的并发性

java - 复合对象的同步方法如何工作