体验 Java 并发 api,用不同方式实现信号量锁(Semaphore)(7)

来源:互联网 发布:怎么让访客网络不影响 编辑:程序博客网 时间:2024/06/05 20:07
/* * ---------------------------------------------------- *  使用LockSupport来实现可重入的信号(前面实现的都不是可重入的) *  除了共享数据本身的原子性、一致性、隔离性的考虑之外 *  作为某种锁的存在的对象常还要考虑中断和重入 *  中断的情况最开始的测试代码中有提到,需要根据具体的情况来考虑如何处理。 *  而重入则是重复对某个带锁方法的调用,有的时候是有意的,有时则是误调用,都需要引起注意。 * ---------------------------------------------------- */class ReentrantSema implements IMySemaphore {    private AtomicInteger _iSignal;    private final int _iPermits;    private Queue<Thread> waiters = new ConcurrentLinkedQueue<Thread> ();    // 对于并发线程重入次数需要记录    private ConcurrentHashMap<Thread, Integer> runningThread = new ConcurrentHashMap<Thread, Integer> ();    ReentrantSema(final int i) {        _iSignal = new AtomicInteger(0);        _iPermits = i;    }    public void acquire() throws InterruptedException {        Thread current = Thread.currentThread();        // 判断是否已得到信号量        Integer cnt = runningThread.get(current);        if (cnt != null) {            if (Thread.interrupted()) {                throw new InterruptedException();            }            runningThread.put(current, new Integer(cnt + 1));            return;        }        boolean wasInterrupted = false;        waiters.add(current);        int i, update;        while (true) {            i = _iSignal.get();            if (waiters.peek() != current || (!wasInterrupted && (i >= _iPermits))) {                // 申请条件不够                LockSupport.park(this);                if (Thread.interrupted()) {                    wasInterrupted = true;                    continue;                }            }            if (wasInterrupted) {                break;            }            if (i >= _iPermits) {                continue;            }            // 利用 AtomicInteger 的 CAS 操作确保申请到信号量            update = i + 1;            if (waiters.peek() == current && _iSignal.compareAndSet(i, update)) {                // get the lock                runningThread.put(current, new Integer(1));                break;            }        }        // 唤醒下一个等待线程        waiters.remove();        LockSupport.unpark(waiters.peek());        if (wasInterrupted) {            throw new InterruptedException();        }    }    public void release() {        Thread current = Thread.currentThread();        Integer cnt = runningThread.get(current);        assert (cnt != null);        // 释放信号时要注意可能只是从某个层次退出        // 完全退出后才能真正释放        if (cnt == 1) {            runningThread.remove(current);            _iSignal.decrementAndGet();            LockSupport.unpark(waiters.peek());        } else {            runningThread.put(current, new Integer(cnt - 1));        }    }} // END: ReentrantSema/** * 带重入方法的 Worker, 会多次调用 acquire 方法 */class ReentrantWorker implements Runnable {    private final int _id;    private final long _wait;    private final IMySemaphore _semaphore;    private final long _lCreateTime;    private static void println(final String msg) {        System.out.println(msg);    }    ReentrantWorker(final int id, final IMySemaphore semaphore, final long now) {        _id = id;        _wait = (10 - _id) * 1000;        _semaphore = semaphore;        _lCreateTime = now;    }    public void run() {        try {            // 第一次申请            _semaphore.acquire();            try {                println(String.format("Worker[%d] is running...", _id));                // 重入,子类中很可能会误调用某个已经申请了信号量的方法                // 如果信号量不支持重入,就可能引起死锁                reentrant();                long period = (System.currentTimeMillis() - _lCreateTime) / 1000;                println(String.format("Worker[%d] is end, %d seconds after created.", _id, period));            } catch (InterruptedException ie) {                // swap exception to somewhere                // report and stop this thread            } finally {                // 释放                _semaphore.release();            }        } catch (InterruptedException ie) {            // 申请时被中断            long period = (System.currentTimeMillis() - _lCreateTime) / 1000;            println(String.format("Worker[%d] is interrupted, %d seconds after created.", _id, period));            return;        }    }    private void reentrant() throws InterruptedException {        try {            // 申请            _semaphore.acquire();            try {                long begin = System.currentTimeMillis();                println(String.format("Worker[%d] reentrant success...", _id));                // 又重入一次                reentrant2();                long period = (System.currentTimeMillis() - begin) / 1000;                println(String.format("Worker[%d] reentrant end, %d seconds after created.", _id, period));            } catch (InterruptedException ie) {                throw ie;            } finally {                // 释放                _semaphore.release();            }        } catch (InterruptedException ie) {            // 申请被中断            throw ie;        }    }    private void reentrant2() throws InterruptedException {        try {            // 申请            _semaphore.acquire();            try {                long begin = System.currentTimeMillis();                println(String.format("Worker[%d] reentrant 2 success...", _id));                Thread.currentThread().sleep(_wait);                long period = (System.currentTimeMillis() - begin) / 1000;                println(String.format("Worker[%d] reentrant 2 end, %d seconds after created.", _id, period));            } catch (InterruptedException ie) {                throw ie;            } finally {                // 释放                _semaphore.release();            }        } catch (InterruptedException ie) {            // 申请被中断            throw ie;        }    }} // END: ReentrantWorker

还有更多的方法可以继续体验……

0 0
原创粉丝点击