Java多线程访问Synchronized同步方法的八种使用场景
Java资料站
共 15707字,需浏览 32分钟
· 2021-07-22
点击上方蓝色字体,选择“标星公众号”
优质文章,第一时间送达
简介
八种使用场景:
场景一:两个线程同时访问同一个对象的同步方法
两个线程同时访问同一个对象的同步方法,是线程安全的。
场景二:两个线程同时访问两个对象的同步方法
两个线程同时访问两个对象的同步方法,是线程不安全的。
代码验证:
public class Condition2 implements Runnable {
// 创建两个不同的对象
static Condition2 instance1 = new Condition2();
static Condition2 instance2 = new Condition2();
@Override
public void run() {
method();
}
private synchronized void method() {
System.out.println("线程名:" + Thread.currentThread().getName() + ",运行开始");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + Thread.currentThread().getName() + ",运行结束");
}
public static void main(String[] args) {
Thread thread1 = new Thread(instance1);
Thread thread2 = new Thread(instance2);
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
System.out.println("测试结束");
}
}
运行结果:
线程名:Thread-0,运行开始
线程名:Thread-1,运行开始
线程:Thread-0,运行结束
线程:Thread-1,运行结束
测试结束
代码分析:
场景三:两个线程同时访问(一个或两个)对象的静态同步方法
两个线程同时访问(一个或两个)对象的静态同步方法,是线程安全的。
场景四:两个线程分别同时访问(一个或两个)对象的同步方法和非同步方法
两个线程分别同时访问(一个或两个)对象的同步方法和非同步方法,是线程不安全的。
public class Condition4 implements Runnable {
static Condition4 instance = new Condition4();
@Override
public void run() {
//两个线程访问同步方法和非同步方法
if (Thread.currentThread().getName().equals("Thread-0")) {
//线程0,执行同步方法method0()
method0();
}
if (Thread.currentThread().getName().equals("Thread-1")) {
//线程1,执行非同步方法method1()
method1();
}
}
// 同步方法
private synchronized void method0() {
System.out.println("线程名:" + Thread.currentThread().getName() + ",同步方法,运行开始");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + Thread.currentThread().getName() + ",同步方法,运行结束");
}
// 普通方法
private void method1() {
System.out.println("线程名:" + Thread.currentThread().getName() + ",普通方法,运行开始");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + Thread.currentThread().getName() + ",普通方法,运行结束");
}
public static void main(String[] args) {
Thread thread1 = new Thread(instance);
Thread thread2 = new Thread(instance);
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
System.out.println("测试结束");
}
}
运行结果:
线程名:Thread-0,同步方法,运行开始
线程名:Thread-1,普通方法,运行开始
线程:Thread-0,同步方法,运行结束
线程:Thread-1,普通方法,运行结束
测试结束
结果分析
非同步方法不受其它由synchronized修饰的同步方法影响
场景五:两个线程访问同一个对象中的同步方法,同步方法又调用一个非同步方法
public class Condition8 implements Runnable {
static Condition8 instance = new Condition8();
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) {
//直接调用普通方法
method2();
} else {
// 先调用同步方法,在同步方法内调用普通方法
method1();
}
}
// 同步方法
private static synchronized void method1() {
System.out.println("线程名:" + Thread.currentThread().getName() + ",同步方法,运行开始");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + Thread.currentThread().getName() + ",同步方法,运行结束,开始调用普通方法");
method2();
}
// 普通方法
private static void method2() {
System.out.println("线程名:" + Thread.currentThread().getName() + ",普通方法,运行开始");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + Thread.currentThread().getName() + ",普通方法,运行结束");
}
public static void main(String[] args) {
// 此线程直接调用普通方法
Thread thread0 = new Thread(instance);
// 这两个线程直接调用同步方法
Thread thread1 = new Thread(instance);
Thread thread2 = new Thread(instance);
thread0.start();
thread1.start();
thread2.start();
while (thread0.isAlive() || thread1.isAlive() || thread2.isAlive()) {
}
System.out.println("测试结束");
}
}
运行结果:
线程名:Thread-0,普通方法,运行开始
线程名:Thread-1,同步方法,运行开始
线程:Thread-1,同步方法,运行结束,开始调用普通方法
线程名:Thread-1,普通方法,运行开始
线程:Thread-0,普通方法,运行结束
线程:Thread-1,普通方法,运行结束
线程名:Thread-2,同步方法,运行开始
线程:Thread-2,同步方法,运行结束,开始调用普通方法
线程名:Thread-2,普通方法,运行开始
线程:Thread-2,普通方法,运行结束
测试结束
结果分析:
两个线程访问同一个对象中的同步方法,同步方法又调用一个非同步方法,仅在没有其他线程直接调用非同步方法的情况下,是线程安全的。若有其他线程直接调用非同步方法,则是线程不安全的。
场景六:两个线程同时访问同一个对象的不同的同步方法
两个线程同时访问同一个对象的不同的同步方法时,是线程安全的。
public class Condition5 implements Runnable {
static Condition5 instance = new Condition5();
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) {
//线程0,执行同步方法method0()
method0();
}
if (Thread.currentThread().getName().equals("Thread-1")) {
//线程1,执行同步方法method1()
method1();
}
}
private synchronized void method0() {
System.out.println("线程名:" + Thread.currentThread().getName() + ",同步方法0,运行开始");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + Thread.currentThread().getName() + ",同步方法0,运行结束");
}
private synchronized void method1() {
System.out.println("线程名:" + Thread.currentThread().getName() + ",同步方法1,运行开始");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + Thread.currentThread().getName() + ",同步方法1,运行结束");
}
//运行结果:串行
public static void main(String[] args) {
Thread thread1 = new Thread(instance);
Thread thread2 = new Thread(instance);
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
System.out.println("测试结束");
}
}
运行结果:
线程名:Thread-1,同步方法1,运行开始
线程:Thread-1,同步方法1,运行结束
线程名:Thread-0,同步方法0,运行开始
线程:Thread-0,同步方法0,运行结束
测试结束
结果分析:
场景七:两个线程分别同时访问静态synchronized和非静态synchronized方法
两个线程分别同时访问静态synchronized和非静态synchronized方法,线程不安全。
代码实现:
public class Condition6 implements Runnable {
static Condition6 instance = new Condition6();
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) {
//线程0,执行静态同步方法method0()
method0();
}
if (Thread.currentThread().getName().equals("Thread-1")) {
//线程1,执行非静态同步方法method1()
method1();
}
}
// 重点:用static synchronized 修饰的方法,属于类锁,锁对象为(*.class)对象。
private static synchronized void method0() {
System.out.println("线程名:" + Thread.currentThread().getName() + ",静态同步方法0,运行开始");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + Thread.currentThread().getName() + ",静态同步方法0,运行结束");
}
// 重点:synchronized 修饰的方法,属于方法锁,锁对象为(this)对象。
private synchronized void method1() {
System.out.println("线程名:" + Thread.currentThread().getName() + ",非静态同步方法1,运行开始");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + Thread.currentThread().getName() + ",非静态同步方法1,运行结束");
}
//运行结果:并行
public static void main(String[] args) {
//问题原因: 线程1的锁是类锁(*.class)对象,线程2的锁是方法锁(this)对象,两个线程的锁不一样,自然不会互相影响,所以会并行执行。
Thread thread1 = new Thread(instance);
Thread thread2 = new Thread(instance);
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
System.out.println("测试结束");
}
运行结果:
线程名:Thread-0,静态同步方法0,运行开始
线程名:Thread-1,非静态同步方法1,运行开始
线程:Thread-1,非静态同步方法1,运行结束
线程:Thread-0,静态同步方法0,运行结束
测试结束
场景八:同步方法抛出异常后,JVM会自动释放锁的情况
只有当同步方法执行完或执行时抛出异常这两种情况,才会释放锁。
代码实现:
public class Condition7 implements Runnable {
private static Condition7 instance = new Condition7();
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) {
//线程0,执行抛异常方法method0()
method0();
}
if (Thread.currentThread().getName().equals("Thread-1")) {
//线程1,执行正常方法method1()
method1();
}
}
private synchronized void method0() {
System.out.println("线程名:" + Thread.currentThread().getName() + ",运行开始");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//同步方法中,当抛出异常时,JVM会自动释放锁,不需要手动释放,其他线程即可获取到该锁
System.out.println("线程名:" + Thread.currentThread().getName() + ",抛出异常,释放锁");
throw new RuntimeException();
}
private synchronized void method1() {
System.out.println("线程名:" + Thread.currentThread().getName() + ",运行开始");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + Thread.currentThread().getName() + ",运行结束");
}
public static void main(String[] args) {
Thread thread1 = new Thread(instance);
Thread thread2 = new Thread(instance);
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
System.out.println("测试结束");
}
}
运行结果:
线程名:Thread-0,运行开始
线程名:Thread-0,抛出异常,释放锁
线程名:Thread-1,运行开始
Exception in thread "Thread-0" java.lang.RuntimeException
at com.study.synchronize.conditions.Condition7.method0(Condition7.java:34)
at com.study.synchronize.conditions.Condition7.run(Condition7.java:17)
at java.lang.Thread.run(Thread.java:748)
线程:Thread-1,运行结束
测试结束
结果分析:
总结
作者 | 大脑补丁
来源 | https://blog.csdn.net/x541211190/article/details/106272922
评论
了解加密货币到加密货币的互换
1、什么是加密货币互换?加密货币到加密货币的互换是指以现行市场汇率将一种加密货币直接兑换为另一种加密货币。与需要法定货币存款和较长流程的传统交易所不同,加密货币到加密货币的互换可以无缝地促进交换。掉期在提高加密货币的流动性和效率方面发挥着重要作用。该功能使用户能够将他们的加密货币与钱包中的其他代币进
区块链头条
0
李彦宏:开源大模型不如闭源,后者会持续领先;周鸿祎:“开源不如闭源” 的言论是胡说八道
架构师大咖
架构师大咖,打造有价值的架构师交流平台。分享架构师干货、教程、课程、资讯。架构师大咖,每日推送。
公众号该公众号已被封禁0、李彦宏:开源大模型不如闭源,后者会持续领先当今
源码共读
0
【第129期】程序员的新宠:三款终端工具,让你告别Xshell!
概述 WindTerm:跨平台的SSH利器 首先介绍的是WindTerm,这是一款使用C语言开发的跨平台SSH客户端。它不仅完全免费,而且没有商业使用的限制。WindTerm支持SSH v2、Telnet、Raw Tcp等协议,而且性能出色,甚至超过了FinalShell和Electerm。功能
前端微服务
0
字节员工:35岁以后被裁员的,后来都走了哪条路?现在2-2,要不要利用最后一年拼命上个岸。
架构师大咖
架构师大咖,打造有价值的架构师交流平台。分享架构师干货、教程、课程、资讯。架构师大咖,每日推送。
公众号该公众号已被封禁在当今竞争激烈的职场环境中,年龄并不总是一个决定性
源码共读
0
【送书福利】《Java面试八股文:高频面试题与求职攻略一本通》
先来唠唠最近粉丝面试回来跟我聊天,基本上都提到一个点,在面试过程中八股文占比很高(八股文70%、项目20%、10%算法)除了一些搞算法突出的厂除外。其实现在很多厂八股都是逐渐深入的方式来问,所以大家在学习的过程中,针对一些重点的内容,最好深入去学习,不然还是比较难应对这种追问式的问题。最近刚好从一位
Java后端技术
0
上班的时候,有一群摸鱼搭子非常重要...
上班的时候,有一群摸鱼搭子非常重要!一到上班时间,他们就从四面八方涌进群里冒泡...从八卦聊到股市、从职场聊到乌X兰局势,偶尔还会复读、相亲、battle...然后,下午6点钟准时消失不见...所以你要不要加入我们一起摸鱼?我们有北京、上海、深圳、广州、杭州、武汉、成都、南京等8个城市的摸鱼群,还有
产品经理日记
0
周四002 瑞超:同样落寞的境遇——北雪平vs埃尔夫斯堡
上赛季最终排名联赛第9的北雪平本赛季伊始表现不佳,4轮战罢他们仅以1胜1平2负的战绩排在倒数第三,这支历史上曾夺得13次联赛冠军、6次杯赛冠军老牌劲旅,正如英格兰赛场上的一众百年俱乐部,在低谷中不断探索着出路。球队主教练安德烈亚斯·阿尔姆曾是AIK索尔纳及赫根队的主教练,他于今年年初刚刚拿起球队教鞭
产品与体验
0
雷军辟谣了!不是高考状元,卡里也没有冰冷的 40 亿
架构师大咖
架构师大咖,打造有价值的架构师交流平台。分享架构师干货、教程、课程、资讯。架构师大咖,每日推送。
公众号该公众号已被封禁最近很火的雷军简历,听说落魄时卡里只有冰冷的 40
源码共读
0