如果有多个线程访问共享资源,可能会出现当一个线程没有处理完业务,然后另一个线程进入,从而导致共享资源出现不安全的情况。

日常例子:银行取钱,A和B有拥有同一个银行账户,A用存折在柜台取钱,B在取款机取钱。取钱有两个关键步骤:

    (1)判断账户里的钱的余额是否大于所取钱数

    (2)如果大于所取钱数,则账户最终所剩余额 = 余额 - 所取钱数。

如果没有线程同步的情况下,我们假设这一种情况,这个共同的账户里共1000元。

    (1)A  B同时去取600元,A所在线程执行到上面的第一个步骤,判断所取钱数小于现有余额,CPU时间片用完。

    (2)这时B进来到第一个步骤,同样是执行判断,因为A只执行完第一步骤,没有执行减法,这时现有余额还是1000元。

    (3)由于在CPU分配的时间里他接着完成了减法操作。这时账户余额为1000 - 600 = 400。成功取出600元。

    (4)最后A接着之前执行的步骤,去做减法操作, 账户余额为 -200 = 400 - 600。

到这里,我只想说为什么,是什么银行可以允许你这么做, 当然,除非银行是你家开的。

总之银行不可能让这种情况发生,所以我们的伟大先贤们就想到线程同步,其实很简单,你也能想到。如果让这两个步骤同时完成,不可分开,问题也就迎刃而解。

下面就说到在JAVA中同步代码的实现:

  涉及概念:同步监视器,是一个普通的java对象,同一个同步监视器如果一个线程拿到,则其他线程就没有办法拿到。好像是一个房门里只有唯一的一把钥匙, 不能复制。如果一个人拿着它进入房门,其他人只能在外面等候。等他出来你获得了它,你才能进入房间。

下面的代码如果没有做线程同步操作(同步代码块、同步方法、同步锁)结果是如下:

  Thread-1------判断所取钱数是否大于余额------
  Thread-0------判断所取钱数是否大于余额------
  Thread-0======做减法操作,取出现金======
  Thread-1======做减法操作,取出现金======

很显然线程1的那两步没有同时完成。

下面的几种方法可以实现两步同时完成。

1、同步代码块:

public class ThreadTest {

  public static void main(String[] args){
    Thread t1 = new Thread1(); //线程1
    Thread t2 = new Thread1();//线程2
    t1.start();
    t2.start();
  }
}

class Thread1 extends Thread{

  @Override
  public void run() {
    super.run();
    try {

      BeTested b = new BeTested(); // 这地方,因为这个例子中同步监视器 obj 是线程共享的,两个线程用两个不同的对象,也没有关系,不影响结果。
      b.beTested(this);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

class BeTested {
  static Object obj = new Object();;
  public void beTested(Thread t) throws InterruptedException{
    synchronized (obj) { //  obj 为同步监视器
      System.out.println(t.getName() + "------判断所取钱数是否大于余额------");
      t.sleep(1000); //  如果没有同步这样能理明显地看到这两步骤不能在一个线程,同一个时间片里执行完成。
      System.out.println(t.getName() + "======做减法操作,取出现金======");
    }
  }
}

执行结果如下:

Thread-0------判断所取钱数是否大于余额------
Thread-0======做减法操作,取出现金======
Thread-1------判断所取钱数是否大于余额------
Thread-1======做减法操作,取出现金======

注意:同步监视器对象的选用很关键。要选择线程共享的对象,比如上面例子的 obj, 它是static修饰的才行,如果没有static修饰,则是使用不同的同步监视器(不是同一个对象),相当于是两把钥匙。

  (如果obj = "aaaa" 没有static修饰也可以实现同步,那是因为这个obj引用的常量池里的同一个string对象,强烈不推荐使用)

2、同步方法(非静态方法)

把上面的那两类改成如下,main方法所在类不变。

class Thread1 extends Thread{

  static BeTested b = new BeTested();  // 在这种方法中,这里必须是同个对象(static修饰),下文会详细说明
  @Override
  public void run() {
    super.run();
    try {
      b.beTested(this);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

class BeTested {
  static Object obj = new Object();;
  public synchronized void beTested(Thread t) throws InterruptedException{
      System.out.println(t.getName() + "------判断所取钱数是否大于余额------");
      t.sleep(1000); 
      System.out.println(t.getName() + "======做减法操作,取出现金======");
  }
}

执行结果如下:

Thread-0------判断所取钱数是否大于余额------
Thread-0======做减法操作,取出现金======
Thread-1------判断所取钱数是否大于余额------
Thread-1======做减法操作,取出现金======

注意:因为同步方法中,所用的同步监视器不能指定,默认使用的调用该方法的对象,也就是this。所以 Thread1 类中相对于示例1中同步代码块中修改的部分, 也是要static修饰。也就是说要使用同一个对象。

3、同步方法(静态方法)

把上面的那两类改成如下,main方法所在类不变。

class Thread1 extends Thread{

  @Override
  public void run() {
    super.run();
    try {
      BeTested b = new BeTested(); // 这里每个线程使用不同的对象。
      b.beTested(this);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

class BeTested {
  static Object obj = new Object();;
  public static synchronized void beTested(Thread t) throws InterruptedException{
      System.out.println(t.getName() + "------判断所取钱数是否大于余额------");
      t.sleep(1000); 
      System.out.println(t.getName() + "======做减法操作,取出现金======");
  }
}

执行结果如下:

Thread-0------判断所取钱数是否大于余额------
Thread-0======做减法操作,取出现金======
Thread-1------判断所取钱数是否大于余额------
Thread-1======做减法操作,取出现金======

注意:因为同步静态方法中,同步监视器是这个类而不是这个类的对象。所以Thread1 类中相对于示例2中同步代码块中修改的部分,不须要用static修饰,不是同一个对象也没关系。因为这个类他本身就是共享的。

总结:如上几种方式进行线程同步处理时,要注意你所使用的同步监视器对象,它必须是共享的。

注:还有使用同步锁的方式实现线程同步,本篇文章不做讨论。

转载于:https://www.cnblogs.com/Adamo/p/7420961.html

同步监视器之同步代码块、同步方法相关推荐

  1. java同步方法完成案例_Java同步代码块和同步方法原理与应用案例详解

    本文实例讲述了java同步代码块和同步方法.分享给大家供大家参考,具体如下: 一 点睛 所谓原子性WOmoad:一段代码要么执行,要么不执行,不存在执行一部分被中断的情况.言外之意是这段代码就像原子一 ...

  2. java 同步块原理_Java同步代码块和同步方法原理与应用案例详解

    Java同步代码块和同步方法原理与应用案例详解 发布于 2020-8-7| 复制链接 摘记: 本文实例讲述了Java同步代码块和同步方法.分享给大家供大家参考,具体如下:一 点睛所谓原子性:一段代码要 ...

  3. 6※、线程同步、同步锁、同步代码块的使用、同步锁释放的时机、ReentrantLock可重入锁、公平锁与非公平锁的区别、什么是死锁、线程间的通信(生产者和消费者模式)

    线程锁 1.※线程的同步:(要确保对象锁是一致的) 1.未使用同步锁的抢票 2.使用了同步锁的抢票 3.线程-同步代码块的使用 4.同步方法和代码块的区别 5.同步锁释放的时机 练习:多线程生产手机 ...

  4. 设计模式之单例模式8种实现方式,其五:懒汉式(线程不安全,同步代码块)

    实现方式: 1.构造方法私有化 2.创建类的静态变量,不实例化 3.向外部暴露一个静态的公共方法,并判断是否存在实例,如果不存在,则实例化 4.在公共方法的内部生成实例的代码块加上synchroniz ...

  5. 同步方法和同步代码块

    2019独角兽企业重金招聘Python工程师标准>>> 打个比方:一个object就像一个大房子,大门永远打开.房子里有很多房间(也就是方法).这些房间有上锁的(synchroniz ...

  6. 从操作系统的PV操作理解JAVA的synchronized同步方法,同步代码块实现,及比较

    也有利于理解操作系统的同步 知识果然是息息相关的 JAVA同样离不开操作系统的原理,不过他体现在虚拟机JVM中 synchronized关键字是同步关键字 首先我们知道在操作系统里 PV两者操作信息量 ...

  7. java锁方法和锁代码块_java的同步方法和同步代码块,对象锁,类锁区别

    /** * @author admin * @date 2018/1/12 9:48 * 做用在同一个实例对象上讨论 * synchronized同步方法的测试 * 两个线程,一个线程调用synchr ...

  8. 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁

    什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...

  9. java中synchronized(同步代码块和同步方法)详解及区别

    问题的由来: 看到这样一个面试题: ? 1 2 3 4 5 6 //下列两个方法有什么区别 public synchronized void method1(){} public void metho ...

最新文章

  1. 【Linux】Ubuntu的一些高(sao)效(cao)率(zuo)工具
  2. 宏基因组扩增子1图表解读-理解文章思路,零基础测序分析图表解读大全(箱线,散点,热,曼哈顿,火山,韦恩,三元,网络),老板再也不愁我的文献阅读了!
  3. 自建MySQL5.6数据库查询优化
  4. 【IDEA】干掉注释自动在行首
  5. c语言跑马灯实验报告,单片机跑马灯实验
  6. Aligning Plots in a Column作图列对齐
  7. 对java这门课程的认识_关于java课程的总结
  8. SpringCloud 入门教程(一): 服务注册
  9. 用java写分段函数_使用Java将分段函数转换为CSV文件
  10. 虚拟机fedora共享_开源虚拟现实,用于电子测试的新电路板,Fedora 25,以及更多新闻
  11. c语言中3%3e2%3e1的值,C语言--ch2--数据类型和表达式.ppt
  12. 如何使用putExtra()和getExtra()来表示字符串数据
  13. java 实现根据ip重定向_从0到1用java再造tcpip协议栈:代码实现ping应用功能1
  14. 【现代机器人学】基于指数积的机械臂逆运动学
  15. SQL Server 2005 分页SQL
  16. 解决sql2005远程连接报错,提示请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接
  17. 数字电视复用器中的PCR矫正技术
  18. paypal html5 支付,PayPal H5支付组件
  19. 两个三维向量叉积_三维向量叉乘推导
  20. k8s集群Canal的网络控制

热门文章

  1. 小度智能音响拆解 芯片_不拆不快:小度音箱拆解测评
  2. xp可以装java6不_XP系统XMind 6中缺失安装java环境
  3. html制作搜狗主页,自学htmlcss之仿搜狗主页(示例代码)
  4. 路由器性能测试工具_小米路由器AX3600与AX1800 MESH 实测
  5. java嵌套类中的方法怎么调用_java类与嵌套嵌套后,怎么使用最外层的类建立对象后使用内部类的方法?...
  6. 一个java实现的简单日历,采用左树右列表的方式实现,具有参考意义
  7. Python案例:求转置矩阵
  8. MyBatis框架笔记03:MyBatis实现CRUD
  9. 【codevs1368】【BZOJ1034】泡泡堂BNB,贪心思路
  10. mysql中的lgwr_MySQL Replication和Oracle logical standby的原理对比