+ private void queueFireContentChangeEvent()
+ {
+ long sleepTime;
+ boolean fireInOwnThread = false;
+ synchronized (contentChangeLock)
+ {
+ if (contentChangeQueued)
+ return;
+ long nextContentChangeAllowedMillisLocal = nextContentChangeAllowedMillis;
+ long now = System.currentTimeMillis();
+ sleepTime = nextContentChangeAllowedMillisLocal - now;
+ if (sleepTime >= 0)
+ {
+ fireInOwnThread = true;
+ contentChangeQueued = true;
+ nextContentChangeAllowedMillis = nextContentChangeAllowedMillisLocal + maxContentChangeInterval;
+ } else
+ {
+ fireInOwnThread = false;
+ nextContentChangeAllowedMillis = now + maxContentChangeInterval;
+ }
+ }
+ if (fireInOwnThread)
+ {
+ // the following two statements can't cause racing problems since we set contentChangeQueued to true,
+ // which means no-one will write this field until the thread started:
+ // this method will never get here, and in this moment there is no (other) content change thread running,
+ // since contentChangeQueued was false
+ contentChangeThread = new Thread(() ->
+ {
+ boolean interrupted = false;
+ try
+ {
+ Thread.sleep(sleepTime);
+ }
+ catch (@SuppressWarnings("unused") InterruptedException e)
+ {
+ interrupted = true;
+ }
+ synchronized (contentChangeLock)
+ {
+ contentChangeThread = null;
+ contentChangeQueued = false;
+ }
+ if (!interrupted && !Thread.interrupted())
+ fireContentChangeEventNow();
+ });
+ contentChangeThread.start();
+ } else
+ fireContentChangeEventNow();
+ }
+