目录
Lock与synchronized的对比
首页 Java java教程 Java多线程中Lock怎么使用

Java多线程中Lock怎么使用

May 12, 2023 pm 02:46 PM
java lock

Jdk1.5 以后,在 java.util.concurrent.locks 包下,有一组实现线程同步的接口和类,说到线程的同步,可能大家都会想到 synchronized 关键字,

这是 java 内置的关键字,用来处理线程同步的,但这个关键字有很多的缺陷,使用起来也不是很方便和直观,所以就出现了 Lock,下面,我们

就来对比着讲解 Lock。

通常我们在使用 synchronized 关键字的时候会遇到下面这些问题:

(1)不可控性,无法做到随心的加锁和释放锁。

(2)效率比较低下,比如我们现在并发的读两个文件,读与读之间是互不影响的,但如果给这个读的对象使用 synchronized 来实现同步的话,

那么只要有一个线程进入了,那么其他的线程都要等待。

(3)无法知道线程是否获取到了锁。

而上面 synchronized 的这些问题,Lock 都可以很好的解决,并且 jdk1.5 以后,还提供了各种锁,例如读写锁,但有一点需要注意,使用 synchronized

关键时,无须手动释放锁,但使用 Lock 必须手动释放锁。下面我们就来学习一下 Lock 锁。

Lock 是一个上层的接口,其原型如下,总共提供了 6 个方法:

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();
}
登录后复制

那么这几个方法该如何使用了?前面我们说到,使用 Lock 是需要手动释放锁的,但是如果程序中抛出了异常,那么就无法做到释放锁,有可能引起死锁,

所以我们在使用 Lock 的时候,有一种固定的格式,如下:

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

下面我们来看一个简单的例子,代码如下:

