[Java] JUC concurrent programming-Lock lock

I. Overview

Lock is a thread synchronization mechanism similar to Synchronized, but Lock is more flexible than Synchronized. Lock is an interface with two implementation classes: ReentrantLock (reentrant lock), ReentrantReadWriteLock (read-write lock)

Two, the difference between Lock and Synchronized

  1. lock is an interface, synchronized is a keyword in Java
  2. Synchronized does not require the user to manually release the lock, and automatically releases the lock when an exception occurs or the thread ends; lock requires the user to manually release the lock. If the lock is not actively released, it may lead to deadlock.
  3. lock can make the thread waiting for the lock respond to the interrupt. When using synchronized, the waiting thread will wait forever and cannot respond to the interrupt
    insert image description here

3. Lock API

insert image description here

Four, ReentrantLock (reentrant lock), ReentrantReadWriteLock (read-write lock)

1. ReentrantLock (reentrant lock)

Reentrant locks are also called recursive locks, which means that after the outer function of the same thread acquires the lock, the inner recursive function still has the code to acquire the lock, but it is not affected.

public class ReentrantDemo implements Runnable {
    
    
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
    
    
        set();
    }
    public void set() {
    
    
        try {
    
    
            lock.lock();
            System.out.println("set 方法");
            get();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();// 必须在finally中释放
        }
    }

    public void get() {
    
    

        try {
    
    
            lock.lock();
            System.out.println("get 方法");
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }
    }
    public static void main(String[] args) {
    
    
        ReentrantDemo reentrantDemo = new ReentrantDemo();
        new Thread(reentrantDemo).start();
    }

Test results: The same thread first acquires the lock in the set method, then calls the get method, and repeatedly acquires the same lock in the get method. Both methods execute successfully
insert image description here

2. ReentrantReadWriteLock (read-write lock)

Read-write locks can acquire read locks or write locks respectively. That is to say, the read and write operations of data are separated and divided into two locks to be assigned to threads, so that multiple threads can perform read operations at the same time. Read locks use shared mode; write locks use exclusive mode; read locks can be held by multiple threads at the same time when there is no write lock, and write locks are exclusive. When there is a read lock, the write lock cannot be obtained; and when there is a write lock, except for the thread that obtains the write lock can obtain the read lock, other threads cannot obtain the read lock

  • writeLock(): Get a write lock.
  • readLock(): Acquires a read lock.

Execute three threads to read and write operations, and set a barrier. After the threads are ready in turn, they wait before acquiring the lock. When the third thread executes
cyclicBarrier.await(), the barrier is released, and the three threads execute simultaneously.

public class WriteAndReadLockTest {
    
    
    private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10,
            60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
    private static CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
    private static int i = 100;
    public static void main(String[] args) {
    
    
        threadPoolExecutor.execute(()->{
    
    
            read(Thread.currentThread());
        });
        threadPoolExecutor.execute(()->{
    
    
            write(Thread.currentThread());
        });
        threadPoolExecutor.execute(()->{
    
    
            read(Thread.currentThread());
        });
        threadPoolExecutor.shutdown();
    }

    private static void read(Thread thread) {
    
    
        try {
    
    
            cyclicBarrier.await();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
    
    
            e.printStackTrace();
        }
        reentrantReadWriteLock.readLock().lock();
        try {
    
    
            System.out.println("读线程 "+ thread.getName() + " 开始执行, i=" + i);
            Thread.sleep(1000);
            System.out.println(thread.getName() +" is over!");
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            reentrantReadWriteLock.readLock().unlock();

        }
    }
    private static void write(Thread thread) {
    
    
        try {
    
    
            cyclicBarrier.await();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
    
    
            e.printStackTrace();
        }
        reentrantReadWriteLock.writeLock().lock();
        try {
    
    
            i++;
            System.out.println("写线程 "+ thread.getName() + " is doing, i=" + i);
            System.out.println(thread.getName() +" is over!");
        } finally {
    
    
            reentrantReadWriteLock.writeLock().unlock();
        }
    }
}

Execution result: Thread 3 acquires the read lock first. Because the read lock can be shared, thread 1 can also acquire the read lock. After the read operations of thread 1 and thread 3 are completed, the read lock can be released before thread 2 can acquire it. to the write lock and begin the write operation
insert image description here

5. API code implementation of Lock lock

1、lock()、unLock()

public class LockAndUnlock  implements Runnable{
    
    
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
    
    
        //上锁
        lock.lock();
        try {
    
    
            System.out.println("ThreadA获取锁");
            Thread.sleep(100);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock(); // 解锁
            System.out.println("ThreadA释放锁");
        }
    }

    public static void main(String[] args) {
    
    
        LockAndUnlock lockAndUnlock = new LockAndUnlock();
        Thread threadA = new Thread(lockAndUnlock,"threadA");
        threadA.start();
    }
}

Results of the:
insert image description here

2、lockInterruptibly()

Example: When two threads want to acquire a lock through lock.lockInterruptibly() at the same time, if thread A acquires the lock at this time, and thread B is only waiting, then calling threadB.interrupt() method on thread B can interrupt the thread B's waiting process.

