Java 中 synchronized 和 Thread 的使用场合介绍
在 Java 编程中,synchronized
和 Thread
是处理并发与多线程编程的关键工具。多线程编程是为了在单一程序中并行执行多个任务,Java 提供了丰富的 API 和关键字以实现这一目标,而其中 synchronized
和 Thread
是非常基础和重要的部分。
synchronized
的用途和实现机制
synchronized
关键字用于在 Java 程序中实现线程同步。线程同步的主要目的是解决多线程访问共享资源时的竞争问题,确保数据的一致性。Java 提供了内置的同步机制来避免多个线程同时访问共享资源而引发的竞争条件。synchronized
可以用来修饰方法或代码块,确保在某一时刻只能有一个线程访问被修饰的代码片段。
synchronized
的三种使用方式
- 修饰实例方法:当一个方法被
synchronized
修饰后,线程在调用该方法时会先获得当前对象的锁。其他线程如果也想调用该方法或其他同样使用了synchronized
修饰的实例方法,则必须等待当前线程释放锁。典型的使用场景是多个线程同时操作同一对象时需要同步。
-
修饰静态方法:当一个静态方法被
synchronized
修饰后,线程在调用该方法时会获得该类对象的类级别锁。由于静态方法属于类而不是某个实例,因此此种情况下加锁的是类对象本身,而非具体的实例。 -
修饰代码块:代码块级别的同步通过
synchronized
锁定特定的对象,而非整个方法。这样可以灵活控制锁的粒度,减少对性能的影响。这种方式尤其适合只需要同步部分代码的场景,而非整个方法。
JVM 中的实现
synchronized
的底层实现依赖于 JVM 的对象监视器(Monitor)。在 JVM 中,每个对象都关联有一个监视器对象,当一个线程进入 synchronized
块时,它必须首先获得与该对象关联的监视器。当该线程获得监视器时,其他线程就无法再进入该对象的 synchronized
代码块,直到当前线程退出并释放监视器。
上图出处:https://medium.com/javarevisited/whats-a-monitor-in-java-8f0ebecaea2a
监视器的实现依赖于两条 JVM 指令:monitorenter
和 monitorexit
。在 Java 字节码层面,编译后的 synchronized
代码块会插入 monitorenter
和 monitorexit
指令,分别对应线程进入和退出同步代码块。JVM 在执行这两条指令时会检查当前线程是否拥有该监视器,确保锁的获取和释放是正确的。
具体来看,monitorenter
指令会在进入 synchronized
代码块时执行,它会尝试获取监视器对象的所有权。如果获取成功,线程可以进入临界区;如果获取失败,线程将被阻塞,直到监视器被释放。monitorexit
指令则在 synchronized
代码块执行完毕后释放监视器,使其他等待的线程有机会获得锁。
值得注意的是,JVM 优化了 synchronized
的性能,引入了偏向锁(biased locking)、轻量级锁和重量级锁等机制,以减少线程争抢锁的开销。在没有竞争的情况下,锁的获取和释放成本极低,提升了多线程环境下的性能。
真实世界的例子
假设一个在线银行系统中,多个用户可能同时对他们的账户进行操作,比如存款、取款和转账。为了避免多个线程同时操作同一个账户时引发的数据不一致问题,可以使用 synchronized
确保每次只能有一个线程执行修改操作。
public class BankAccount {private int balance;public BankAccount(int initialBalance) {this.balance = initialBalance;}public synchronized void deposit(int amount) {balance += amount;}public synchronized void withdraw(int amount) {if (balance >= amount) {balance -= amount;} else {System.out.println("Insufficient funds");}}public synchronized int getBalance() {return balance;}
}
在上面的代码中,deposit
、withdraw
和 getBalance
方法都被 synchronized
修饰,保证多个线程访问这些方法时不会发生竞态条件。
Thread
的用途和实现机制
在 Java 中,Thread
是创建并管理线程的基本类。每个 Thread
对象代表一个独立执行的线程。Java 提供了两种实现多线程的方式:通过继承 Thread
类或者实现 Runnable
接口。
Thread
的使用方式
- 继承
Thread
类:通过继承Thread
类并重写run()
方法来定义线程的任务。线程启动时调用start()
方法,系统会自动调用run()
方法执行线程中的任务。
class MyThread extends Thread {public void run() {System.out.println("Thread is running...");}
}public class Main {public static void main(String[] args) {MyThread thread = new MyThread();thread.start();}
}
- 实现
Runnable
接口:这是创建线程的另一种方式,通过实现Runnable
接口并重写run()
方法定义任务,然后将Runnable
对象传递给Thread
对象。
class MyRunnable implements Runnable {public void run() {System.out.println("Thread is running...");}
}public class Main {public static void main(String[] args) {Thread thread = new Thread(new MyRunnable());thread.start();}
}
两者的区别在于,继承 Thread
类的方式不能再继承其他类,而实现 Runnable
接口的方式可以实现多重继承。因此,推荐使用 Runnable
接口实现多线程。
JVM 中的线程模型
Java 的线程由 JVM 和底层操作系统共同管理。在 JVM 层面,每个 Thread
对象都与一个操作系统的原生线程关联。线程的调度由底层操作系统完成,JVM 通过操作系统的 API 创建、销毁和管理线程。
在现代操作系统中,线程调度通常基于时间片轮转(time slicing)或抢占式调度(preemptive scheduling)。操作系统会根据优先级、时间片等因素在不同线程之间切换,确保多线程程序的高效运行。JVM 的线程调度由操作系统提供支持,确保多线程程序能够充分利用系统资源。
Java 中的线程有五种基本状态:新建(New)、就绪(Runnable)、运行中(Running)、阻塞(Blocked)和终止(Terminated)。线程从创建到结束的整个生命周期都由 JVM 进行管理。JVM 通过线程调度器(Thread Scheduler)决定哪个线程在某一时刻应该被执行,线程的调度是不可预知的,具体的调度方式依赖于底层操作系统。
线程的同步与通信
在多线程编程中,线程之间的同步与通信是一个重要的话题。除了通过 synchronized
实现的线程同步外,Java 还提供了 wait()
、notify()
和 notifyAll()
等方法进行线程之间的通信。这些方法用于在多个线程之间协调工作,避免线程的忙等待(busy waiting)问题。
当一个线程执行 wait()
方法时,它会释放持有的锁并进入等待状态,直到其他线程调用 notify()
或 notifyAll()
方法唤醒它。这种机制通常用于生产者-消费者模型中,生产者线程负责生成数据,消费者线程负责处理数据,wait()
和 notify()
用于协调两者的工作。
线程池的使用场景
虽然 Thread
类是多线程编程的基础,但在实际应用中直接使用 Thread
并不是最优的选择,尤其是在需要频繁创建和销毁线程的场景下。线程的创建和销毁是昂贵的操作,会影响程序的性能。为了解决这个问题,Java 提供了线程池(Thread Pool)机制,允许重用现有的线程,而不是每次都创建新的线程。
Java 的 java.util.concurrent
包提供了多种线程池的实现,如 FixedThreadPool
、CachedThreadPool
和 ScheduledThreadPool
等。线程池通过事先创建一定数量的线程,并将任务提交给这些线程处理,从而避免频繁创建和销毁线程的开销。
真实世界的例子
假设一个 Web 服务器需要处理大量并发请求,每个请求可能涉及复杂的计算和 I/O 操作。为了提高服务器的性能,通常会使用线程池来处理这些请求,而不是为每个请求都创建一个新的线程。下面的例子展示了如何使用线程池处理并发任务:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class WebServer {private final ExecutorService threadPool = Executors.newFixedThreadPool(10);public void handleRequest(Runnable request) {threadPool.execute(request);}public void shutdown() {threadPool.shutdown();}public static void main(String[] args) {WebServer server = new WebServer();// 模拟多个并发请求for (int i = 0; i < 100; i++) {server.handleRequest(() -> System.out.println("Handling request in thread: " + Thread.currentThread().getName()));}server.shutdown();}
}
volatile
与 synchronized
的对比
在并发编程中,除了 synchronized
,Java 还提供了 volatile
关键字来控制变量的可见性。volatile
关键字用于修饰共享变量,保证变量的可见性,但不保证操作的原子性。相较于 synchronized
,volatile
更加轻量级,但适用的场景较为有限。
当一个变量被声明为 volatile
时,JVM 保证所有线程在访问该变量时都能读取到最新的值。JVM 通过禁用线程的本地缓存确保这一点。因此,volatile
适用于简单的标志位变量,而不适用于复杂的线程同步场景。
真实世界的例子
volatile
适用于某些无需复杂同步的场景,例如一个停止标志位:
public class VolatileExample {private volatile boolean running = true;public void start() {while (running) {System.out.println("Thread is running...");}}public void stop() {running = false;}public static void main(String[] args) {VolatileExample example = new VolatileExample();Thread thread = new Thread(example::start);thread.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}example.stop();}
}
在这个例子中,running
变量被声明为 volatile
,确保当 stop()
方法被调用时,running
的修改能被其他线程立即看到,从而使线程正确退出。
结束语
Thread
和 synchronized
是 Java 多线程编程的核心概念。通过理解它们的用途和在 JVM 中的实现机制,开发者可以编写高效的并发程序。在实际应用中,合理地使用线程池、volatile
关键字以及高级并发工具,可以帮助开发者更好地管理并发操作,提升程序的性能和稳定性。
相关文章:

Java 中 synchronized 和 Thread 的使用场合介绍
在 Java 编程中,synchronized 和 Thread 是处理并发与多线程编程的关键工具。多线程编程是为了在单一程序中并行执行多个任务,Java 提供了丰富的 API 和关键字以实现这一目标,而其中 synchronized 和 Thread 是非常基础和重要的部分。 synch…...
爬虫库是什么?是ip吗
爬虫库通常指的是用于网页爬虫(Web Scraping)开发的代码库或框架,它不是IP地址。以下是关于爬虫库的详细解释: 爬虫库的定义 爬虫库是一些用于简化网络数据抓取过程的工具和框架,通常提供了一系列函数和类࿰…...

【MySQL】查询原理 —— B+树查询数据全过程
使用B树作为索引结构的原因: 一种自平衡树: B树在插入和删除的时候节点会进行分裂和合并操作,以保持树的平衡,存在冗余节点,使得删除的时候树结构变化小,更高效。 高度不会增长过快,查询磁盘I…...

系统设置 WIFI输入框被挡住解决方案
文章目录 问题点复现的场景机器横屏可复现,竖屏不存在跟density 相关的。 解决问题方案设置输入模式路径 部分源码跟踪方法 延伸思考设置输入模式设置主题 问题点 进入系统设置-网络和互联网-WLAN-点击WIFI item ,密码输入框被遮挡,输入的密码不可见.如…...

