Difference between ReentranLock and Synchronized

ReentranLock

  • Lock interface is a built-in attribute, not built-in in java language. It can realize synchronous access

  • ReentranLock is the implementation class of Lock interface

  • After java SE 5, a Lock interface (and related implementation classes) is added to implement the Lock function

  • However, it is used to acquire and release locks explicitly, which is synchronized implicitly

  • lock has the operability of acquiring and releasing locks, and can interrupt and timeout the operation of acquiring locks

Mode of use

		ReentrantLock reentrantLock = new ReentrantLock();
        reentrantLock.lock();
        // It is not written in the try block because when an exception occurs, the lock will be released for no reason
        try {
        }
        finally {
            reentrantLock.unlock();
        }

lock classification

  1. Fair lock: it means that the order in which threads acquire locks is allocated according to the order in which threads lock, that is, the order of first come first served FIFO.

  2. Unfair lock: it is a preemptive mechanism to obtain locks, which is to obtain locks randomly. Unlike fair locks, those who come first do not necessarily get the lock first. This method may cause some threads to fail to get the lock all the time, and the result is unfair.

API related to Lock

Method namedescribe
void lock()The current thread acquires the lock
void lockInterruptibly() throws InterruptedExceptionObtain the lock interruptably. This method will respond to the interrupt and interrupt the current thread when obtaining the lock
boolean tryLock()Try to acquire the lock without blocking, and return true after successful acquisition
boolean tryLock(long timeout, TimeUnit unit) throws InterruptedExceptionFor timeout acquisition, the current thread will return in the following three cases: 1 Get lock within current thread timeout 2 The current thread was interrupted within the timeout When the timeout expires, FALSE is returned
void unlock()Release lock
Condition newCondition()Obtain the wait notification component, which is bound to the current lock. The current thread can call the wait() method of the component only after obtaining the lock. After calling, the current thread will release the lock

