Java多线程之synchronized(二)
为了解决“非线程安全”带来的问题,上一节中使用的办法是用关键字synchronized修饰多个线程可能同时访问到的方法,但是这样写是存在一定的弊端的,比如线程A调用一个用synchronized修饰的同步方法,这个方法要执行很长时间,那么其它的线程必须无条件的等线程A执行完释放掉对象锁,当然前提是其他的线程也要访问这个同步方法。这种情况就可以用synchronized代码块来解决。在解决之前我先附上一段没优化之前的方法,这样就可以直观的看到效果差异。
证明synchronized方法的弊端,代码如下:
public class Entity {
public static long beginTime1;
public static long endTime1;
public static long beginTime2;
public static long endTime2;
}
public class Task_Synchronized { private String getData1;
private String getData2;
//两个子线程要调用的公共方法
public synchronized void doLongTimeTask() { try {
System.out.println("begin task");
Thread.sleep(3000);
//getData1和getdata2实际过程中可以是两个非常耗时的操作,这样看起来效果更名
getData1 = "长时间处理任务后从远程返回的值1 threadName="
+ Thread.currentThread().getName();
getData2 = "长时间处理任务后从远程返回的值2 threadName="
+ Thread.currentThread().getName();
System.out.println(getData1);
System.out.println(getData2);
System.out.println("end task"); } catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException { Task_Synchronized task = new Task_Synchronized();
MyThread1 t1 = new MyThread1(task);
t1.start();
MyThread2 t2 = new MyThread2(task);
t2.start();
Thread.sleep(10000);
// 因为线程1和线程2哪个先执行不一定,所以比较了一下时间,开始的时间取比较小的值,结束的时间取较大的值
long beginTime = Entity.beginTime1;
if (Entity.beginTime2 < Entity.beginTime1) {
beginTime = Entity.beginTime2;
}
long endTime = Entity.endTime1;
if (Entity.endTime2 > Entity.endTime1) {
endTime = Entity.endTime2;
}
System.out.println("耗时" + (endTime - beginTime) / 1000 + "s");
}
//第一个线程
public static class MyThread1 extends Thread { private Task_Synchronized task; public MyThread1(Task_Synchronized task) {
super();
this.task = task;
} @Override
public void run() {
super.run();
Entity.beginTime1 = System.currentTimeMillis();
task.doLongTimeTask();
Entity.endTime1 = System.currentTimeMillis();
}
}
//第二个线程
public static class MyThread2 extends Thread { private Task_Synchronized task; public MyThread2(Task_Synchronized task) {
super();
this.task = task;
} @Override
public void run() {
// TODO Auto-generated method stub
super.run();
Entity.beginTime2 = System.currentTimeMillis();
task.doLongTimeTask();
Entity.endTime2 = System.currentTimeMillis();
}
}
运行结果如下:从Task_Synchronized 类可以看出,synchronized修饰的是doLongTimeTask()方法,从执行结果也可以看出syschronized修饰方法的执行顺序是这样的:Thread—0必须把同步的方法全部执行完,释放掉对象锁之后,Thread—1才可以执行,也就是说无论哪个线程先抢上CPU,就要执行到底之后,另一个线程才可以上CPU执行,这样执行下来用时间是6s。
那么接下来看看用synchronized代码块怎么解决这个弊端,我写了一个例子,如下:
public class Task_Synchronized { private String getData1;
private String getData2;
//没有synchronized修饰
public void doLongTimeTask() { try {
System.out.println("begin task");
Thread.sleep(3000);
String privateGetData1 = "长时间处理的任务1 threadName="
+ Thread.currentThread().getName();
String privateGetData2 = "长时间处理的任务1 threadName="
+ Thread.currentThread().getName();
//synchronized代码块
synchronized (this) {
getData1 = privateGetData1;
getData2 = privateGetData2;
}
System.out.println(getData1);
System.out.println(getData2);
System.out.println("end task"); } catch (InterruptedException e) { e.printStackTrace();
}
}
}
执行结果如下:只需要修改一下doLongTimeTask()这个方法即可,用synchronized代码块来代替synchronized修饰方法,从运行结果可以看到时间只用3s,时间缩短了是由于线程Thread—0访问Task_Synchronized类的同步代码块时,线程Thread—1仍然可以访问Task_Synchronized类里的非同步方法。在这里为什么会想到只同步变量getData1和getData2呢,我以前说过出现“非线程安全”的原因,其中有一个原因就是有多个线程同时访问成员变量时可能会出现“脏读”现象。
但是还有两个问题需要验证,那就是syschronized代码块里的内容真的是同步执行的吗?这个this真的代表当前类的对象锁吗?下面我写了一个例子来验证一下,如下:
public static void main(String[] args) { Task task = new Task();
ThreadA a = new ThreadA(task);
a.setName("A");
ThreadB b = new ThreadB(task);
b.setName("B");
a.start();
b.start(); } public static class ThreadA extends Thread { private Task task; public ThreadA(Task task) {
super();
this.task = task;
} @Override
public void run() { super.run();
task.doLongTimeTask();
}
} public static class ThreadB extends Thread { private Task task; public ThreadB(Task task) {
super();
this.task = task;
} @Override
public void run() { super.run();
task.doLongTimeTask();
}
}
} class Task { public void doLongTimeTask() { for (int i = 0; i < 100; i++) {
System.out.println("noSynchronized threadName="
+ Thread.currentThread().getName() + " i=" + (i + 1));
}
System.out.println("*********************************");
synchronized (this) {
for (int i = 0; i < 100; i++) {
System.out.println("Synchronized threadName="
+ Thread.currentThread().getName() + " i=" + (i + 1));
}
}
}
运行结果如下:由图(a)可以看出来,两个线程在执行第一for循环的时候,是不同步交叉执行的,由图b1、b2、c1、c2可以看出来线程A排队执行完,线程B才开始执行,所以synchronized代码块是同步的。
图(a) 图(b1) 图(b2)
图(c1) 图(c2)
那么怎么验证synchronized使用的“对象监视器”是一个呢,也就是this代表的是当前类的对象锁。需要证明只有一个线程释放掉当前对象锁,其它的线程才可以执行。我写了一个例子,如下:
public static void main(String[] args) { MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.setName("A");
ThreadB b = new ThreadB(service);
b.setName("B");
a.start();
b.start();
} public static class ThreadA extends Thread { private MyService service; public ThreadA(MyService service) {
super();
this.service = service;
} @Override
public void run() { super.run();
service.serviceMethodA(); }
} public static class ThreadB extends Thread { private MyService service; public ThreadB(MyService service) {
super();
this.service = service;
} @Override
public void run() { super.run();
service.serviceMethodB();
}
}
} class MyService { public void serviceMethodA() {
synchronized (this) { try {
System.out.println("A begin time=" + System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("A end time=" + System.currentTimeMillis());
} catch (InterruptedException e) { e.printStackTrace();
}
}
} public void serviceMethodB() {
synchronized (this) { try {
System.out.println("B begin time=" + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("B end time=" + System.currentTimeMillis());
} catch (InterruptedException e) { e.printStackTrace();
}
}
}
运行结果如下:从代码上可以看出,两个synchronized代码块里都有休眠方法,但是并没有影响线程的执行顺序,如果两个this不是同一把对象锁,那么在休眠的这段时间,线程肯定会出现交替执行的,从结果也可以看出来,线程A执行完之后,线程B才开始执行的,说明当线程A访问MyService的同步代码块serviceMethodA的时候,线程B对同步代码块serviceMethodB的访问将被阻塞。所以“对象监视器”是同一个。
Java多线程之synchronized(二)的更多相关文章
- (二)java多线程之synchronized
本人邮箱: kco1989@qq.com 欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco github: https://github.com/kco198 ...
- JAVA多线程之Synchronized关键字--对象锁的特点
一,介绍 本文介绍JAVA多线程中的synchronized关键字作为对象锁的一些知识点. 所谓对象锁,就是就是synchronized 给某个对象 加锁.关于 对象锁 可参考:这篇文章 二,分析 s ...
- JAVA多线程之Synchronized、wait、notify实例讲解
一.Synchronized synchronized中文解释是同步,那么什么是同步呢,解释就是程序中用于控制不同线程间操作发生相对顺序的机制,通俗来讲就是2点,第一要有多线程,第二当多个线程同时竞争 ...
- java 多线程之synchronized wait/notify解决买票问题
一.Java线程具有五中基本状态 新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread(); 就绪状态(Runnable):当调用线程对象的st ...
- java多线程之synchronized(线程同步)
一.线程同步,主要应用synchronized关键字: public class TraditionalThreadSynchorinzed { public static void main(Str ...
- Java多线程之synchronized(四)
前面几章都是在说synchronized用于对象锁,无论是修饰方法也好修饰代码块也好,然而关键字synchronized还可以应用到static静态方法上,如果这样写,那就是对当前的*.java文件所 ...
- Java多线程之synchronized(三)
在多线程访问同一个对象中的不同的synchronized方法或synchronized代码块的前提下,也就是“对象监控器”为同一个对象的时候,也就是synchronized的锁为同一把锁的时候,调用的 ...
- Java多线程之synchronized及其优化
Synchronized和同步阻塞synchronized是jvm提供的同步和锁机制,与之对应的是jdk层面的J.U.C提供的基于AbstractQueuedSynchronizer的并发组件.syn ...
- JAVA多线程之synchronized和volatile实例讲解
在多线程中,提到线程安全.线程同步,我们经常会想到两个关键字:volatile和synchronized,那么这两者有什么区别呢? 1. volatile修饰的变量具有可见性 volatile是变量修 ...
随机推荐
- TCP/IP笔记 三.运输层(2)——TCP 流量控制与拥塞控制
TCP 的流量控制与拥塞控制可以说是一体的.流量控制是通过滑动窗口实现的,拥塞避免主要包含以下2个内容: (1)慢开始,拥塞避免 (2)快重传,快恢复 1.流量控制——滑动窗口 TCP采用大小可变的滑 ...
- Socket编程指南及示例程序
例子代码就在我的博客中,包括六个UDP和TCP发送接受的cpp文件,一个基于MFC的局域网聊天小工具工程,和此小工具的所有运行时库.资源和执行程序.代码的压缩包位置是http://www.blogja ...
- 国产编程语言R++ V1.5发布
R++ v1.5内核改动较大,下面是一些主要变化: 1.使用PJIT(Pseudocode Just-In-Time),编译速度大幅提高,但运行效率远远不如C++,不过R++将在下一版本支持RJIT( ...
- UIView 中 frame, bounds, center 属性的关系
最近一直在学 iOS 开发,所以专门创建了这样一个类别,将自己学习中的一些问题整理,记录下来.由于自己是初学者,所以所写的文章非常基础,写这个类别一是为了给自己留下存 档,二是为了给和我有同样问题的初 ...
- poj 2480 (欧拉函数应用)
点击打开链接 //求SUM(gcd(i,n), 1<=i<=n) /* g(n)=gcd(i,n),根据积性定义g(mn)=g(m)*g(n)(gcd(m,n)==1) 所以gcd(i,n ...
- linux进程之fork 和 exec函数
---恢复内容开始--- fork函数 该函数是unix中派生新进程的唯一方法. #include <unistd.h> pid_t fork(void); 返回: (调用它一次, 它 ...
- zoj 2376 Ants
#include<stdio.h> #include<stdlib.h> ]; int main(void) { int t,n,m,i,len,max,min,mx,mi; ...
- spring+hibernate基础
把数据库的配置信息写在一个文件中 jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc\:mysql\://localhost\:3306/ ...
- C#DB2开发问题随记
最近公司有个小工具需要用到DB2数据库,以前没玩过DB2,觉得应该很容易就实现了. 这个小工具最开始用了Nhibernate来连接DB2,Nhibernate也是第一次用..实在是惭愧啊... 第一次 ...
- Cookie管理
1,判断来访者是否第一次 public class VistorTest extends HttpServlet { @Override protected void doGet(HttpServle ...