浅谈同步监视器之同步代码块、同步方法

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

日常例子:银行取钱,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修饰,不是同一个对象也没关系。因为这个类他本身就是共享的。

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

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

以上这篇浅谈同步监视器之同步代码块、同步方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

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

     java中synchronized(同步代码块和同步方法)详解及区别 问题的由来: 看到这样一个面试题: //下列两个方法有什么区别 public synchronized void method1(){} public void method2(){ synchronized (obj){} } synchronized用于解决同步问题,当有多条线程同时访问共享数据时,如果进行同步,就会发生错误,Java提供的解决方案是:只要将操作共享数据的语句在某一时段让一个线程执行完,在执行过程中,其他

  • java 中同步方法和同步代码块的区别详解

    java 中同步方法和同步代码块的区别详解 在Java语言中,每一个对象有一把锁.线程可以使用synchronized关键字来获取对象上的锁.synchronized关键字可应用在方法级别(粗粒度锁)或者是代码块级别(细粒度锁). 问题的由来: 看到这样一个面试题: //下列两个方法有什么区别 public synchronized void method1(){} public void method2(){ synchronized (obj){} } synchronized用于解决同步问

  • java多线程-同步块实例讲解

    java多线程-同步块 Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java 同步块用来避免竞争.本文介绍以下内容: Java 同步关键字(synchronzied) 实例方法同步 静态方法同步 实例方法中同步块 静态方法中同步块 Java 同步示例 Java 同步关键字(synchronized) Java 中的同步块用 synchronized 标记.同步块在 Java 中是同步在某个对象上.所有同步在一个对象上的同步块在同时只能被一个线程进入并执

  • 浅谈同步监视器之同步代码块、同步方法

    如果有多个线程访问共享资源,可能会出现当一个线程没有处理完业务,然后另一个线程进入,从而导致共享资源出现不安全的情况. 日常例子:银行取钱,A和B有拥有同一个银行账户,A用存折在柜台取钱,B在取款机取钱.取钱有两个关键步骤: (1)判断账户里的钱的余额是否大于所取钱数 (2)如果大于所取钱数,则账户最终所剩余额 = 余额 - 所取钱数. 如果没有线程同步的情况下,我们假设这一种情况,这个共同的账户里共1000元. (1)A B同时去取600元,A所在线程执行到上面的第一个步骤,判断所取钱数小于现

  • 浅谈Python线程的同步互斥与死锁

    线程间通信方法 1. 通信方法 线程间使用全局变量进行通信     2. 共享资源争夺 共享资源:多个进程或者线程都可以操作的资源称为共享资源.对共享资源的操作代码段称为临界区. 影响 : 对共享资源的无序操作可能会带来数据的混乱,或者操作错误.此时往往需要同步互斥机制协调操作顺序.     3. 同步互斥机制 同步 : 同步是一种协作关系,为完成操作,多进程或者线程间形成一种协调,按照必要的步骤有序执行操作.两个或两个以上的进程或线程在运行过程中协同步调,按预定的先后次序运行.比如 A 任务的

  • 浅谈Java非阻塞同步机制和CAS

    什么是非阻塞同步 非阻塞同步的意思是多个线程在竞争相同的数据时候不会发生阻塞,从而能够在更加细粒度的维度上进行协调,从而极大的减少线程调度的开销,从而提升效率.非阻塞算法不存在锁的机制也就不存在死锁的问题. 在基于锁的算法中,如果一个线程持有了锁,那么其他的线程将无法进行下去.使用锁虽然可以保证对资源的一致性访问,但是在挂起和恢复线程的执行过程中存在非常大的开销,如果锁上面存在着大量的竞争,那么有可能调度开销比实际工作开销还要高. 悲观锁和乐观锁 我们知道独占锁是一个悲观锁,悲观锁的意思就是假设

  • 浅谈PHP拦截器之__set()与__get()的理解与使用方法

    "一般来说,总是把类的属性定义为private,这更符合现实的逻辑. 但是,对属性的读取和赋值操作是非常频繁的,因此在PHP5中,预定义了两个函数"__get()"和"__set()"来获取和赋值其属性,以及检查属性的"__isset()"和删除属性的方法"__unset()". 我们为每个属性做了设置和获取的方法,在PHP5中给我们提供了专门为属性设置值和获取值的方法,"__set()"和&qu

  • 浅谈go语言renderer包代码分析

    renderer是Go语言的一个简单的.轻量的.快速响应的呈现包,它可以支持JSON.JSONP.XML.HYAML.HTML.File等类型的响应.在开发web应用或RESTFul API的时候,这个包是非常方便的toolkit. 本文绕开如何使用它,深入到代码实现中研究它,同时也尝尝Go语言包的开发套路. Go包基础介绍 代码结构 package pkgname import ( "fmt" ... ) const ( CONST1 typeX = xx ... ) var ( V

  • Java同步代码块和同步方法原理与应用案例详解

    本文实例讲述了Java同步代码块和同步方法.分享给大家供大家参考,具体如下: 一 点睛 所谓原子性:一段代码要么执行,要么不执行,不存在执行一部分被中断的情况.言外之意是这段代码就像原子一样,不可拆分. 同步的含义:多线程在代码执行的关键点上,互通消息,相互协作,共同把任务正确的完成. 同步代码块语法: synchronized(对象) { 需要同步的代码块; } 同步方法语法: 访问控制符 synchronized 返回值类型方法名称(参数) { 需要同步的代码; } 二 同步代码块完成卖票功

  • Java 多线程的同步代码块详解

    目录 synchronized 同步代码块 同步方法(this锁) 静态同步方法 死锁问题 lock 总结 火车站抢票问题 由于现实中买票也不会是零延迟的,为了真实性加入了延迟机制,也就是线程休眠语句 package test.MyThread.ticketDemo; public class RunnableThread implements Runnable{ private int ticket = 100; @Override public void run(){ while(true)

  • 浅谈Java多线程实现及同步互斥通讯

    Java多线程深入理解本文主要从三个方面了解和掌握多线程: 1. 多线程的实现方式,通过继承Thread类和通过实现Runnable接口的方式以及异同点. 2. 多线程的同步与互斥中synchronized的使用方法. 3. 多线程的通讯中的notify(),notifyAll(),及wait(),的使用方法,以及简单的生成者和消费者的代码实现. 下面来具体的讲解Java中的多线程: 一:多线程的实现方式 通过继承Threa类来实现多线程主要分为以下三步: 第一步:继承 Thread,实现Thr

  • 浅谈js文件引用方式及其同步执行与异步执行

    任何以appendChild(scriptNode) 的方式引入的js文件都是异步执行的 (scriptNode 需要插入document中,只创建节点和设置 src 是不会加载 js 文件的,这跟 img 的与加载不同 ) html文件中的<script>标签中的代码或src引用的js文件中的代码是同步加载和执行的 html文件中的<script>标签中的代码使用document.write()方式引入的js文件是异步执行的 html文件中的<script>标签src

  • 浅谈js的ajax的异步和同步请求的问题

    先来看以下代码: var flag=true; var index=0; $.ajax({ url: "http://www.jb51.net/", success: function(data){ flag=false; } }); while(flag){ index++; } alert(index); 请问最后alert的index的结果是多少? 可能有人会说0呗.实际上却没那么简单.大家可以自己试试看.可以看到最终程序进入了一个死循环!怎么会这样呢! 我们在看一段代码: va

随机推荐