Home  >  Article  >  Java  >  How to use Lock in Java multithreading

How to use Lock in Java multithreading

PHPz
PHPzforward
2023-05-12 14:46:061523browse

After Jdk1.5, under the java.util.concurrent.locks package, there is a set of interfaces and classes that implement thread synchronization. When it comes to thread synchronization, everyone may think of the synchronized keyword,

This It is a built-in keyword in Java, used to handle thread synchronization. However, this keyword has many flaws and is not very convenient and intuitive to use, so Lock appears. Below, we

will compare Explain Lock.

Usually we encounter the following problems when using the synchronized keyword:

(1) Uncontrollability, unable to lock and release locks at will.

(2) The efficiency is relatively low. For example, we are currently reading two files concurrently. The reading and reading have no influence on each other. However, if synchronized is used for the read object to achieve synchronization,

So as long as one thread enters, other threads will have to wait.

(3) There is no way to know whether the thread has acquired the lock.

Lock can solve the above synchronized problems very well, and after jdk1.5, various locks are also provided, such as read-write locks, but there is one thing to note, using synchronized

When it is critical, there is no need to manually release the lock, but when using Lock, you must manually release the lock. Let's learn about Lock locks.

Lock is an upper-layer interface. Its prototype is as follows, providing a total of 6 methods:

public interface Lock {
  // 用来获取锁,如果锁已经被其他线程获取,则一直等待,直到获取到锁
   void lock();
  // 该方法获取锁时,可以响应中断,比如现在有两个线程,一个已经获取到了锁,另一个线程调用这个方法正在等待锁,但是此刻又不想让这个线程一直在这死等,可以通过
    调用线程的Thread.interrupted()方法,来中断线程的等待过程
  void lockInterruptibly() throws InterruptedException;
  // tryLock方法会返回bool值,该方法会尝试着获取锁,如果获取到锁,就返回true,如果没有获取到锁,就返回false,但是该方法会立刻返回,而不会一直等待
   boolean tryLock();
  // 这个方法和上面的tryLock差不多是一样的,只是会尝试指定的时间,如果在指定的时间内拿到了锁,则会返回true,如果在指定的时间内没有拿到锁,则会返回false
   boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
  // 释放锁
   void unlock();
  // 实现线程通信,相当于wait和notify,后面会单独讲解
   Condition newCondition();
}

So how to use these methods? As we mentioned earlier, using Lock requires manual release of the lock. However, if an exception is thrown in the program, the lock cannot be released and may cause deadlock.

So when we use Lock , there is a fixed format, as follows:

Lock l = ...;
      l.lock();
      try {
        // access the resource protected by this lock
      } finally {// 必须使用try,最后在finally里面释放锁
        l.unlock();
      }

Let’s look at a simple example, the code is as follows:

/**
 * 描述:Lock使用
 */
public class LockDemo {
    // new一个锁对象,注意此处必须声明成类对象,保持只有一把锁,ReentrantLock是Lock的唯一实现类
   Lock lock = new ReentrantLock();
   public void readFile(String fileMessage){
      lock.lock();// 上锁
      try{
         System.out.println(Thread.currentThread().getName()+"得到了锁,正在读取文件……");
         for(int i=0; i<fileMessage.length(); i++){
            System.out.print(fileMessage.charAt(i));
         }
         System.out.println();
         System.out.println("文件读取完毕!");
      }finally{
         System.out.println(Thread.currentThread().getName()+"释放了锁!");
         lock.unlock();
      }
   }
   public void demo(final String fileMessage){
      // 创建若干个线程
      ExecutorService service = Executors.newCachedThreadPool();
      // 提交20个任务
      for(int i=0; i<20; i++){
         service.execute(new Runnable() {
            @Override
            public void run() {
               readFile(fileMessage);
               try {
                  Thread.sleep(20);
               } catch (InterruptedException e) {
                  e.printStackTrace();
               }
            }
         });
      }
    // 释放线程池中的线程
      service.shutdown();
   }
}

Comparison between Lock and synchronized

1 , Function

lock and synchronized are both tools used in Java to solve thread safety issues.

2. Source

sychronized is a keyword in Java.

lock is an interface provided in the JUC package. This interface has many implementation classes, including our most commonly used ReentrantLock (reentrant lock).

3. Lock strength

sychronized can control the lock strength in two ways:

Modify the sychronized keyword at the method level.
is decorated on the code block.
Differences in lock objects:

