0

0

java 多线程-锁详解及示例代码

高洛峰

高洛峰

发布时间:2017-01-05 15:30:30

|

1389人浏览过

|

来源于php中文网

原创

自 java 5 开始,java.util.concurrent.locks 包中包含了一些锁的实现,因此你不用去实现自己的锁了。但是你仍然需要去了解怎样使用这些锁。

一个简单的锁

让我们从 java 中的一个同步块开始:

public class Counter{
  private int count = 0;
 
  public int inc(){
    synchronized(this){
      return ++count;
    }
  }
}

可以看到在 inc()方法中有一个 synchronized(this)代码块。该代码块可以保证在同一时间只有一个线程可以执行 return ++count。虽然在 synchronized 的同步块中的代码可以更加复杂,但是++count 这种简单的操作已经足以表达出线程同步的意思。

以下的 Counter 类用 Lock 代替 synchronized 达到了同样的目的:

立即学习Java免费学习笔记(深入)”;

public class Counter{
  private Lock lock = new Lock();
  private int count = 0;
 
  public int inc(){
    lock.lock();
    int newCount = ++count;
    lock.unlock();
    return newCount;
  }
}

lock()方法会对 Lock 实例对象进行加锁,因此所有对该对象调用 lock()方法的线程都会被阻塞,直到该 Lock 对象的 unlock()方法被调用。

这里有一个 Lock 类的简单实现:

public class Counter{
public class Lock{
  private boolean isLocked = false;
 
  public synchronized void lock()
    throws InterruptedException{
    while(isLocked){
      wait();
    }
    isLocked = true;
  }
 
  public synchronized void unlock(){
    isLocked = false;
    notify();
  }
}

注意其中的 while(isLocked)循环,它又被叫做“自旋锁”。当 isLocked 为 true 时,调用 lock()的线程在 wait()调用上阻塞等待。为防止该线程没有收到 notify()调用也从 wait()中返回(也称作虚假唤醒),这个线程会重新去检查 isLocked 条件以决定当前是否可以安全地继续执行还是需要重新保持等待,而不是认为线程被唤醒了就可以安全地继续执行了。如果 isLocked 为 false,当前线程会退出 while(isLocked)循环,并将 isLocked 设回 true,让其它正在调用 lock()方法的线程能够在 Lock 实例上加锁。

当线程完成了临界区(位于 lock()和 unlock()之间)中的代码,就会调用 unlock()。执行 unlock()会重新将 isLocked 设置为 false,并且通知(唤醒)其中一个(若有的话)在 lock()方法中调用了 wait()函数而处于等待状态的线程。

锁的可重入性

Java 中的 synchronized 同步块是可重入的。这意味着如果一个 java 线程进入了代码中的 synchronized 同步块,并因此获得了该同步块使用的同步对象对应的管程上的锁,那么这个线程可以进入由同一个管程对象所同步的另一个 java 代码块。下面是一个例子:

public class Reentrant{
  public synchronized outer(){
    inner();
  }
 
  public synchronized inner(){
    //do something
  }
}

注意 outer()和 inner()都被声明为 synchronized,这在 Java 中和 synchronized(this)块等效。如果一个线程调用了 outer(),在 outer()里调用 inner()就没有什么问题,因为这两个方法(代码块)都由同一个管程对象(”this”)所同步。如果一个线程已经拥有了一个管程对象上的锁,那么它就有权访问被这个管程对象同步的所有代码块。这就是可重入。线程可以进入任何一个它已经拥有的锁所同步着的代码块。

前面给出的锁实现不是可重入的。如果我们像下面这样重写 Reentrant 类,当线程调用 outer()时,会在 inner()方法的 lock.lock()处阻塞住。

public class Reentrant2{
  Lock lock = new Lock();
 
  public outer(){
    lock.lock();
    inner();
    lock.unlock();
  }
 
  public synchronized inner(){
    lock.lock();
    //do something
    lock.unlock();
  }
}

调用 outer()的线程首先会锁住 Lock 实例,然后继续调用 inner()。inner()方法中该线程将再一次尝试锁住 Lock 实例,结果该动作会失败(也就是说该线程会被阻塞),因为这个 Lock 实例已经在 outer()方法中被锁住了。

《PHP设计模式指南》中文版
《PHP设计模式指南》中文版

《PHP设计模式》首先介绍了设计模式,讲述了设计模式的使用及重要性,并且详细说明了应用设计模式的场合。接下来,本书通过代码示例介绍了许多设计模式。最后,本书通过全面深入的案例分析说明了如何使用设计模式来计划新的应用程序,如何采用PHP语言编写这些模式,以及如何使用书中介绍的设计模式修正和重构已有的代码块。作者采用专业的、便于使用的格式来介绍相关的概念,自学成才的编程人员与经过更多正规培训的编程人员

下载

两次 lock()之间没有调用 unlock(),第二次调用 lock 就会阻塞,看过 lock()实现后,会发现原因很明显:

public class Lock{
  boolean isLocked = false;
 
  public synchronized void lock()
    throws InterruptedException{
    while(isLocked){
      wait();
    }
    isLocked = true;
  }
 
