多线程同时访问一个对象不同的synchronized方法(验证及解决)
Java中两个线程不可以同时访问同一个对象的两个不同的synchronized方法。
多个线程访问同一个类的synchronized方法时, 都是串行执行的 ! 就算有多个cpu也不例外 !
synchronized方法使用了类java的内置锁, 即锁住的是方法所属对象本身. 同一个锁某个时刻只能被一个执行线程所获取,
因此其他线程都得等待锁的释放. 因此就算你有多余的cpu可以执行, 但是你没有锁, 所以你还是不能进入synchronized方法执行, CPU因此而空闲.
如果某个线程长期持有一个竞争激烈的锁, 那么将导致其他线程都因等待所的释放而被挂起,
从而导致CPU无法得到利用, 系统吞吐量低下. 因此要尽量避免某个线程对锁的长期占有 !
public class SyncMethod {
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">synchronized</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> syncMethod2() {
</span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
System.out.println(</span>"@@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 已经获取内置锁`SyncMethod.this`)"<span style="color: #000000;">);
Thread.sleep(</span>5000<span style="color: #000000;">);
} </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
e.printStackTrace();
}
System.out.println(</span>"@@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 即将释放内置锁`SyncMethod.this`)"<span style="color: #000000;">);
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">synchronized</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> syncMethod1() {
System.out.println(</span>"######################## (syncMethod1, 已经获取内置锁`SyncMethod.this`, 并即将退出)"<span style="color: #000000;">);
}
</span><span style="color: #0000ff;">static</span> <span style="color: #0000ff;">class</span> Thread1 <span style="color: #0000ff;">extends</span><span style="color: #000000;"> Thread {
SyncMethod syncMethod;
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> Thread1(SyncMethod syncMethod) {
</span><span style="color: #0000ff;">this</span>.syncMethod =<span style="color: #000000;"> syncMethod;
}
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> run() {
syncMethod.syncMethod2();
}
}
</span><span style="color: #0000ff;">static</span> <span style="color: #0000ff;">class</span> Thread2 <span style="color: #0000ff;">extends</span><span style="color: #000000;"> Thread {
SyncMethod syncMethod;
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> Thread2(SyncMethod syncMethod) {
</span><span style="color: #0000ff;">this</span>.syncMethod =<span style="color: #000000;"> syncMethod;
}
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> run() {
System.out.println(</span>"Thread2 running ..."<span style="color: #000000;">);
syncMethod.syncMethod1();
}
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span> main(String[] args) <span style="color: #0000ff;">throws</span><span style="color: #000000;"> InterruptedException {
SyncMethod syncMethod </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> SyncMethod();
Thread1 thread1 </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> Thread1(syncMethod);
Thread2 thread2 </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> Thread2(syncMethod);
thread1.start(); </span><span style="color: #008000;">//</span><span style="color: #008000;">先执行, 以便抢占锁</span>
Thread.sleep(500); <span style="color: #008000;">//</span><span style="color: #008000;">放弃cpu, 让thread1执行, 以便获的锁</span>
thread2.start(); //在syncMethod1()方法获得锁时, 看看syncMethod2()方法能否执行
<span style="color: #008000;">/*</span><span style="color: #008000;">
能否并发执行同一个对象不同的synchronized方法, 即看看能否同时进入一个对象synchronized方法块
1. 创建一个有两个synchronized方法的对象`syncMethod`
2. 先启动一个线程(Thread1), 并让其进入syncMethod对象的sychronized方法(syncMethod1)内, 并使其停在synchronized方法内
3. 再启动一个线程(Thread2), 并执行syncMethod对象的一个synchronized方法(syncMethod2), 看看能否进入此方法内
输出如下:
@@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 已经获取内置锁`SyncMethod.this`)
Thread2 running ...
@@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 即将释放内置锁`SyncMethod.this`)
######################## (syncMethod1, 已经获取内置锁`SyncMethod.this`, 并即将退出)
结果分析:
<strong><span style="color: #ff0000;">观察显示, 在输出`Thread2 running ...`后会暂停数秒(Thread2无法获得锁而被挂起, 因为锁已经被Thread1持有).</span></strong>
如果不同线程可以同时访问同一个对象不同synchronized方法的话,
在有足够cpu时间片时(Thread1在调用syncMethod1时使用Thread.sleep放弃了cpu),
Thread2调用的syncMethod2方法应该马上执行, 也就是syncMethod2方法中的语句在`Thread2 running ...`语句输出后马上输出,
而不是等待数秒才输出 (应为此时没有其他线程跟Thread2竞争cpu, 况且现在的电脑都不只一个cpu),
因此得出结论: "不同线程不能同时执行一个对象的不同synchronized方法"
其实此结论是显而易见的, 原理前面已经阐明, 此处不再赘述.
</span><span style="color: #008000;">*/</span><span style="color: #000000;">
}
}</span></pre>
下面是一些关于使用锁的一些建议:
为了避免对锁的竞争, 你可以使用锁分解,锁分段以及减少线程持有锁的时间, 如果上诉程序中的syncMethod1和syncMethod2方法是两个不相干的方法(请求的资源不存在关系), 那么这两个方法可以分别使用两个不同的锁, 改造后的SyncMethod类如下:
public class SyncMethod {
private Object lock1 = new Object();
private Object lock2 = new Object();
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> syncMethod2() {
</span><span style="color: #0000ff;">synchronized</span><span style="color: #000000;"> (lock1) {
</span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
System.out.println(</span>"@@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 已经获取内置锁`SyncMethod.this`)"<span style="color: #000000;">);
Thread.sleep(</span>5000<span style="color: #000000;">);
} </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
e.printStackTrace();
}
System.out.println(</span>"@@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 即将释放内置锁`SyncMethod.this`)"<span style="color: #000000;">);
}
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> syncMethod1() {
</span><span style="color: #0000ff;">synchronized</span><span style="color: #000000;"> (lock2) {
System.out.println(</span>"######################## (syncMethod1, 已经获取内置锁`SyncMethod.this`, 并即将退出)"<span style="color: #000000;">);
}
}
}
改造之后Thread1和Thread2就不会发生, 由于锁竞争而挂起的情况了.
当然, 如果syncMethod1中耗时操作与锁定的资源无关, 那么也可以将耗时操作移出同步块. 在上述改造的基础上对syncMethod1的进一步改造如下:
public void syncMethod2() {
synchronized (lock1) {
System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 已经获取内置锁`SyncMethod.this`)");
System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 即将释放内置锁`SyncMethod.this`)");
}
</span><span style="color: #008000;">//</span><span style="color: #008000;">将耗时操作移出同步块</span>
<span style="color: #0000ff;">try</span><span style="color: #000000;"> {
Thread.sleep(</span>5000);<span style="color: #008000;">//</span><span style="color: #008000;">与同步使用的资源无关的耗时操作</span>
} <span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
e.printStackTrace();
}
}
转载:https://www.cnblogs.com/Jackie-zhang/p/10384136.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)