/**
 * 描述: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();
   }
}
登录后复制

Lock与synchronized的对比

1、作用

lock 和 synchronized 都是 Java 中去用来解决线程安全问题的一个工具。

2、来源

sychronized 是 Java 中的一个关键字。

lock 是 JUC 包里面提供的一个接口,这个接口有很多实现类,其中就包括我们最常用的 ReentrantLock(可重入锁)。

3、锁的力度

sychronized 可以通过两种方式去控制锁的力度:

把 sychronized 关键字修饰在方法层面。
修饰在代码块上。
锁对象的不同:

锁对象为静态对象或者是class对象,那这个锁属于全局锁。
锁对象为普通实例对象,那这个锁的范围取决于这个实例的生命周期。
lock锁的力度是通过 lock()与unlock()两个方法决定的。在两个方法之间的代码能保证其线程安全。lock的作用域取决于lock实例的生命周期。

4、灵活性

lock锁比sychronized的灵活性更高。

lock可以自主的去决定什么时候加锁与释放锁。只需要调用lock 的lock()和unlock()这两个方法就可以。

sychronized 由于是一个关键字,所以他无法实现非阻塞竞争锁的方法,一个线程获取锁之后,其他锁只能等待那个线程释放之后才能有获取锁的机会。

5、公平锁与非公平锁

公平锁:多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能得到锁。

优点:所有的线程都能得到资源,不会饿死。
缺点:吞吐量低,队列里面除了第一个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的开销大。
非公平锁:多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。

优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必取唤醒所有线程,会减少唤起线程的数量。
缺点:可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁,最终饿死。
lock提供了公平锁和非公平锁两种机制(默认非公平锁)。

sychronized是非公平锁。

6、异常是否释放锁

synchronized锁的释放是被动的,当sychronized同步代码块执行结束或者出现异常的时候才会被释放。

lock锁发生异常的时候,不会主动释放占有的锁,必须手动unlock()来释放,所以我们一般都是将同步代码块放进try-catch里面,finally中写入unlock()方法,避免死锁发生。

7、判断是否能获取锁

synchronized不能。

lock提供了非阻塞竞争锁的方法trylock(),返回值是Boolean类型。它表示的是用来尝试获取锁:成功获取则返回true;获取失败则返回false,这个方法无论如何都会立即返回。

8、调度方式

synchronized使用的是object对象本身的wait、notify、notifyAll方法,而lock使用的是Condition进行线程之间的调度。

9、是否能中断

synchronized只能等待锁的释放,不能响应中断。

lock等待锁过程中可以用interrupt()来中断。

10、性能

如果竞争不激烈,性能差不多;竞争激烈时,lock的性能会更好。

lock锁还能使用readwritelock实现读写分离,提高多线程的读操作效率。

11、sychronized锁升级

synchronized 代码块是由一对 monitorenter/monitorexit 指令实现的。Monitor的实现完全是依靠操作系统内部的互斥锁,因为需要进行用户态到内核态的切换,所以同步操作是一个无差别的重量级操作。

所以现在JVM提供了三种不同的锁:偏向锁、轻量级锁、重量级锁。

偏向锁:
当没有竞争出现时,默认使用偏向锁。线程会利用 CAS 操作在对象头上设置线程 ID ,以表示对象偏向当前线程。

目的:在很多应用场景中,大部分对象生命周期最多会被一个线程锁定,使用偏向锁可以降低无竞争时的开销。

轻量级锁:
JVM比较当前线程的 threadID 和 Java 对象头中的threadID是否一致,如果不一致(比如线程2要竞争锁对象),那么需要查看 Java 对象头中记录的线程1是否存活(偏向锁不会主动释放因此还是存储的线程1的 threadID),如果没有存活,那么锁对象还是为偏向锁(对象头中的threadID为线程2的);如果存活,那么撤销偏向锁,升级为轻量级锁。

当有其他线程想访问加了轻量级锁的资源时,会使用自旋锁优化,来进行资源访问。

目的:竞争锁对象的线程不多,而且线程持有锁的时间也不长的情景。因为阻塞线程需要CPU从用户态转到内核态,开销大,如果刚刚阻塞不久这个锁就被释放了,就得不偿失了,因此这个时候就干脆不阻塞这个线程,让它自旋这等待锁释放。

重量级锁:
自旋失败,很大概率 再一次自选也是失败,因此直接升级成重量级锁,进行线程阻塞,减少cpu消耗。

当锁升级为重量级锁后,未抢到锁的线程都会被阻塞,进入阻塞队列。

以上是Java多线程中Lock怎么使用的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Java 中的完美数 Java 中的完美数 Aug 30, 2024 pm 04:28 PM

Java 完美数指南。这里我们讨论定义,如何在 Java 中检查完美数?,示例和代码实现。

Java 中的随机数生成器 Java 中的随机数生成器 Aug 30, 2024 pm 04:27 PM

Java 随机数生成器指南。在这里,我们通过示例讨论 Java 中的函数,并通过示例讨论两个不同的生成器。

Java中的Weka Java中的Weka Aug 30, 2024 pm 04:28 PM

Java 版 Weka 指南。这里我们通过示例讨论简介、如何使用weka java、平台类型和优点。

Java 中的史密斯数 Java 中的史密斯数 Aug 30, 2024 pm 04:28 PM

Java 史密斯数指南。这里我们讨论定义,如何在Java中检查史密斯号?带有代码实现的示例。

Java Spring 面试题 Java Spring 面试题 Aug 30, 2024 pm 04:29 PM

在本文中,我们保留了最常被问到的 Java Spring 面试问题及其详细答案。这样你就可以顺利通过面试。

突破或从Java 8流返回? 突破或从Java 8流返回? Feb 07, 2025 pm 12:09 PM

Java 8引入了Stream API,提供了一种强大且表达力丰富的处理数据集合的方式。然而,使用Stream时,一个常见问题是:如何从forEach操作中中断或返回? 传统循环允许提前中断或返回,但Stream的forEach方法并不直接支持这种方式。本文将解释原因,并探讨在Stream处理系统中实现提前终止的替代方法。 延伸阅读: Java Stream API改进 理解Stream forEach forEach方法是一个终端操作,它对Stream中的每个元素执行一个操作。它的设计意图是处

Java 中的时间戳至今 Java 中的时间戳至今 Aug 30, 2024 pm 04:28 PM

Java 中的时间戳到日期指南。这里我们还结合示例讨论了介绍以及如何在java中将时间戳转换为日期。

创造未来:面向零基础的 Java 编程 创造未来:面向零基础的 Java 编程 Oct 13, 2024 pm 01:32 PM

Java是热门编程语言,适合初学者和经验丰富的开发者学习。本教程从基础概念出发,逐步深入讲解高级主题。安装Java开发工具包后,可通过创建简单的“Hello,World!”程序实践编程。理解代码后,使用命令提示符编译并运行程序,控制台上将输出“Hello,World!”。学习Java开启了编程之旅,随着掌握程度加深,可创建更复杂的应用程序。

See all articles