Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/374.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
这个Java缓存是线程安全和优化的吗?_Java_Concurrency - Fatal编程技术网

这个Java缓存是线程安全和优化的吗?

这个Java缓存是线程安全和优化的吗?,java,concurrency,Java,Concurrency,这个Java缓存是否更安全或更快 public class Cache { private long lastupdateTime = 0L; private String cachedData = null; public String getData() { long now = System.currentTimeMillis(); if (now - lastupdateTime < 30000) {

这个Java缓存是否更安全或更快

public class Cache {
    private long lastupdateTime = 0L;
    private String cachedData = null;
    public String getData() {
        long now = System.currentTimeMillis();
        if (now - lastupdateTime < 30000) {
            return cachedData;
        }
        else {
            synchronized (this) {
                if (now - lastupdateTime < 30000) {
                    return cachedData;
                }
                else {
                    lastupdateTime = now;
                    cachedData = getDataFromDatabase();
                    return cachedData;
                }
            }
        }
    }
    private String getDataFromDatabase() { ... }
}
公共类缓存{
私有长lastupdateTime=0L;
私有字符串cachedData=null;
公共字符串getData(){
long now=System.currentTimeMillis();
如果(现在-lastupdateTime<30000){
返回缓存数据;
}
否则{
已同步(此){
如果(现在-lastupdateTime<30000){
返回缓存数据;
}
否则{
lastupdateTime=now;
cachedData=getDataFromDatabase();
返回缓存数据;
}
}
}
}
私有字符串getDataFromDatabase(){…}
}

是。即使忽略您没有使
lastupdateTime
cachedData
易失性的事实

                lastupdateTime = now;
                cachedData = getDataFromDatabase();
它坏了。
如果
getDataFromDatabase
异常失败,您将已经更新了
lastupdateTime
,因此将继续返回过时数据30秒,如果
getDataFromDatabase
第一次尝试失败,则可能返回
null

通过将
lastUpdateTime
初始化为
Long.MIN_值
,您将不会丢失任何东西,并且在时钟已向后设置的系统上工作更可靠


System.getCurrentTimeMillis
可以运行,这不太可能影响30秒的缓存,但是对于这个用例,没有理由不使用
System.nanoTime()

这里有一个想法-基于
同步
是一种过时且低效的机制的原理

这里我使用一个
AtomicReference
来指示缓存正在更新。
Phaser
可用于等待更新完成

public class Test {

  public static class Cache {
    // Cache timeout.
    private static final long Timeout = 10000;
    // Last time we updated.
    private volatile long lastupdateTime = 0L;
    // The cached data.
    private volatile String cachedData = null;
    // Cache is in the progress of updating.
    private AtomicReference<Phaser> updating = new AtomicReference<>();
    // The next Phaser to use - avoids unnecessary Phaser creation.
    private Phaser nextPhaser = new Phaser(1);

    public String getData() {
      // Do this just once.
      long now = System.currentTimeMillis();
      // Watch for expiry.
      if (now - lastupdateTime > Timeout) {
        // Make sure only one thread updates.
        if (updating.compareAndSet(null, nextPhaser)) {
          // We are the unique thread that gets to do the updating.
          System.out.println(Thread.currentThread().getName() + " - Get ...");
          // Get my new cache data.
          cachedData = getDataFromDatabase();
          lastupdateTime = now;
          // Make the Phaser to use next time - avoids unnecessary creations.
          nextPhaser = new Phaser(1);
          // Get the queue and clear it - there cannot be any new joiners after this.
          Phaser queue = updating.getAndSet(null);
          // Inform everyone who is waiting that they can go.
          queue.arriveAndDeregister();
        } else {
          // Wait in the queue.
          Phaser queue = updating.get();
          if (queue != null) {
            // Wait for it.
            queue.register();
            System.out.println(Thread.currentThread().getName() + " - Waiting ...");
            queue.arriveAndAwaitAdvance();
            System.out.println(Thread.currentThread().getName() + " - Back");
          }
        }
      }
      // Let them have the correct data.
      return cachedData;
    }

    private String getDataFromDatabase() {
      try {
        // Deliberately wait for a bit.
        Thread.sleep(5000);
      } catch (InterruptedException ex) {
        // Ignore.
      }
      System.out.println(Thread.currentThread().getName() + " - Hello");
      return "Hello";
    }
  }

  public void test() throws InterruptedException {
    System.out.println("Hello");
    // Start time.
    final long start = System.currentTimeMillis();
    // Make a Cache.
    final Cache c = new Cache();
    // Make some threads.
    for (int i = 0; i < 20; i++) {
      Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
          while (System.currentTimeMillis() - start < 60000) {
            c.getData();
            try {
              Thread.sleep(500);
            } catch (InterruptedException ex) {
              // Ignore.
            }
          }
        }
      });
      t.setName("Thread - " + i);
      t.start();
      // Stagger the threads.
      Thread.sleep(300);
    }
  }

  public static void main(String args[]) throws InterruptedException {
    new Test().test();
  }
}
公共类测试{
公共静态类缓存{
//缓存超时。
私有静态最终长超时=10000;
//上次我们更新了。
私有volatile long lastupdateTime=0L;
//缓存的数据。
私有易失性字符串cachedData=null;
//缓存正在更新中。
私有AtomicReference更新=新建AtomicReference();
//下一个要使用的相位器-避免不必要的相位器创建。
专用相位器nextPhaser=新相位器(1);
公共字符串getData(){
//只做一次。
long now=System.currentTimeMillis();
//注意过期。
如果(现在-lastupdateTime>超时){
//确保只有一个线程更新。
if(更新.compareAndSet(null,nextPhaser)){
//我们是唯一一个进行更新的线程。
System.out.println(Thread.currentThread().getName()+“-Get…”);
//获取我的新缓存数据。
cachedData=getDataFromDatabase();
lastupdateTime=now;
//使相位器下次使用-避免不必要的创建。
下一相=新相器(1);
//获取队列并清除它-在此之后不能有任何新的加入者。
Phaser queue=updating.getAndSet(null);
//通知正在等待的每个人,他们可以走了。
queue.arriveanddelegister();
}否则{
//排队等候。
Phaser queue=更新.get();
if(队列!=null){
//等等。
register();
System.out.println(Thread.currentThread().getName()+“-Waiting…”);
queue.arriveandWaitAdvance();
System.out.println(Thread.currentThread().getName()+“-Back”);
}
}
}
//让他们有正确的数据。
返回缓存数据;
}
私有字符串getDataFromDatabase(){
试一试{
//故意等一会儿。
睡眠(5000);
}捕获(中断异常例外){
//忽略。
}
System.out.println(Thread.currentThread().getName()+“-Hello”);
回复“你好”;
}
}
public void test()引发InterruptedException{
System.out.println(“你好”);
//开始时间。
最终长启动=System.currentTimeMillis();
//做一个缓存。
最终缓存c=新缓存();
//做一些线。
对于(int i=0;i<20;i++){
线程t=新线程(新的可运行线程(){
@凌驾
公开募捐{
同时(System.currentTimeMillis()-开始<60000){
c、 getData();
试一试{
睡眠(500);
}捕获(中断异常例外){
//忽略。
}
}
}
});
t、 集合名(“线程-”+i);
t、 start();
//错开线。
睡眠(300);
}
}
公共静态void main(字符串args[])引发InterruptedException{
新测试().Test();
}
}
请注意,这是我第一次使用
移相器
——我希望我没有弄错


请注意,如果超时时间超过从数据库获取数据所需的时间,此算法可能会崩溃

您的
Cash
类有两个共享的*mutable*状态
lastupdateTime
cachedData
getData()
当前可由两个不同的线程调用,它使用一个局部变量
现在
,其他线程可以看到该变量(位于
堆栈中)

怎么了?
  • 首先
    如果(现在-lastupdateTime<30000){return cachedData;}
    ,其他线程可以看到
    lastupdateTime
    cachedData
    上的
    stale
    数据(您正在访问一个红色的可变状态,没有同步保证)

  • now
    变量形式,如带有
    lastupdateTime
    不变量,因此
    now-lastupdateTime
    应在
    原子块中执行

我能做什么? 只需使
满足
public class Cache {
    private long lastupdateTime = 0L;
    private String cachedData = null;
    public synchronized String getData() {
           long now = System.currentTimeMillis()
           if (nano - lastupdateTime < 30000) {
                return cachedData;
           }
           else {
               lastupdateTime = now;
               cachedData = getDataFromDatabase();
               return cachedData;
           }                
    }
    private String getDataFromDatabase() { ... }
}