groovy.transform.Synchronized Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package groovy.transform;
import org.codehaus.groovy.transform.GroovyASTTransformationClass;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Method annotation to make a method call synchronized for concurrency handling
* with some useful baked-in conventions.
*
* {@code @Synchronized} is a safer variant of the synchronized
method modifier.
* The annotation can only be used on static and instance methods. It operates similarly to
* the synchronized
keyword, but it locks on different objects. When used with
* an instance method, the synchronized
keyword locks on this
, but the annotation
* locks on a (by default automatically generated) field named $lock
.
* If the field does not exist, it is created for you. If you annotate a static method,
* the annotation locks on a static field named $LOCK
instead.
*
* If you want, you can create these locks yourself.
* The $lock
and $LOCK
fields will not be generated if you create
* them yourself. You can also choose to lock on another field, by specifying its name as
* parameter to the {@code @Synchronized} annotation. In this usage variant, the lock field
* will not be created automatically, and you must explicitly create it yourself.
*
* Rationale: Locking on this
or your own class object can have unfortunate side-effects,
* as other code not under your control can lock on these objects as well, which can
* cause race conditions and other nasty threading-related bugs.
*
* Example usage:
*
* class SynchronizedExample {
* private final myLock = new Object()
*
* {@code @}Synchronized
* static void greet() {
* println "world"
* }
*
* {@code @}Synchronized
* int answerToEverything() {
* return 42
* }
*
* {@code @}Synchronized("myLock")
* void foo() {
* println "bar"
* }
* }
*
* which becomes:
*
* class SynchronizedExample {
* private static final $LOCK = new Object[0]
* private final $lock = new Object[0]
* private final myLock = new Object()
*
* static void greet() {
* synchronized($LOCK) {
* println "world"
* }
* }
*
* int answerToEverything() {
* synchronized($lock) {
* return 42
* }
* }
*
* void foo() {
* synchronized(myLock) {
* println "bar"
* }
* }
* }
*
*
* Credits: this annotation is inspired by the Project Lombok annotation of the
* same name. The functionality has been kept similar to ease the learning
* curve when swapping between these two tools.
*
* Details: If $lock
and/or $LOCK
are auto-generated, the fields are initialized
* with an empty Object[]
array, and not just a new Object()
as many snippets using
* this pattern tend to use. This is because a new Object
is NOT serializable, but
* a 0-size array is. Therefore, using {@code @Synchronized} will not prevent your
* object from being serialized.
*
More examples:
*
* import groovy.transform.Synchronized
*
* class Util {
* private counter = 0
*
* private def list = ['Groovy']
*
* private Object listLock = new Object[0]
*
* @Synchronized
* void workOnCounter() {
* assert 0 == counter
* counter++
* assert 1 == counter
* counter --
* assert 0 == counter
* }
*
* @Synchronized('listLock')
* void workOnList() {
* assert 'Groovy' == list[0]
* list << 'Grails'
* assert 2 == list.size()
* list = list - 'Grails'
* assert 'Groovy' == list[0]
* }
* }
*
* def util = new Util()
* def tc1 = Thread.start {
* 100.times {
* util.workOnCounter()
* sleep 20
* util.workOnList()
* sleep 10
* }
* }
* def tc2 = Thread.start {
* 100.times {
* util.workOnCounter()
* sleep 10
* util.workOnList()
* sleep 15
* }
* }
* tc1.join()
* tc2.join()
*
*
* @author Paul King
* @since 1.7.3
*/
@java.lang.annotation.Documented
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.METHOD})
@GroovyASTTransformationClass("org.codehaus.groovy.transform.SynchronizedASTTransformation")
public @interface Synchronized {
/**
* @return if a user specified lock object with the given name should be used
*/
String value () default "";
}