Java 锁机制(synchronized 与 Lock)

Java 锁机制(synchronized 与 Lock)

原文链接:https://blog.csdn.net/qq_33375499/article/details/105161343

在java中,解决同步问题,很多时候都会使用到synchronized和Lock,这两者都是在多线程并发时候常使用的锁机制。

synchronized是java中的一个关键字,也就是说是java内置的一个特性。当一个线程访问一个被synchronized修饰的代码块,会自动获取对应的一个锁,并在执行该代码块时,其他线程想访问这个代码块,会一直处于等待状态,自有等该线程释放锁后,其他线程进行资源竞争,竞争获取到锁的线程才能访问该代码块。

线程释放synchronized修饰的代码块锁的方式有两种:

  1. 该线程执行完对应代码块,自动释放锁。
  2. 在执行该代码块是发生了异常,JVM会自动释放锁。

采用synchronized关键字来实现同步,会导致如果存在多个线程想执行该代码块,而当前获取到锁的线程又没有释放锁,可想而知,其他线程只有一只等待,这将严重印象执行效率。Lock锁机制的出现就是为了解决该现象。Lock是一个java接口,通过这个接口可以实现同步,使用Lock时,用户必须手动进行锁的释放,否则容易出现死锁。

类别synchronizedLock
存在层次Java的关键字,在JVM层面上是一个类
锁的释放1.以获取锁的线程执行完同步代码,释放锁;2.线程执行发生异常,JVM会自动让线程释放锁;在finally中必须释放锁,不然容易造成线程死锁
锁的获取假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待分情况而定,Lock有多个锁获取的方式,具体下面会说道,大致就是可以尝试获得锁,线程可以不用一直等待
锁状态无法判断可以判断
锁类型可重入 不可中断 非公平可重入 可判断 可公平(两者皆可)
性能少量同步大量同步

ReentranLock是Lock的唯一实现类。下面简单介绍一下ReentranLock与synchronized的区别:

  • Synchronized是一个同步锁。当一个线程A访问synchronized修饰的代码块时,线程A就会获取该代码块的锁,如果这时存在其他线程范围该代码块时,将会阻塞,但是不影响这些线程访问其他非同步代码块。
  • ReentranLock是可重入锁。由构造方法可知,该锁支持两种锁模式,公平锁和非公平锁。默认是非公平的。
    请添加图片描述

**公平锁:**当线程A获取访问该对象,获取到锁后,此时内部存在一个计数器num+1,其他线程想访问该对象,就会进行排队等待(等待队列最前一个线程处于待唤醒状态),直到线程A释放锁(num = 0),此时会唤醒处于待唤醒状态的线程进行获取锁的操作,一直循环。如果线程A再次尝试获取该对象锁是,会检查该对象锁释放已经被占用,如果被占用,会做一次是否为当前线程占用锁的判断,如果是内部计数器num+1,并且不需要进入等待队列,而是直接回去当前锁。

**非公平锁:**当线程A在释放锁后,等待对象的线程会进行资源竞争,竞争成功的线程将获取该锁,其他线程继续睡眠。

公平锁是严格的以FIFO的方式进行锁的竞争,但是非公平锁是无序的锁竞争,刚释放锁的线程很大程度上能比较快的获取到锁,队列中的线程只能等待,所以非公平锁可能会有“饥饿”的问题。但是重复的锁获取能减小线程之间的切换,而公平锁则是严格的线程切换,这样对操作系统的影响是比较大的,所以非公平锁的吞吐量是大于公平锁的,这也是为什么JDK将非公平锁作为默认的实现。

下面是接口Lock的方法:

请添加图片描述

附上对接口Lock方法的测试,有什么问题欢迎各位大佬留言评论。

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
    // ReentrantLock为Lock的唯一实现类
    private Lock lock = new ReentrantLock();

    /**
     * 测试使用lock 的 lock()方法 :如果锁已经被其他线程获取,则等待
     * @param thread
     */
    public void testLock(Thread thread){
        try {
            // 1.获取锁
            lock.lock();
            System.out.println("线程 " + thread.getName() + " 获取了锁!");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println("线程 " + thread.getName() + " 释放了锁!");
            // 必须在 finally 中释放锁,防止死锁
            lock.unlock();
        }
    }

    /**
     * 测试使用lock 的 lockInterruptibly()方法 :通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。
     * @param thread
     */
    public void testLockInterruptibly(Thread thread){
        try {
            // 1.获取锁
            lock.lockInterruptibly();
            System.out.println("线程 " + thread.getName() + " 获取了锁!");
            Thread.sleep(3000);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println("线程 " + thread.getName() + " 释放了锁!");
            // 必须在 finally 中释放锁,防止死锁
            lock.unlock();
        }
    }

    /**
     * 测试使用lock 的 tryLock()方法 :如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false
     * @param thread
     */
    public void testTryLock(Thread thread){
        if(lock.tryLock()){// 如果获取到了锁
            try {
                System.out.println("线程 " + thread.getName() + " 获取了锁!");
                Thread.sleep(3000);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                System.out.println("线程 " + thread.getName() + " 释放了锁!");
                // 必须在 finally 中释放锁,防止死锁
                lock.unlock();
            }
        }else {
            // 没有获取到锁
            System.out.println("线程 " + thread.getName() + " 没有获取到锁!");
        }
    }

    /**
     * 测试使用lock 的 tryLock(long time, TimeUnit unit)方法 :和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,
     * 在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。
     * @param thread
     */
    public void testTryLock_time_unit(Thread thread){
        try {
            if(lock.tryLock(1000, TimeUnit.MILLISECONDS)){// 如果获取到了锁
                try {
                    System.out.println("线程 " + thread.getName() + " 获取了锁!");
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    System.out.println("线程 " + thread.getName() + " 释放了锁!");
                    // 必须在 finally 中释放锁,防止死锁
                    lock.unlock();
                }
            }else {
                // 没有获取到锁
                System.out.println("线程 " + thread.getName() + " 没有获取到锁!");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args){
        TestLock testLock = new TestLock();
        Thread a = new Thread("A") {
            @Override
            public void run() {
                /*// 测试 lock()
                testLock.testLock(Thread.currentThread());*/
                /*// 测试 lockInterruptibly()
                testLock.testLockInterruptibly(Thread.currentThread());*/
                /*// 测试 tryLock()
                testLock.testTryLock(Thread.currentThread());*/
                /*// 测试 tryLock(long time, TimeUnit unit)
                testLock.testTryLock_time_unit(Thread.currentThread());*/
                testLock.testTryLock_time_unit(Thread.currentThread());
            }
        };
        Thread b = new Thread("B") {
            @Override
            public void run() {
                testLock.testTryLock(Thread.currentThread());
            }
        };
        a.start();
        b.start();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值