Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Here's an idea - on the principle that <code>synchronized</code> is an archaic and inefficient mechanism.</p> <p>Here I use an <code>AtomicReference&lt;Phaser&gt;</code> to indicate the cache is being updated. The <code>Phaser</code> can be used to await completion of the update.</p> <pre><code>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&lt;Phaser&gt; updating = new AtomicReference&lt;&gt;(); // 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 &gt; 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 &lt; 20; i++) { Thread t = new Thread(new Runnable() { @Override public void run() { while (System.currentTimeMillis() - start &lt; 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(); } } </code></pre> <p>Please note that this is the first time I have used a <code>Phaser</code> - I hope I have it right.</p> <p>Note that this algorithm may break down if the timeout is longer than the time it takes to get the data from the database. </p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload