为了解决“非线程安全”带来的问题,上一节中使用的办法是用关键字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;}@Overridepublic 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;}@Overridepublic void run() {// TODO Auto-generated method stubsuper.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;}@Overridepublic void run() {super.run();task.doLongTimeTask();}}public static class ThreadB extends Thread {private Task task;public ThreadB(Task task) {super();this.task = task;}@Overridepublic 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;}@Overridepublic void run() {super.run();service.serviceMethodA();}}public static class ThreadB extends Thread {private MyService service;public ThreadB(MyService service) {super();this.service = service;}@Overridepublic 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的访问将被阻塞。所以“对象监视器”是同一个。

转载于:https://www.cnblogs.com/chentong/p/5654112.html

Java多线程之synchronized(二)相关推荐

  1. Java多线程之Synchronized和Lock的区别

    Java多线程之Synchronized和Lock的区别 目录: 原始构成 使用方法 等待是否可以中断 加锁是否公平 锁绑定多个条件Condition 小结:Lock相比较Synchronized的优 ...

  2. java synchronized 卖票_(二)java多线程之synchronized

    引言 现在,让我们来考虑一个问题,如果要让多个线程来访问同一份数据,会发生什么现象呢?比如12306的火车售票系统,比如银行的存取款系统等等.都可以会出现多线程访问同一个数据的情况.让我们先模拟写一个 ...

  3. JAVA多线程之Synchronized、wait、notify实例讲解

    一.Synchronized synchronized中文解释是同步,那么什么是同步呢,解释就是程序中用于控制不同线程间操作发生相对顺序的机制,通俗来讲就是2点,第一要有多线程,第二当多个线程同时竞争 ...

  4. Java多线程之Synchronized深入理解

    文章目录 1 Synchronized 1.1 引言 1.2 概念理解 1.2.1 不同锁对象 1.2.2 对象锁和类锁概念区别 1.2.3 同步概念monitorenter&monitore ...

  5. Java多线程之Synchronized详解

    一直以来对于Synchronized都比较迷惑,尤其还对于ReentrantLock并不了解他们之间的区别,今天闲来无事,学习了. 1,为什么要使用Synchronized 首先看Synchroniz ...

  6. java多线程之synchronized与lock、wait与notify

    一.synchronized与lock synchronized在java很早的版本就已经有了,它的作用只要是同步代码,解决线程的安全问题,但是随着java的发展,以及开发业务的不断提高,synchr ...

  7. JAVA多线程之wait/notify

    本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法. ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 ②wait ...

  8. Java多线程之volatile详解

    Java多线程之volatile详解 目录: 什么是volatile? JMM内存模型之可见性 volatile三大特性之一:保证可见性 volatile三大特性之二:不保证原子性 volatile三 ...

  9. Java多线程之Callable、Future和FutureTask

    Java多线程之Callable接口 自己想总结一下的,看到一篇总结的更好的博客,就转载了,突然感觉真轻松,哈哈哈哈 文章转载于:Matrix海子:Java并发编程:Callable.Future和F ...

最新文章

  1. websphere mq 查看队列中是否有数据_全网最全的 “消息队列”
  2. 机房重构(个人版)——类图
  3. 用html写消费记录页面,支付流水记录.html
  4. WPF与缓动(一) N次缓动
  5. html任务清单源码,JavaScript jQuery 任务清单 ToDoList
  6. qunee for html5 api,Qunee for HTML5
  7. 批处理命令set截取字符详解
  8. c++ udp多线程 例子_[内附完整源码和文档] 基于udp实现tcp功能进行大文件传输
  9. linux-vim设置环境
  10. 建自己的小屋真辛苦啊?!·##¥
  11. 经典参考书:《编程之美——微软技术面试心得》
  12. ktv收银管理系统服务器,KTV包厢收银管理系统增强版
  13. Android开机优化之调整Launcher的加载时间
  14. 解决电脑启动蓝屏出现Recovery—错误代码0xc000014
  15. 1、spring之Resource加载
  16. 现货黄金规则如此简单吗?
  17. 苹果 WWDC 2019 全记录:iPadOS独立、SwiftUI、Project Catalyst
  18. 【跨境电商】5个最佳WordPress插件推荐
  19. HDU-2550-百步穿杨
  20. ES千亿级搜索实战-架构优化

热门文章

  1. k8s安装之服务器基础环境配置
  2. mybatis-plus自定义配置方式
  3. Redis的两种持久化机制RDB和AOF
  4. Hadoop大数据--Mapreduce编程规范及入门示例
  5. jQuery EasyUI 折叠面板accordion的使用实例
  6. acme.sh签发Let‘s Encrypt证书
  7. linux查看端口和kill端口
  8. python(numpy,pandas3)——numpy索引
  9. c语言mktime,在C语言中转换时间的基本方法介绍
  10. 学计算机广东2B大学,2017年广东2B大学最新排名情况