package org.araqne.logstorage.engine; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Date; import java.util.Random; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.araqne.logstorage.TableLock; public class BackOffLock { private static ThreadLocal<Random> rand = new ThreadLocal<Random>() { @Override protected Random initialValue() { return new Random(); } }; private long min; private long max; private static final long MAX_WAIT = Integer.MAX_VALUE / 10; private long to; private boolean locked = false; private TableLock lock; private int tryCnt = 0; public BackOffLock(TableLock l) { this.lock = l; to = Integer.MIN_VALUE; this.min = 10000; // 0.01ms this.max = MAX_WAIT; } public BackOffLock(TableLock l, long time, TimeUnit unit) { this.lock = l; to = unit.toNanos(time); this.min = 10000; // 0.01ms this.max = (int) Math.min(unit.toNanos(time) / 100, MAX_WAIT); } private long nextBackOff() { long rmin = min; long rmax = rmin + Math.max(100000, min); // 0.1ms long ni = rmin + rand.get().nextInt((int) (rmax - rmin)); min = Math.min(min == 0 ? 1 : min * 2, max); return ni; } public boolean tryLock() throws InterruptedException { if (tryCnt++ != 0) { long cbo = nextBackOff(); if (to != Integer.MIN_VALUE) to -= cbo; return (locked = lock.tryLock(cbo, TimeUnit.NANOSECONDS) != null); } else { return (locked = lock.tryLock() != null); } } public void setDone() { to = -1; } public boolean isDone() { return locked || (to != Integer.MIN_VALUE && to < 0); } public boolean hasLocked() { return locked; } public void unlock() { if (locked) lock.unlock(); } public static void main(String[] args) throws InterruptedException { final TableLock l = new TableLock() { @Override public UUID lock() { l.lock(); return uuid = UUID.randomUUID(); } @Override public UUID lockInterruptibly() throws InterruptedException { l.lockInterruptibly(); return uuid = UUID.randomUUID(); } @Override public UUID tryLock() { if (l.tryLock()) { return uuid = UUID.randomUUID(); } else { return null; } } @Override public UUID tryLock(long time, TimeUnit unit) throws InterruptedException { if (l.tryLock(time, unit)) { return uuid = UUID.randomUUID(); } else { return null; } } @Override public void unlock() { uuid = null; l.unlock(); } @Override public int getTableId() { return 0; } @Override public String getLockOwner() { return null; } @Override public Collection<Purpose> getPurposes() { return null; } Lock l = new ReentrantLock(true); UUID uuid = null; }; l.lock(); Thread t = new Thread(new Runnable() { @Override public void run() { BackOffLock bol = new BackOffLock(l); do { boolean locked; try { locked = bol.tryLock(); if (locked) { System.out.println("locked"); } else { System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } while (!bol.isDone()); } }); t.start(); t.join(); } }