Compared with synchronized, it has the following characteristics

  • Interruptible
 public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        Thread t1 = new Thread(() -> {

            log.debug("start-up...");
            try {
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.debug("The process of waiting for the lock is interrupted");
                return;
            }
            try {
                log.debug("Got the lock");
            } finally {
                lock.unlock();
            }
        }, "t1");
        lock.lock();
        log.debug("Got the lock");
        t1.start();
        try {
            TimeUnit.SECONDS.sleep(1);
            t1.interrupt();
            log.debug("Execution interrupt");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

Note: if it is not an interruptible mode, the interrupt operation will not take effect

  • You can set the timeout (try to acquire the lock non blocking)
  1. Timeout is not set
 public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        Thread t1 = new Thread(() -> {
            log.debug("start-up...");
            if (!lock.tryLock()) {
                log.debug("Get immediate failure, return");
                return;
            }
            try {
                log.debug("Got the lock");
            } finally {
                lock.unlock();
            }
        }, "t1");
        lock.lock();
        
        log.debug("Got the lock");
        t1.start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

  1. Set timeout
 public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        Thread t1 = new Thread(() -> {
            log.debug("start-up...");
            try {
                if (!lock.tryLock(2,TimeUnit.SECONDS)) {
                    log.debug("2s Failed to acquire lock after. Return");
                    return;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                log.debug("Got the lock");
            } finally {
                lock.unlock();
            }
        }, "t1");
        lock.lock();

        log.debug("Got the lock");
        t1.start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

Case: solving the problem of dining for philosophers
  • Chopsticks
class Chopstick extends ReentrantLock{
    private String name;

    public Chopstick( String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Chopstick{" +
                "name='" + name + '\'' +
                '}';
    }
}
  • Philosophers
@Slf4j(topic = "c.Philosopher")
class Philosopher extends Thread{

    private Chopstick left;
    private Chopstick right;



    public Philosopher(String name , Chopstick left,Chopstick right){
        super(name);

        this.left = left;
        this.right = right;
    }

    @Override
    public void run() {
        while (true){
            if(left.tryLock()){
                try {
                    if(right.tryLock()){
                        try {
                            eat();
                        }
                        finally {
                            right.unlock();
                        }
                    }
                }
                finally {
                    left.unlock();

                }
            }
        }
    }

    private void eat() {
        log.debug("eating ...");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

  • Test code
public static void main(String[] args) {

        Chopstick c1 = new Chopstick("nail");
        Chopstick c2 = new Chopstick("B");
        Chopstick c3 = new Chopstick("C");
        Chopstick c4 = new Chopstick("Ding");
        Chopstick c5 = new Chopstick("E");

        Philosopher p1 = new Philosopher("nail", c1, c2);
        Philosopher p2 = new Philosopher("B", c2, c3);
        Philosopher p3 = new Philosopher("C", c3, c4);
        Philosopher p4 = new Philosopher("Ding", c4, c5);
        Philosopher p5 = new Philosopher("E", c1, c5);
        p1.start();
        p2.start();
        p3.start();
        p4.start();
        p5.start();
    }
  • results of enforcement
"C:\Program Files\Java\jdk1.8.0_131\bin\java.exe" "-javaagent:D:\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=7696:D:\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_131\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar;D:\Java development\CurrnetCode\target\classes;C:\Users\Tang Hao\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.10.0\jackson-databind-2.10.0.jar;C:\Users\Tang Hao\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.10.0\jackson-annotations-2.10.0.jar;C:\Users\Tang Hao\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.10.0\jackson-core-2.10.0.jar;C:\Users\Tang Hao\.m2\repository\org\projectlombok\lombok\1.18.10\lombok-1.18.10.jar;C:\Users\Tang Hao\.m2\repository\org\springframework\spring-context\5.2.0.RELEASE\spring-context-5.2.0.RELEASE.jar;C:\Users\Tang Hao\.m2\repository\org\springframework\spring-aop\5.2.0.RELEASE\spring-aop-5.2.0.RELEASE.jar;C:\Users\Tang Hao\.m2\repository\org\springframework\spring-beans\5.2.0.RELEASE\spring-beans-5.2.0.RELEASE.jar;C:\Users\Tang Hao\.m2\repository\org\springframework\spring-core\5.2.0.RELEASE\spring-core-5.2.0.RELEASE.jar;C:\Users\Tang Hao\.m2\repository\org\springframework\spring-jcl\5.2.0.RELEASE\spring-jcl-5.2.0.RELEASE.jar;C:\Users\Tang Hao\.m2\repository\org\springframework\spring-expression\5.2.0.RELEASE\spring-expression-5.2.0.RELEASE.jar;C:\Users\Tang Hao\.m2\repository\org\springframework\spring-webmvc\5.2.0.RELEASE\spring-webmvc-5.2.0.RELEASE.jar;C:\Users\Tang Hao\.m2\repository\org\springframework\spring-web\5.2.0.RELEASE\spring-web-5.2.0.RELEASE.jar;C:\Users\Tang Hao\.m2\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;C:\Users\Tang Hao\.m2\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;C:\Users\Tang Hao\.m2\repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;C:\Users\Tang Hao\.m2\repository\mysql\mysql-connector-java\5.1.48\mysql-connector-java-5.1.48.jar;C:\Users\Tang Hao\.m2\repository\org\openjdk\jol\jol-core\0.10\jol-core-0.10.jar" com.lock.EatQ
17:51:18.392 c.Philosopher [nail] - eating ...
17:51:18.392 c.Philosopher [Ding] - eating ...
17:51:19.407 c.Philosopher [nail] - eating ...
17:51:19.407 c.Philosopher [C] - eating ...
17:51:20.422 c.Philosopher [B] - eating ...
17:51:20.422 c.Philosopher [E] - eating ...
17:51:21.423 c.Philosopher [C] - eating ...
17:51:21.423 c.Philosopher [E] - eating ...
17:51:22.426 c.Philosopher [Ding] - eating ...
17:51:22.426 c.Philosopher [nail] - eating ...
17:51:23.427 c.Philosopher [nail] - eating ...
17:51:23.427 c.Philosopher [Ding] - eating ...
17:51:24.430 c.Philosopher [nail] - eating ...
17:51:24.430 c.Philosopher [C] - eating ...
17:51:25.433 c.Philosopher [Ding] - eating ...
17:51:25.433 c.Philosopher [nail] - eating ...
17:51:26.434 c.Philosopher [B] - eating ...
17:51:26.434 c.Philosopher [E] - eating ...
17:51:27.437 c.Philosopher [B] - eating ...
17:51:27.437 c.Philosopher [Ding] - eating ...
17:51:28.440 c.Philosopher [Ding] - eating ...
17:51:28.440 c.Philosopher [B] - eating ...
17:51:29.443 c.Philosopher [Ding] - eating ...
17:51:29.443 c.Philosopher [B] - eating ...
17:51:30.446 c.Philosopher [Ding] - eating ...
17:51:30.446 c.Philosopher [B] - eating ...
17:51:31.459 c.Philosopher [B] - eating ...
17:51:31.459 c.Philosopher [E] - eating ...
17:51:32.460 c.Philosopher [Ding] - eating ...
17:51:32.460 c.Philosopher [B] - eating ...
17:51:33.462 c.Philosopher [Ding] - eating ...
17:51:33.462 c.Philosopher [B] - eating ...
17:51:34.465 c.Philosopher [Ding] - eating ...
17:51:34.465 c.Philosopher [B] - eating ...
17:51:35.468 c.Philosopher [C] - eating ...
17:51:35.468 c.Philosopher [E] - eating ...
17:51:36.470 c.Philosopher [Ding] - eating ...
17:51:36.470 c.Philosopher [B] - eating ...
17:51:37.470 c.Philosopher [E] - eating ...
17:51:37.470 c.Philosopher [B] - eating ...
17:51:38.485 c.Philosopher [Ding] - eating ...
17:51:38.485 c.Philosopher [nail] - eating ...
17:51:39.488 c.Philosopher [Ding] - eating ...
17:51:39.488 c.Philosopher [B] - eating ...
17:51:40.489 c.Philosopher [B] - eating ...
17:51:40.489 c.Philosopher [Ding] - eating ...

Process finished with exit code -1

  • Can be set to fair lock
  • The default setting is false
 public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
  }
  • Unfair model
public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock(false);

        for (int i = 0; i < 500; i++) {

            new Thread( () -> {
                lock.lock();
               try {
                   String name = Thread.currentThread().getName();

                   log.debug(name+ "Perform tasks");
               }
               finally {
                   lock.unlock();
               }

            },"thread "+i).start();
        }


        Thread.sleep(1000);
        new Thread(()-> {
            log.debug(Thread.currentThread().getName() + "begin ... ");
            lock.lock();
            try {
                log.debug(Thread.currentThread().getName() + "running ... ");
            }
            finally {
                lock.unlock();
            }

        },"Insert thread").start();

    }


Note: the above situation is accidental and cannot be repeated

  • Fair lock on (ReentrantLock lock = new ReentrantLock(true);)

  • Reentrant: if the same thread obtains the lock for the first time, it has the right to obtain the lock again because it is the owner of the lock. If it is a non reentrant lock, it will be blocked by the lock the second time it obtains the lock (the same as synchronized)
      static ReentrantLock lock = new ReentrantLock();
        public static void main(String[] args) {
            method1();
        }
        public static void method1() {
            lock.lock();
            try {
                log.debug("execute method1");
                method2();
            } finally {
                lock.unlock();
            }
        }
        public static void method2() {
            lock.lock();
            try {
                log.debug("execute method2");
                method3();
            } finally {
                lock.unlock();
            }
        }
        public static void method3() {
            lock.lock();
            try {
                log.debug("execute method3");
            } finally {
                lock.unlock();
            }
        }

  • Multiple condition variables are supported, that is, threads that do not meet the conditions can be put into different sets to wait
@Slf4j(topic = "c.T6")
public class T6 {

    static ReentrantLock lock = new ReentrantLock();
    static Condition c1 = lock.newCondition();
    static Condition c2 = lock.newCondition();
    static volatile boolean isOfferC1 = false;
    static volatile boolean isOfferC2 = false;

    public static void offerc1() {
        lock.lock();
        try {
            log.debug("provide c1 Working conditions");
            isOfferC1 = true;
            c1.signal();
        } finally {
            lock.unlock();
        }
    }

    public static void offerc2() {
        lock.lock();
        try {
            log.debug("provide c2 Working conditions");
            isOfferC2 = true;
            c2.signal();
        } finally {
            lock.unlock();
        }
    }


    public static void main(String[] args) throws InterruptedException {


        new Thread(() -> {
            try {
                lock.lock();
                log.debug("Satisfied"+Thread.currentThread().getName()+"Working conditions");
                while (!isOfferC1) {
                    try {
                        c1.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug(Thread.currentThread().getName()+"start-up...");
            } finally {
                lock.unlock();
            }
        }, "Thread one").start();

        new Thread(() -> {
            try {
                lock.lock();
                log.debug("Satisfied"+Thread.currentThread().getName()+"Working conditions");
                while (!isOfferC2) {
                    try {
                        c2.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                log.debug(Thread.currentThread().getName()+"start-up...");
            } finally {
                lock.unlock();
            }
        }, "Thread two").start();


        TimeUnit.SECONDS.sleep(1);
        offerc1();
        TimeUnit.SECONDS.sleep(1);
        offerc2();



    }
}

Synchronized

  • Definition (Reference: link)

synchronized built-in lock is an object lock. The granularity is an object rather than a reference variable. Any non empty object can be regarded as a "lock", which can be used to modify a method or code block to ensure that at most one thread executes the modified method or code block at the same time. synchronized is decorated in different places, but its locks are on the same object. Mutexes are generated where it holds the same object lock, rather than only the currently referenced code block or method body.

Compared with the display lock, synchronized does not require locking and unlocking operations. The synchronized syntax is simple and convenient, and it is also applicable in the case of high concurrency.

  • Synchronized is a keyword in Java, and synchronized is a built-in language implementation;
  • synchronized will automatically release the lock held by the thread when an exception occurs, so it will not lead to deadlock;
  • Lock allows the thread waiting for the lock to respond to the interrupt, but synchronized does not. When synchronized is used, the waiting thread will wait all the time and cannot respond to the interrupt;
  • synchronized failed to see if the lock was acquired

summary

In terms of performance, if the competition for resources is not fierce, the performance of the two is similar. When the competition for resources is very fierce (that is, a large number of threads compete at the same time), the performance of Lock is much better than that of synchronized. Therefore, the specific use should be selected according to the appropriate situation.

Keywords: Java Back-end Concurrent Programming JUC

Added by AtomicRax on Fri, 18 Feb 2022 18:08:16 +0200