001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.commons.concurrent; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021import java.util.concurrent.locks.*; 022 023/** 024 * A simple auto-closeable wrapper around a {@link Lock} that automatically acquires the lock 025 * when created and releases it when closed. 026 * 027 * <p> 028 * This class wraps any {@link Lock} implementation and makes it work seamlessly with try-with-resources 029 * statements. The lock is automatically acquired in the constructor and released when {@link #close()} 030 * is called (either explicitly or automatically via try-with-resources). 031 * 032 * <p> 033 * This class is typically used indirectly through {@link SimpleReadWriteLock#read()} and 034 * {@link SimpleReadWriteLock#write()}, but can also be used directly with any {@link Lock} instance. 035 * 036 * <h5 class='section'>Example:</h5> 037 * <p class='bjava'> 038 * <jc>// Using with a ReentrantLock</jc> 039 * <jk>var</jk> <jv>lock</jv> = <jk>new</jk> ReentrantLock(); 040 * <jk>try</jk> (<jk>var</jk> <jv>simpleLock</jv> = <jk>new</jk> SimpleLock(<jv>lock</jv>)) { 041 * <jc>// Lock is automatically held - perform critical section operations</jc> 042 * <jv>sharedResource</jv>.modify(); 043 * } 044 * <jc>// Lock is automatically released when exiting try block</jc> 045 * 046 * <jc>// Using with SimpleReadWriteLock</jc> 047 * <jk>var</jk> <jv>rwLock</jv> = <jk>new</jk> SimpleReadWriteLock(); 048 * <jk>try</jk> (<jk>var</jk> <jv>readLock</jv> = <jv>rwLock</jv>.read()) { 049 * <jc>// Read lock is automatically held</jc> 050 * <jv>sharedResource</jv>.read(); 051 * } 052 * <jc>// Read lock is automatically released</jc> 053 * </p> 054 * 055 * <h5 class='section'>Thread Safety:</h5> 056 * <p> 057 * This class is not thread-safe itself. Each instance wraps a single {@link Lock} and should be used 058 * by a single thread. The underlying lock's thread-safety guarantees apply to the wrapped lock. 059 * 060 * <h5 class='section'>Notes:</h5><ul> 061 * <li class='note'>The lock is automatically acquired in the constructor and released in {@link #close()}. 062 * <li class='note'>If <jk>null</jk> is passed to the constructor, no locking operations are performed 063 * (useful for creating no-op locks). 064 * <li class='note'>This class implements {@link AutoCloseable}, making it ideal for use with 065 * try-with-resources statements. 066 * <li class='note'>Multiple calls to {@link #close()} are safe - subsequent calls have no effect if 067 * the lock has already been released. 068 * <li class='note'>The underlying lock's reentrancy behavior is preserved - if the lock supports 069 * reentrant locking, the same thread can acquire it multiple times. 070 * </ul> 071 * 072 * <h5 class='section'>See Also:</h5><ul> 073 * <li class='jc'>{@link SimpleReadWriteLock} - A read-write lock that returns {@link SimpleLock} instances 074 * <li class='jc'>{@link Lock} - The underlying lock interface 075 * </ul> 076 */ 077@SuppressWarnings({ 078 "resource" // Intentional singleton; wraps a null lock so close() is a no-op 079}) 080public class SimpleLock implements AutoCloseable { 081 082 /** 083 * A no-op lock instance that performs no actual locking operations. 084 * 085 * <p> 086 * This instance can be used when locking is conditionally disabled or when you need a 087 * {@link SimpleLock} instance but don't want any actual synchronization overhead. 088 * All lock operations on this instance are no-ops. 089 * 090 * <h5 class='section'>Example:</h5> 091 * <p class='bjava'> 092 * <jc>// Use NO_OP when locking is disabled</jc> 093 * <jk>var</jk> <jv>lock</jv> = isLockingEnabled() ? <jk>new</jk> SimpleLock(<jk>new</jk> ReentrantLock()) : SimpleLock.NO_OP; 094 * <jk>try</jk> (<jk>var</jk> <jv>simpleLock</jv> = <jv>lock</jv>) { 095 * <jc>// Code works the same whether locking is enabled or not</jc> 096 * <jv>sharedResource</jv>.modify(); 097 * } 098 * </p> 099 * 100 * <h5 class='section'>Notes:</h5><ul> 101 * <li class='note'>Calling {@link #close()} on this instance has no effect. 102 * <li class='note'>This instance is thread-safe and can be shared across threads. 103 * <li class='note'>This instance wraps a <jk>null</jk> lock, so no actual locking occurs. 104 * </ul> 105 */ 106 public static final SimpleLock NO_OP = new SimpleLock(null); 107 108 private final Lock lock; 109 110 /** 111 * Constructor. 112 * 113 * <p> 114 * Creates a new {@link SimpleLock} wrapper around the specified {@link Lock}. The lock is 115 * automatically acquired during construction. If <jk>null</jk> is passed, no locking operations 116 * are performed (useful for creating no-op locks). 117 * 118 * <h5 class='section'>Example:</h5> 119 * <p class='bjava'> 120 * <jc>// Wrap a ReentrantLock</jc> 121 * <jk>var</jk> <jv>underlyingLock</jv> = <jk>new</jk> ReentrantLock(); 122 * <jk>var</jk> <jv>simpleLock</jv> = <jk>new</jk> SimpleLock(<jv>underlyingLock</jv>); 123 * <jc>// Lock is now held - use try-with-resources to ensure release</jc> 124 * <jk>try</jk> (<jv>simpleLock</jv>) { 125 * <jc>// Critical section</jc> 126 * } 127 * </p> 128 * 129 * <h5 class='section'>Notes:</h5><ul> 130 * <li class='note'>The lock is acquired immediately in the constructor. This method may block 131 * if the lock is not available. 132 * <li class='note'>If <jk>null</jk> is passed, no lock is acquired and {@link #close()} will have no effect. 133 * <li class='note'>The underlying lock's behavior (fairness, reentrancy, etc.) is preserved. 134 * </ul> 135 * 136 * @param lock The {@link Lock} being wrapped. Can be <jk>null</jk> to create a no-op lock. 137 */ 138 @SuppressWarnings({ 139 "java:S2222" // Lock is intentionally held past constructor return and released in close() per the AutoCloseable contract. 140 }) 141 public SimpleLock(Lock lock) { 142 this.lock = lock; 143 if (nn(lock)) 144 lock.lock(); 145 } 146 147 /** 148 * Releases the lock. 149 * 150 * <p> 151 * This method is called automatically when used in a try-with-resources statement, or can be 152 * called explicitly to release the lock early. If the lock was <jk>null</jk> (no-op lock), 153 * this method has no effect. 154 * 155 * <h5 class='section'>Example:</h5> 156 * <p class='bjava'> 157 * <jk>var</jk> <jv>lock</jv> = <jk>new</jk> SimpleLock(<jk>new</jk> ReentrantLock()); 158 * <jc>// Lock is held</jc> 159 * <jv>lock</jv>.close(); <jc>// Explicitly release the lock</jc> 160 * </p> 161 * 162 * <h5 class='section'>Notes:</h5><ul> 163 * <li class='note'>This method is called automatically when exiting a try-with-resources block. 164 * <li class='note'>Multiple calls to this method are safe - subsequent calls have no effect if 165 * the lock has already been released. 166 * <li class='note'>If the lock was <jk>null</jk> (no-op lock), this method returns immediately 167 * without performing any operations. 168 * <li class='note'>The underlying lock's unlock behavior is preserved (e.g., reentrant locks 169 * must be unlocked the same number of times they were locked). 170 * </ul> 171 * 172 * @throws IllegalMonitorStateException If the current thread does not hold this lock 173 * (only applies to certain lock implementations). 174 */ 175 @Override 176 public void close() { 177 if (nn(lock)) 178 lock.unlock(); 179 } 180}