If the lock object is a static object or a class object, then this lock is a global lock.
The lock object is a normal instance object, and the scope of this lock depends on the life cycle of this instance.
The strength of the lock is determined by the two methods lock() and unlock(). The code between the two methods is guaranteed to be thread-safe. The scope of the lock depends on the life cycle of the lock instance.

4. Flexibility

Lock is more flexible than sychronized.

lock can decide independently when to lock and release the lock. Just call the lock() and unlock() methods of lock.

sychronized Because it is a keyword, it cannot implement the non-blocking competition lock method. After one thread acquires the lock, other locks can only wait for that thread to release before they have a chance to acquire the lock.

5. Fair lock and unfair lock

Fair lock: Multiple threads obtain locks in the order in which they apply for locks. The threads will directly enter the queue to queue up, forever. Only the first person in the queue can get the lock.

Advantages: All threads can obtain resources and will not starve to death.
Disadvantages: Low throughput, except for the first thread in the queue, other threads will be blocked, and the CPU overhead of waking up blocked threads is high.
Unfair lock: When multiple threads acquire the lock, they will directly try to acquire it. If they cannot acquire it, they will enter the waiting queue. If they can acquire it, they will acquire the lock directly.

Advantages: It can reduce the overhead of CPU waking up threads, the overall throughput efficiency will be higher, and the CPU does not have to wake up all threads, which will reduce the number of awakened threads.
Disadvantages: The thread in the middle of the queue may not be able to obtain the lock or cannot obtain the lock for a long time, and eventually starve to death.
lock provides two mechanisms: fair lock and unfair lock (default unfair lock).

sychronized is an unfair lock.

6. Whether to release the lock due to exception

The release of the synchronized lock is passive and will only be released when the execution of the sychronized synchronized code block ends or an exception occurs.

When an exception occurs in the lock lock, the occupied lock will not be actively released. It must be released manually with unlock(), so we usually put the synchronization code block into try-catch and write unlock in finally. () method to avoid deadlock.

7. Determine whether the lock can be obtained

synchronized cannot.

lock provides a non-blocking competition lock method trylock(), and the return value is of type Boolean. It indicates that it is used to try to acquire the lock: it returns true if the acquisition is successful; it returns false if the acquisition fails. This method will return immediately no matter what.

8. Scheduling method

synchronized uses the wait, notify, and notifyAll methods of the object object itself, while lock uses Condition for scheduling between threads.

9. Whether it can be interrupted

synchronized can only wait for the lock to be released and cannot respond to interrupts.

You can use interrupt() to interrupt while waiting for the lock.

10. Performance

If the competition is not fierce, the performance is about the same; when the competition is fierce, the performance of lock will be better.

Lock lock can also use readwritelock to separate reading and writing, improving the efficiency of multi-threaded reading operations.

11. Synchronized lock upgrade

The synchronized code block is implemented by a pair of monitorenter/monitorexit instructions. The implementation of Monitor completely relies on the mutex lock inside the operating system. Because it requires switching from user mode to kernel mode, synchronization operation is an undifferentiated heavyweight operation.

So now the JVM provides three different locks: biased locks, lightweight locks, and heavyweight locks.

Biased lock:
When no competition occurs, biased lock is used by default. The thread will use the CAS operation to set the thread ID on the object header to indicate that the object is biased towards the current thread.

Purpose: In many application scenarios, the life cycle of most objects will be locked by at most one thread. Using biased locks can reduce the overhead when there is no competition.

Lightweight lock:
The JVM compares the threadID of the current thread and the threadID in the Java object header to see if it is consistent. If it is inconsistent (for example, thread 2 wants to compete for the lock object), then you need to check the record in the Java object header. Whether thread 1 is alive (the biased lock will not be actively released, so it is still the stored threadID of thread 1). If it is not alive, then the lock object is still a biased lock (the threadID in the object header is thread 2's); if it survives, then revoke Bias lock, upgraded to lightweight lock.

When other threads want to access resources with lightweight locks, spin lock optimization will be used to access the resources.

Purpose: There are not many threads competing for the lock object, and the threads do not hold the lock for a long time. Because blocking the thread requires the CPU to transfer from user mode to kernel mode, which is expensive. If the lock is released shortly after blocking, the gain is not worth the loss. Therefore, it is better not to block the thread at this time and let it spin to wait for the lock to be released.

Heavyweight lock:
If the spin fails, there is a high probability that the self-selection will fail again, so it is directly upgraded to a heavyweight lock to block threads and reduce CPU consumption.

When the lock is upgraded to a heavyweight lock, threads that have not grabbed the lock will be blocked and enter the blocking queue.

The above is the detailed content of How to use Lock in Java multithreading. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete