5

【Java】JUC并发编程-Lock锁

 1 year ago
source link: https://blog.51cto.com/u_15903651/6988110
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
  • 二、Lock和Synchronized的区别
  • 三、Lock锁的API
  • 四、ReentrantLock(重入锁)、ReentrantReadWriteLock(读写锁)
  • 1、ReentrantLock(重入锁)
  • 2、ReentrantReadWriteLock(读写锁)
  • 五、Lock锁的API代码实现
  • 1、lock()、unLock()
  • 2、lockInterruptibly()
  • 3、tryLock()
  • 4、tryLock(long time, TimeUnit unit)

Lock锁是一个类似于Synchronized的线程同步机制,但是Lock比Synchronized更加灵活。Lock是个接口,有两个实现类:ReentrantLock(重入锁)、ReentrantReadWriteLock(读写锁)

二、Lock和Synchronized的区别

  1. lock是一个接口,synchronized是Java中的关键字
  2. synchronized不需要用户去手动释放锁,发生异常或者线程结束时自动释放锁;lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象
  3. lock可以让等待锁的线程响应中断,使用synchronized时,等待的线程会一直等待下去,不能够响应中断

三、Lock锁的API

【Java】JUC并发编程-Lock锁_java

四、ReentrantLock(重入锁)、ReentrantReadWriteLock(读写锁)

1、ReentrantLock(重入锁)

重入锁也叫做递归锁,指的是同一线程外层函数获得锁之后,内层递归函数仍然有获取该锁的代码,但不受影响。

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();
    }

测试结果:同一个线程,首先在set方法中获取锁,然后调用get方法,get方法中重复获取同一个锁。两个方法都执行成功

【Java】JUC并发编程-Lock锁_System_02

2、ReentrantReadWriteLock(读写锁)

读写锁,可以分别获取读锁或写锁。也就是说将数据的读写操作分开,分成2个锁来分配给线程,从而使得多个线程可以同时进行读操作。读锁使用共享模式;写锁使用独占模式;读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的。当有读锁时,写锁就不能获得;而当有写锁时,除了获得写锁的这个线程可以获得读锁外,其他线程不能获得读锁

  • writeLock():获取写锁。
  • readLock():获取读锁。

执行三个线程进行读写操作,并设置一个屏障,线程依次准备就绪后未获取锁之前都在等待,当第三个线程执行
cyclicBarrier.await()后屏障解除,三个线程同时执行。

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();
        }
    }
}

执行结果:线程3先获取到了读锁,因为读锁时是可以共享的,所以线程1也可以获取到读锁,线程1、线程3读操作完成后,将读锁释放后,线程2才能获取到写锁并开始执行写操作

【Java】JUC并发编程-Lock锁_开发语言_03

五、Lock锁的API代码实现

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();
    }
}

执行结果:

【Java】JUC并发编程-Lock锁_ide_04

2、lockInterruptibly()

实例:当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。

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();
    }
}

执行结果:

【Java】JUC并发编程-Lock锁_读锁_05

3、tryLock()

实例:当两个线程同时通过lock.tryLock()想获取某个锁时,假若此时线程A获取到了锁,而线程B不会等待,直接放弃获取锁

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();
    }
}

执行结果:

【Java】JUC并发编程-Lock锁_开发语言_06

4、tryLock(long time, TimeUnit unit)

如果锁定可用,则此方法立即返回值true。
如果锁不可用,则当前线程将被禁用以进行线程调度,并且在发生以下三种情况之一之前处于休眠状态:
● 当前线程获取锁。
● 其他一些线程中断当前线程。
● 等待时间过去了,返回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();
    }
}

执行结果:

【Java】JUC并发编程-Lock锁_开发语言_07

如果锁不可用,则当前线程将被禁用以进行线程调度,并且在发生以下三种情况之一之前处于休眠状态,当前线程是可以被中断的。

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();
    }
}

执行结果:threadA获得锁,其他线程休眠,然后被中断

【Java】JUC并发编程-Lock锁_开发语言_08

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK