Avoid synchronized(this) in Java?

后端 未结 22 1170
予麋鹿
予麋鹿 2020-11-22 01:23

Whenever a question pops up on SO about Java synchronization, some people are very eager to point out that synchronized(this) should be avoided. Instead, they c

相关标签:
22条回答
  • 2020-11-22 02:01

    I only want to mention a possible solution for unique private references in atomic parts of code without dependencies. You can use a static Hashmap with locks and a simple static method named atomic() that creates required references automatically using stack information (full class name and line number). Then you can use this method in synchronize statements without writing new lock object.

    // Synchronization objects (locks)
    private static HashMap<String, Object> locks = new HashMap<String, Object>();
    // Simple method
    private static Object atomic() {
        StackTraceElement [] stack = Thread.currentThread().getStackTrace(); // get execution point 
        StackTraceElement exepoint = stack[2];
        // creates unique key from class name and line number using execution point
        String key = String.format("%s#%d", exepoint.getClassName(), exepoint.getLineNumber()); 
        Object lock = locks.get(key); // use old or create new lock
        if (lock == null) {
            lock = new Object();
            locks.put(key, lock);
        }
        return lock; // return reference to lock
    }
    // Synchronized code
    void dosomething1() {
        // start commands
        synchronized (atomic()) {
            // atomic commands 1
            ...
        }
        // other command
    }
    // Synchronized code
    void dosomething2() {
        // start commands
        synchronized (atomic()) {
            // atomic commands 2
            ...
        }
        // other command
    }
    
    0 讨论(0)
  • 2020-11-22 02:02
    1. Make your data immutable if it is possible ( final variables)
    2. If you can't avoid mutation of shared data across multiple threads, use high level programming constructs [e.g. granular Lock API ]

    A Lock provides exclusive access to a shared resource: only one thread at a time can acquire the lock and all access to the shared resource requires that the lock be acquired first.

    Sample code to use ReentrantLock which implements Lock interface

     class X {
       private final ReentrantLock lock = new ReentrantLock();
       // ...
    
       public void m() {
         lock.lock();  // block until condition holds
         try {
           // ... method body
         } finally {
           lock.unlock()
         }
       }
     }
    

    Advantages of Lock over Synchronized(this)

    1. The use of synchronized methods or statements forces all lock acquisition and release to occur in a block-structured way.

    2. Lock implementations provide additional functionality over the use of synchronized methods and statements by providing

      1. A non-blocking attempt to acquire a lock (tryLock())
      2. An attempt to acquire the lock that can be interrupted (lockInterruptibly())
      3. An attempt to acquire the lock that can timeout (tryLock(long, TimeUnit)).
    3. A Lock class can also provide behavior and semantics that is quite different from that of the implicit monitor lock, such as

      1. guaranteed ordering
      2. non-re entrant usage
      3. Deadlock detection

    Have a look at this SE question regarding various type of Locks:

    Synchronization vs Lock

    You can achieve thread safety by using advanced concurrency API instead of Synchronied blocks. This documentation page provides good programming constructs to achieve thread safety.

    Lock Objects support locking idioms that simplify many concurrent applications.

    Executors define a high-level API for launching and managing threads. Executor implementations provided by java.util.concurrent provide thread pool management suitable for large-scale applications.

    Concurrent Collections make it easier to manage large collections of data, and can greatly reduce the need for synchronization.

    Atomic Variables have features that minimize synchronization and help avoid memory consistency errors.

    ThreadLocalRandom (in JDK 7) provides efficient generation of pseudorandom numbers from multiple threads.

    Refer to java.util.concurrent and java.util.concurrent.atomic packages too for other programming constructs.

    0 讨论(0)
  • 2020-11-22 02:05

    It depends on the task you want to do, but I wouldn't use it. Also, check if the thread-save-ness you want to accompish couldn't be done by synchronize(this) in the first place? There are also some nice locks in the API that might help you :)

    0 讨论(0)
  • 2020-11-22 02:07

    Short answer: You have to understand the difference and make choice depending on the code.

    Long answer: In general I would rather try to avoid synchronize(this) to reduce contention but private locks add complexity you have to be aware of. So use the right synchronization for the right job. If you are not so experienced with multi-threaded programming I would rather stick to instance locking and read up on this topic. (That said: just using synchronize(this) does not automatically make your class fully thread-safe.) This is a not an easy topic but once you get used to it, the answer whether to use synchronize(this) or not comes naturally.

    0 讨论(0)
提交回复
热议问题