SpringCloud无法注册Nacos和配置中心
今天升级SpringCloud版本,导致服务无法注册到nacos,使用nacos作为配置中心也无法刷新配置信息,后来发现是因为只更新了SpringCloud版本,SpringCloud-Alibaba没有更新导致的问题。 升级出现问题的版本是: <dependen…...

word2vector训练数据集整理(代码实现)
import math import os import random import torch import dltools from matplotlib import pyplot as plt #读取数据集 def read_ptb():"""将PTB数据集加载到文本行的列表中"""with open(./ptb/ptb.train.txt) as f:raw_text f.read()return…...

无心上班,只想为祖国庆生?让ChatGPT帮你搞定工作!
国庆假期临近,大家的心早已飞向诗和远方了吧。 然而,现实总是无情地将我们拉回到堆积如山的工作任务上:紧急报告的截止日期就在眼前,复杂的项目策划还未动笔,客户的定制需求迫在眉睫。每年的这个时候,如何…...

【Python】YOLO牛刀小试:快速实现视频物体检测
YOLO牛刀小试:快速实现视频物体检测 在深度学习的众多应用中,物体检测是一个热门且重要的领域。YOLO(You Only Look Once)系列模型以其快速和高效的特点,成为了物体检测的首选之一。本文将介绍如何使用YOLOv8模型进行…...