  ...
}

一个线程是否被允许退出 lock()方法是由 while 循环(自旋锁)中的条件决定的。当前的判断条件是只有当 isLocked 为 false 时 lock 操作才被允许,而没有考虑是哪个线程锁住了它。

为了让这个 Lock 类具有可重入性,我们需要对它做一点小的改动:

public class Lock{
  boolean isLocked = false;
  Thread lockedBy = null;
  int lockedCount = 0;
 
  public synchronized void lock()
    throws InterruptedException{
    Thread callingThread =
      Thread.currentThread();
    while(isLocked && lockedBy != callingThread){
      wait();
    }
    isLocked = true;
    lockedCount++;
    lockedBy = callingThread;
 }
 
  public synchronized void unlock(){
    if(Thread.curentThread() ==
      this.lockedBy){
      lockedCount--;
 
      if(lockedCount == 0){
        isLocked = false;
        notify();
      }
    }
  }
 
  ...
}

注意到现在的 while 循环(自旋锁)也考虑到了已锁住该 Lock 实例的线程。如果当前的锁对象没有被加锁(isLocked = false),或者当前调用线程已经对该 Lock 实例加了锁,那么 while 循环就不会被执行,调用 lock()的线程就可以退出该方法(译者注:“被允许退出该方法”在当前语义下就是指不会调用 wait()而导致阻塞)。

除此之外,我们需要记录同一个线程重复对一个锁对象加锁的次数。否则,一次 unblock()调用就会解除整个锁,即使当前锁已经被加锁过多次。在 unlock()调用没有达到对应 lock()调用的次数之前,我们不希望锁被解除。

现在这个 Lock 类就是可重入的了。

锁的公平性

Java 的 synchronized 块并不保证尝试进入它们的线程的顺序。因此,如果多个线程不断竞争访问相同的 synchronized 同步块,就存在一种风险,其中一个或多个线程永远也得不到访问权 —— 也就是说访问权总是分配给了其它线程。这种情况被称作线程饥饿。为了避免这种问题,锁需要实现公平性。本文所展现的锁在内部是用 synchronized 同步块实现的,因此它们也不保证公平性。

在 finally 语句中调用 unlock()

如果用 Lock 来保护临界区,并且临界区有可能会抛出异常,那么在 finally 语句中调用 unlock()就显得非常重要了。这样可以保证这个锁对象可以被解锁以便其它线程能继续对其加锁。以下是一个示例:

lock.lock();
try{
  //do critical section code,
  //which may throw exception
} finally {
  lock.unlock();
}

这个简单的结构可以保证当临界区抛出异常时 Lock 对象可以被解锁。如果不是在 finally 语句中调用的 unlock(),当临界区抛出异常时,Lock 对象将永远停留在被锁住的状态,这会导致其它所有在该 Lock 对象上调用 lock()的线程一直阻塞。

以上就是关于 java 多线程锁的资料整理,后续继续补充相关资料,谢谢大家对本站的支持!

更多java 多线程-锁详解及示例代码相关文章请关注PHP中文网!

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

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

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
C++ 设计模式与软件架构
C++ 设计模式与软件架构

本专题深入讲解 C++ 中的常见设计模式与架构优化,包括单例模式、工厂模式、观察者模式、策略模式、命令模式等,结合实际案例展示如何在 C++ 项目中应用这些模式提升代码可维护性与扩展性。通过案例分析,帮助开发者掌握 如何运用设计模式构建高质量的软件架构,提升系统的灵活性与可扩展性。

8

2026.01.30

c++ 字符串格式化
c++ 字符串格式化

本专题整合了c++字符串格式化用法、输出技巧、实践等等内容,阅读专题下面的文章了解更多详细内容。

8

2026.01.30

java 字符串格式化
java 字符串格式化

本专题整合了java如何进行字符串格式化相关教程、使用解析、方法详解等等内容。阅读专题下面的文章了解更多详细教程。

6

2026.01.30

python 字符串格式化
python 字符串格式化

本专题整合了python字符串格式化教程、实践、方法、进阶等等相关内容,阅读专题下面的文章了解更多详细操作。

1

2026.01.30

java入门学习合集
java入门学习合集

本专题整合了java入门学习指南、初学者项目实战、入门到精通等等内容,阅读专题下面的文章了解更多详细学习方法。

20

2026.01.29

java配置环境变量教程合集
java配置环境变量教程合集

本专题整合了java配置环境变量设置、步骤、安装jdk、避免冲突等等相关内容,阅读专题下面的文章了解更多详细操作。

17

2026.01.29

java成品学习网站推荐大全
java成品学习网站推荐大全

本专题整合了java成品网站、在线成品网站源码、源码入口等等相关内容,阅读专题下面的文章了解更多详细推荐内容。

18

2026.01.29

Java字符串处理使用教程合集
Java字符串处理使用教程合集

本专题整合了Java字符串截取、处理、使用、实战等等教程内容,阅读专题下面的文章了解详细操作教程。

3

2026.01.29

Java空对象相关教程合集
Java空对象相关教程合集

本专题整合了Java空对象相关教程,阅读专题下面的文章了解更多详细内容。

6

2026.01.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Java 教程
Java 教程

共578课时 | 53.4万人学习

HTML教程
HTML教程

共500课时 | 5.1万人学习

Python 并发编程实战
Python 并发编程实战

共12课时 | 0.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号