001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.util; 029 030import java.util.concurrent.locks.Condition; 031import java.util.concurrent.locks.ReentrantLock; 032 033/** 034 * Lock which can be acquired with two different priorities. 035 * 036 * <p>The lock can be only be held by one thread, but can be locked reentrantly by the owner. All waiting threads who requested the lock with high priority will be granted the lock first if it becomes available, before any threads who requested the lock with low priority. 037 */ 038public class CmsPriorityLock { 039 040 /** The current thread holding the lock. */ 041 private Thread m_holder = null; 042 043 /** How many times the current lock owner, if any, is holding the lock. */ 044 private int m_holdCount = 0; 045 046 /** The underlying lock. */ 047 private ReentrantLock m_lock = new ReentrantLock(true); 048 049 /** Condition object used to signal that high priority waiters can acquire the lock. */ 050 private Condition m_highCond = m_lock.newCondition(); 051 052 /** Condition object used to signal that low priority waiters can acquire the lock. */ 053 private Condition m_lowCond = m_lock.newCondition(); 054 055 /** Number of waiters who want to acquire the lock with high priority. */ 056 private int m_waitHigh; 057 058 /** Number of waiters who want to acquire the lock with low priority. */ 059 private int m_waitLow; 060 061 /** 062 * Creates a new instance. 063 */ 064 public CmsPriorityLock() { 065 066 } 067 068 /** 069 * Acquire the lock, with given priority. 070 * 071 * @param highPriority true for high-priority acquisition, false for low-priority 072 */ 073 public void lock(boolean highPriority) { 074 075 m_lock.lock(); 076 try { 077 Thread current = Thread.currentThread(); 078 if (current == m_holder) { 079 m_holdCount++; 080 return; 081 } 082 if (highPriority) { 083 m_waitHigh += 1; 084 while (m_holder != null) { 085 m_highCond.awaitUninterruptibly(); 086 } 087 m_waitHigh -= 1; 088 m_holder = current; 089 m_holdCount = 1; 090 } else { 091 m_waitLow += 1; 092 while ((m_holder != null) || (m_waitHigh > 0)) { 093 m_lowCond.awaitUninterruptibly(); 094 } 095 m_waitLow -= 1; 096 m_holder = current; 097 m_holdCount = 1; 098 } 099 } finally { 100 m_lock.unlock(); 101 } 102 103 } 104 105 /** 106 * Releases the lock. 107 * 108 * @throws IllegalMonitorStateException if called by a thread not owning the lock 109 */ 110 public void unlock() { 111 112 m_lock.lock(); 113 try { 114 Thread current = Thread.currentThread(); 115 if (current != m_holder) { 116 throw new IllegalMonitorStateException(); 117 } 118 m_holdCount -= 1; 119 if (m_holdCount == 0) { 120 m_holder = null; 121 if (m_waitHigh > 0) { 122 m_highCond.signal(); 123 } else if (m_waitLow > 0) { 124 m_lowCond.signal(); 125 } 126 } 127 } finally { 128 m_lock.unlock(); 129 } 130 131 } 132}