Vscode超好看的渐变主题插件
样式效果: 插件使用方法: 然后重启,之后会显示vccode损坏,不用理会,因为这个插件是更改了应用内部代码,直接不再显示即可。...

OceanBase技术解析:自适应分布式下压技术
在《OceanBase 数据库源码解析》这本书中,关于SQL执行器的深入剖析相对较少,因此,希望增添一些实用且详尽的补充内容。 上一篇博客《 OceanBase技术解析: 执行器中的自适应技术》中,已初步介绍了执行器中几项典型的自适…...
Firebase和JavaScript创建Postback Link逻辑
Firebase是一个提供后端即服务(BaaS)的平台,它允许开发者快速构建应用程序而无需管理服务器。Firebase不直接提供生成Postback Link的功能,但您可以使用Firebase的功能来构建和管理URL,然后在客户端使用这些URL来实现Postback。 以下是如何使用Firebase和JavaScript来创建…...
docker配置daemon.json文件
报错 :Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers) 解决方法 配置加速地址 vim /etc/docker/daemon.json添加以下内容 {"registry-mirro…...

【08】纯血鸿蒙HarmonyOS NEXT星河版开发0基础学习笔记-Scroll容器与Tabs组件
序言: 本文详细讲解了关于我们在页面上经常看到的可滚动页面和导航栏在鸿蒙开发中如何用Scroll和Tabs组件实现,介绍了Scroll和Tabs的基本用法与属性。 笔者也是跟着B站黑马的课程一步步学习,学习的过程中添加部分自己的想法整理为笔记分享出…...

苏州 数字化科技展厅展馆-「世岩科技」一站式服务商
数字化科技展厅展馆设计施工是一个综合性强、技术要求高的项目,涉及到众多方面的要点。以下是对数字化科技展厅展馆设计施工要点的详细分析: 一、明确目标与定位 在设计之初,必须明确展厅的目标和定位。这包括确定展厅的主题、目标受众、展…...
音频搜索公司 DeepGram,定位语音搜索AI大脑,DeepGram想做“音频版”
1. 亦仁分享 DeepGram 成立于 2015 年,位于美国山景城,是一家基于 AI 技术的音频搜索引擎公司。运用机器学习进行语音识别、搜寻重要时刻并对音频和视频进行分类,帮助用户快速索引和浏览音频和视频文件,包括电话语音、会议语音、…...

基于php的在线租房管理系统
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏:Java精选实战项目…...

如何评价 Python 语言的运行速度
Python 作为一门编程语言,其运行速度一直是业界讨论的焦点。它的简洁语法和广泛的应用使得它在开发过程中非常高效,然而,运行速度与一些更底层的编程语言相比存在一定的劣势。这是否是由于 Python 语法的简洁性所带来的代价?我们可…...

Tomcat系列漏洞复现
CVE-2017-12615——Tomcat put⽅法任意⽂件写⼊漏洞 漏洞描述 当 Tomcat运⾏在Windows操作系统时,且启⽤了HTTP PUT请求⽅法(例如,将 readonly初始化参数由默认值设置为false),攻击者将有可能可通过精⼼构造的攻击请求…...
K8S拉取本地docker中registry的镜像报错:http: server gave HTTP response to HTTPS client
本地部署了一个K8S集群,但是worker1和worker2的docker无法拉取外面的镜像,docker的daemon.json也配置了,无法下载,于是在master部署了一个docker registry。 但是pod还是无法拉取registry的镜像并报错。 我这里使用的是container…...

Leetcode 1235. 规划兼职工作
1.题目基本信息 1.1.题目描述 你打算利用空闲时间来做兼职工作赚些零花钱。 这里有 n 份兼职工作,每份工作预计从 startTime[i] 开始到 endTime[i] 结束,报酬为 profit[i]。 给你一份兼职工作表,包含开始时间 startTime,结束时…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...

ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...

uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...

Unity UGUI Button事件流程
场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...