public class LockInterruptiblyDemo {
    
    
    private static Lock lock = new ReentrantLock();

    public static void main(String[] args) {
    
    
        Thread threadA = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                try {
    
    
                    lock.lockInterruptibly();
                    System.out.println("ThreadA获得锁 ");
                    // 模拟线程A持有锁的一段时间
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
    
    
                    System.out.println("ThreadA在等待锁时被中断");
                } finally {
    
    
                    if (Thread.holdsLock(lock)) {
    
    
                        lock.unlock();
                    }
                }
            }
        });

        Thread threadB = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                try {
    
    
                    lock.lockInterruptibly();
                    System.out.println("ThreadB获得锁");
                } catch (InterruptedException e) {
    
    
                    System.out.println("ThreadB在等待锁时被中断");
                } finally {
    
    
                    if (Thread.holdsLock(lock)) {
    
    
                        lock.unlock();
                    }
                }
            }
        });

        threadA.start();
        // 让线程A先获取到锁
        try {
    
    
            Thread.sleep(100);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        threadB.start();

        // 在线程B等待锁的过程中,中断线程B
        threadB.interrupt();
    }
}

Results of the:
insert image description here

3、tryLock()

Example: When two threads want to acquire a lock through lock.tryLock() at the same time, if thread A acquires the lock at this time, thread B will not wait and give up acquiring the lock directly

public class TryLock{
    
    
    private static Lock lock = new ReentrantLock();

    public static void main(String[] args) {
    
    
        Thread threadA = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                if (lock.tryLock()) {
    
    
                    try {
    
    
                        System.out.println("ThreadA获得了锁");
                        // 模拟线程A持有锁的一段时间
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    } finally {
    
    
                        lock.unlock();
                    }
                } else {
    
    
                    System.out.println("ThreadA获取锁失败");
                }
            }
        });

        Thread threadB = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                if (lock.tryLock()) {
    
    
                    try {
    
    
                        System.out.println("ThreadB获得了锁");
                    } finally {
    
    
                        lock.unlock();
                    }
                } else {
    
    
                    System.out.println("ThreadB获取锁失败");
                }
            }
        });

        threadA.start();
        // 让线程A先获取到锁
        try {
    
    
            Thread.sleep(100);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        threadB.start();
    }
}

Results of the:
insert image description here

4、tryLock(long time, TimeUnit unit)

This method immediately returns the value true if the lock is available.
If the lock is not available, the current thread is disabled for thread scheduling and sleeps until one of the following three things happens:
● The current thread acquires the lock.
● Some other thread interrupts the current thread.
● wait time elapsed, return false

public class TryLockParam implements Runnable{
    
    
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
    
    
        try {
    
    
            //假设 threadA 线程先持有锁, 完成任务需要 4 秒钟,
            //这个时候 threadB 线程尝试获得锁, threadB 线程在 3 秒内还没有获得锁的话, 那么它就不再等了,直接放弃
            if (lock.tryLock(3, TimeUnit.SECONDS)) {
    
    
                System.out.println(Thread.currentThread().getName() + "获得锁,执行耗时任务");
                Thread.sleep(1000 * 4);
            }else {
    
    
                System.out.println(Thread.currentThread().getName() + "放弃获得锁");
            }
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if (Thread.holdsLock(lock)) {
    
    
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
    
    
        TryLockParam tryLockParam=new TryLockParam();

        Thread threadA=new Thread(tryLockParam,"threadA");
        Thread threadB=new Thread(tryLockParam,"threadB");

        threadA.start();
        threadB.start();
    }
}

Results of the:
insert image description here

If the lock is not available, the current thread will be disabled for thread scheduling and will be dormant until one of the following three situations occurs. The current thread can be interrupted.

public class TryLockParam implements Runnable{
    
    
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
    
    
        try {
    
    
            //假设 threadA 线程先持有锁, 完成任务需要 4 秒钟,
            //这个时候 threadB 线程尝试获得锁, threadB 线程在 3 秒内还没有获得锁的话, 那么它就不再等了,直接放弃
            if (lock.tryLock(3, TimeUnit.SECONDS)) {
    
    
                System.out.println(Thread.currentThread().getName() + "获得锁");
                for (int i = 0; i <= 500000000; i++) {
    
    
                    if (i == 500000000) {
    
    
                        System.out.println("运算结束");
                    }
                }
                lock.unlock();
            }else {
    
    
                System.out.println(Thread.currentThread().getName() + "放弃获得锁");
            }
        } catch (InterruptedException e) {
    
    
            System.out.println(Thread.currentThread().getName() +"中断");
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
    
    
        TryLockParam tryLockParam=new TryLockParam();

        Thread threadA=new Thread(tryLockParam,"threadA");
        Thread threadB=new Thread(tryLockParam,"threadB");

        threadA.start();
        threadB.start();


        try {
    
    
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        threadA.interrupt();
        threadB.interrupt();
    }
}

Execution result: threadA acquires the lock, other threads sleep, and then are interrupted
insert image description here

Guess you like

Origin blog.csdn.net/weixin_45490023/article/details/132076149