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,结束时…...
LeetCode 2535.数组元素和与数字和的绝对差:模拟
【LetMeFly】2535.数组元素和与数字和的绝对差:模拟 力扣题目链接:https://leetcode.cn/problems/difference-between-element-sum-and-digit-sum-of-an-array/ 给你一个正整数数组 nums 。 元素和 是 nums 中的所有元素相加求和。数字和 是 nums 中每…...
SpringCloud-pom创建Eureka
<?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 https://…...
动态规划算法专题(一):斐波那契数列模型
目录 1、动态规划简介 2、算法实战应用【leetcode】 2.1 题一:第N个泰波那契数 2.1.1 算法原理 2.1.2 算法代码 2.1.3 空间优化原理——滚动数组 2.1.4 算法代码——空间优化版本 2.2 题二:三步问题 2.2.1 算法原理 2.2.2 算法代码 2.3 题二&a…...
H.264编解码工具 - x264
一、简介 x264是一个开源的H.264/AVC视频编码库,它可以将视频数据压缩成H.264格式,并且可以从H.264格式解码出原始视频数据。 x264是以C语言编写的,并且可以在多个平台上使用,包括Windows、Linux和Mac OS等操作系统。 x264具有很高的编码效率和视频质量,它支持多种编码…...
外卖点餐小程序源码系统 单店多门店自助切换 带完整的安装代码包以及搭建部署教程
系统概述 本外卖点餐小程序源码系统旨在帮助餐饮企业和商家快速搭建一个功能完善的在线外卖平台。系统支持单店与多门店的灵活切换,方便商家根据自身业务需求进行管理和运营。同时,系统还提供了丰富的营销工具和数据分析功能,助力商家实现精…...
通过Ideal和gitbash共同实现分支合并
文章目录 背景描述:演示jy_20240704_develop分支同步到jy_dev分支方式一方式二 背景描述: 目前项目里有四个分支,分别是master、jy_20240704_develop、jy_dev、jy_qas。 其中master是主分支,其他三个分支都是根据master来创建的…...
Vue.js 组件开发
Vue.js 是一个渐进式的JavaScript框架,主要用于构建用户界面。它采用了组件化的开发方式,使得前端开发更加高效、灵活且易于维护。组件是Vue.js的核心概念之一,理解和掌握组件的开发,有助于我们高效地构建现代Web应用。 本文将涵…...
【Lcode 随笔】C语言版看了不后悔系列持续更新中。。。
文章目录 题目一:最长回文子串题目描述:示例输入与输出:题目分析:解题思路:示例代码:深入剖析: 题目二:合并K个有序链表题目描述:示例输入与输出:题目分析&am…...
排序--希尔排序
希尔排序介绍 希尔排序核心思想就是:1,分组;2,直接插入排序:越有序越快 希尔排序就是多次利用直接插入排序的一个排序算法. 希尔排序的算法思想:间隔式分组,利用直接插入排序让组内有序,然后缩小分组再次排序,直到组数为1希尔排序的理论基础就是直接插入排序越有序越快; 希尔排…...
【教程】57帧! Mac电脑流畅运行黑神话悟空
转载请注明出处:小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你,欢迎[点赞、收藏、关注]哦~ 1、先安装CrossOver。网上有许多和谐版,可自行搜索。(pd虚拟机里运行黑神话估计够呛的) 2、运行CrossOver…...
网站搭建 保定/搜索百度网址网页
作说明及PC端控制软件https://share.weiyun.com/5HFmWGl或https://pan.baidu.com/s/1SXz5BsYJiAF-eUpFG_2l-g 提取码: kixh或https://drive.google.com/open?id1-JViWLBOIzaHTdwdONX2RP8S4EgWxoNDDIY的矢量网络分析仪,参考了日本edy555的相关设计,修改了部分电路&a…...
广州网站推广排名/网址大全百度
一、将你要发布的moudle的build.gradle中添加代码,gradle的最后添加 PUBLISH_GROUP_ID com.zzti.fengyongge PUBLISH_ARTIFACT_ID imagepicker PUBLISH_VERSION 1.0 apply from: https://raw.githubusercontent.com/blundell/release-android-library/master/and…...
wordpress完整安装包/网址安全检测中心
目录 JQuery初级 概念快速入门JQuery对象和JS原生对象的区别选择器DOM操作 内容操作属性操作CRUD操作 案例 JQuery高级 动画遍历事件绑定插件 Ajax 概念实现方式 原生JS实现(了解)JQuery实现方式 $.ajax()$.get()$.post() json 概念语法 基本规则获取数据遍历 …...
全国电子网站建设/今日头条最新新闻消息
从 1 到完美,用 js 和 react-native 写一个 APP facebook 在 2013 年开源了 react 后,紧接着在 2015 年就又开源了 react-native,就此打开了用 js 和前端技术写原生 android&ios APP 之路。尽管到目前为止 react-native 最新版本是 0.56.…...
青岛做网站建设定制/seo需要培训才能找到工作吗
1.请说明如下注解的作用。RequestMapping,RequestParam,RequestBody,ResponseBody RequestMapping是Spring Web应用程序中最常被用到的注解之一,主要用来处理请求地址映射,可以用在类或方法上。用在类上,表…...
建设一个网站需要做哪些工作内容/网站seo是干什么的
1.1 几种常见存储设备的接口 1.IDE接口 IDE的英文全称为"Integrated Drive Electronics",即"电子集成驱动器",是曾经主流的硬盘接口。IDE接口也称之为ATA接口。ATA的英文拼写为"Advanced Technology Attachment"。2003年推…...