必威-必威-欢迎您

必威,必威官网企业自成立以来,以策略先行,经营致胜,管理为本的商,业推广理念,一步一个脚印发展成为同类企业中经营范围最广,在行业内颇具影响力的企业。

实例或者多台计算机同时运行一段处理逻辑,大

2019-10-10 21:24 来源:未知

小伙伴们注意了!

如果你在大学学习过操作系统这门课,监视器是操作系统中做同步的重要概念(额,我真没听过,难道翻译过来的不一样)。它也用于Java同步。这个帖子用类比来解释“监视”的基本思想。

转自:

在Java中,与线程通信相关的几个方法,是定义在Object中的,大家都知道Object是Java中所有类的超类在Java中,所有的类都是Object,借助于一个统一的形式Object,显然在有些处理过程中可以更好地完成转换,传递,省去了一些不必要的麻烦另外有些东西,比如toString,的确是所有的类的特征但是,为何线程通信相关的方法会被设计在Object中?图片 1

多线程的目的

小编在这里给大家送上关注福利:

1.什么是监视器

监视器可以看做包含特殊事物的房间。这个特殊房间一次只能被一个消费者(线程)暂用。这个房间通常包含一些数据和代码。

图片 2

监视器房间

如果客户想要占用这个特殊房间,他必须进入走廊(hallWay)才能等到。调度器会根据特定的算法(比如先进先出)来选择一个。如果它被某种原因挂起了,他将会被送到等候室,以后按照调度进入这个特殊房间。如上图所示,这个建筑物有三个客房。

图片 3

访问特殊代码

简而言之,监视器是确保每次只有一个线程来访问特定的代码和数据的装置。

如果你在大学学习过操作系统,你可能还记得监听器在操作系统中是很重要的概念。同样监听器在java同步机制中也有使用,本文通过类比的方法来解释“监听器”的基本思想。

对于多线程编程模型,一个少不了的概念就是锁虽然叫做锁,但是其实相当于临界区大门的一个钥匙,那把钥匙就放到了临界区门口,有人进去了就把钥匙拿走揣在了身上,结束之后会把钥匙还回来只有拿到了指定临界区的锁,才能够进入临界区,访问临界区资源,当离开临界区时,释放锁,其他线程才能够进入临界区而对于锁本身,也是一种临界资源,是不允许多个线程共同持有的,同一时刻,只能够一个线程持有;在前面的章节中,比如信号量介绍中,对于PV操作,就是对临界区资源的访问,下面的S就是临界区资源 Wait和 signal操作可描述为: wait: while ; S:=S-1;

signal:S:=S+1; 但是上面的S,只是一种抽象的概念,在Java中如何表达? 换个问题就是:在Java中是如何描述锁这种临界区资源的? 其实任何一个对象都可以被当做锁 锁在Java中是对象头中的数据结构中的数据,在JVM中每个对象中都拥有这样的数据 如果任何线程想要访问该对象的实例变量,那么线程必须拥有该对象的锁(也就是在指定的内存区域中进行一些数据的写入) 当所有的其他线程想要访问该对象时,就必须要等到拥有该对象的锁的那个线程释放锁 一个线程拥有了一个对象的锁之后,他就可以再次获取锁,也就是平常说的可重入,如下图所示,两个方法同一个锁 假设methodA中调用了methodB,如果不可重入的话,一个线程获取了锁,进入methodA然后等待进入methodB的锁,但是他们是同一个锁 自己等待自己,岂不是死锁了?所以锁具有可重入的特性 图片 4 对于锁的可重入性,JVM会维护一个计数器,记录对象被加锁了多少次,没有被锁的对象是0,后续每重入一次,计数器加1(只有自己可以重入,别人是不可以,是互斥的) 只有计数器为0时,其他的线程才能够进入,所以,同一个线程加锁了多少次,也必然对应着释放多少次 而对于这些事情,计数器的维护,锁的获取与释放等,是JVM帮助我们解决的,开发人员不需要直接接触锁 简言之,在对象头中有一部分数据用于记录线程与对象的锁之间的关系,通过这个对象锁,进而可以控制线程对于对象的互斥访问

提到多线程就只有一个目录,更好的利用CPU资源,我们让CPU同时处理多个任务,缩短计算和处理时间。

转发本文,关注+私信小编“资料”即可领取小编精心准备的资料一份!

2.它是怎么在Java中实现的?

在Java虚拟机中,每个对象和类都与监视器相关联。为了实现监视器的互斥能力,锁(有时称为互斥)和每个对象和类相互关联。这在操作系统书中被称为信号量,互斥是为二维信号量。

如果一个线程拥有某些数据的锁定,那么其他线程将不能获得锁直到拥有该锁的线程释放它为止。如果我们在多线程编程中一直操作一个信号量将不方便。幸运的Java虚拟机自动为我们做了这些。

要声明一个监视区,意味着数据不能被多个线程访问,Java提供同步语句和同步方法。一旦代码嵌入了synchronized关键字,它就是一个监视器区域。这些锁由Java虚拟机自动实现。

什么是监听器?

监听器可以看成是包含了一间特殊房间的建筑,这间特殊的房间在同一个时间只能被一个客人(线程)拥有,通常这间房间包含了一些数据和代码。

图片 5

如果一个客人想拥有这间特殊的房间,他不得不首先在走廊(进入集)中等待着,然后调度器根据一些调度算法(eg:FIFO 先进先出)选择一个。如果这个客人因为某些原因暂停悬挂着,则会被放到等待房间`,同时也被安排稍后重新进入这个特殊的房间,就像上面图片所展示的那样,在这个建筑里有个3个房间。

 

图片 6

简而言之:一个监听器就是一个监听线程进入这间特殊房间的设施。它确保了只能有一个线程可以访问这些受到保护的数据和代码。

监视器

对于对象锁,可以做到互斥,但是仅仅互斥就足够了吗?比如一个同步方法以当前对象this为锁,如果多个线程过来,只有一个线程可以持有锁,其他线程需要等待 这个过程是如何管理的? 而且,在Java中,还可以借助于wait notify方法进行线程间的协作,这又是如何做到的? 其实在Java中还有另外一个概念,叫做监视器 《深入Java虚拟机》中如下描述监视器:
可以将监视器比作一个建筑,它有一个很特别的房间,房间里有一些数据,而且在同一时间只能被一个线程占据。一个线程从进入这个房间到它离开前,它可以独占地访问房间中的全部数据。如果用一些术语来定义这一系列动作:

  • 进入这个建筑叫做“进入监视器”
  • 进入建筑中的那个特别的房间叫作“获得监视器”
  • 占据房间叫做“持有监视器”
  • 离开房间叫做“释放监视器”
  • 离开建筑叫做“退出监视器”

图片 7这些概念说起来,稍微有些晦涩,换个角度还记得《上篇系列》中的管程的概念么?还记得管程的英文单词吗?图片 8其实Java中的监视器Monitor就是管程的概念,他是管程的一种实现不管实现细节如何,不管对概念的实现程度如何,它的核心其实就是管程在进程通信的部分有介绍到:“管程就是管理进程,管程的概念就是设计模式中“依赖倒置原则”,依赖倒置原则是软件设计的一个理念,IOC的概念就是依赖倒置原则的一个具体的设计管程将对共享资源的同步处理封闭在管程内,需要申请和释放资源的进程调用管程,这些进程不再需要自主维护同步。有了管程这个大管家进程的同步任务将会变得更加简单。管程是墙,过程是门,想要访问共享资源,必须通过管程的控制(通过城墙上的门,也就是经过管程的过程)而管程每次只准许一个进程进入管程,从而实现了进程互斥图片 9管程的核心理念就是相当于构造了一个管理进程同步的“IOC”容器。”**
简言之:Java的监视器就是管程的一种实现,借助于监视器可以实现线程的互斥与同步
**

在实现多线程之前,首先了解一个下多线程的一些概念:

如果您在大学中学习过操作系统课程,您可能记得monitor是操作系统中同步的一个重要概念。

3.在Java的同步代码中,哪部分是监视器?

我们知道任何一个类或对象都和监视器绑定。我认为每个对象都有个监视器是很好的,因为每个对象可以有自己的关键部分(能够见识线程序列)。

为了启用不同的线程协作,Java提供wait()和notify()来挂起一个线程,并分别唤醒另一个在此对象等待的线程。另外还有三个其他版本:

wait(long timeout, int nanos)
wait(long timeout) notified by other threads or notified by timeout. 
notify(all)

这些方法只能在synchronized语句或synchronized方法中调用。原因,如果一个方法不需要互斥,那么就不需要线程之间的监视或协作,每个线程都可以自由地访问他们。
下面是同步代码的示例:
Reference:1. Java Doc for Object

  1. Thread synchronization
  2. Locks and Synchronization
  3. notify() vs notifyAll()

在java中监听器是如何实现的?

在java虚拟机中,每个对象和类在逻辑上都和一个监听器相关联。为了实现监听器的共同执行能力,锁(有时候又叫互斥量)关联着每个对象和类,在操作系统书上被称之为“信号量”,互斥量其实就是一个二态的信号量。

如果一个线程拿到了相关数据的锁,那么其他线程不能再拥有这把锁直到拥有这把锁的线程释放了这把锁。当在多线程编程中,如果我们需要一直写一个信号量这种方式可能会不太方便,幸运的是我们没必要这么做,因为JVM(java虚拟机)已经自动帮我们做了。

声明一个监听器区域,这意味着数据不能被超过一个线程访问(译者注:同时访问的前提)。Java提供了同步的语句和同步的方法,一旦代码嵌入到同步关键字内,这就是一个监听器区域。而锁是由jvm在底层自动实现的。

监视区域

对于监视器“房间”内的内容被称为监视区域,说白了监视区域就是监视器掌管的空间区域**这个空间区域不管里面有多少内容,对于监视器来说,他们是最小单位,是原子的,是不可分割的代码,只会被同一个线程执行**不管你多少并发,监视器会对他进行保障(对于开发者来说,你使用一个synchronized关键字就有了监视器的效果,监视器依赖JVM,而JVM依赖操作系统,操作系统则会进一步依赖软件甚至硬件,就是这样层层封装)其实废话这么多,一个同步方法内中所有的内容,就是属于同一个监视区域图片 10

多线程:在一个进程中,运行不止一个线程

它也用于Java同步。

在java同步代码中,哪部分才是监听器?

我们知道每个对象/类都关联着一个监听器,我觉得这么说好点,每个对象都拥有一个监听器,因为每个对象都有它关键的区域和监听线程队列的能力。

为了使不同线程能相互合作,java提供了wait()和notify()方法来暂停一个线程和唤醒分别等待访问这个对象的其他线程中的一个,另外还有3个其他版本:

这些方法只能在一个同步语句或者同步方法内调用,原因是因为如果一个方法不要求互斥,那么就没有必要在线程间监听或者是合作,每个线程都可以自由访问这个方法。

这里是一些同步代码的例子。

参考资料

1. Java Doc for Object

2. Thread synchronization
3. Locks and Synchronization
4. notify() vs notifyAll()

Java监视器逻辑

去医院就医时,有时需要进一步检查,现在你感冒有时都会让你查血  ̄□ ̄||大致的流程可能是这样子的:图片 11挂号后,你会在医生办公室外等待医生叫号,医生处理后,你会去缴费,化验、等待结果等,拿到结果后,在重新回来进入医生办公室,当医生给当前的病人结束后,就会帮你看(也有些医院取结果后也有报道机,会有复诊的队列,此处我只是举个例子,不要较真,我想你肯定见过这种场景:就是你挂号进去之后,医生旁边站了好几个人,那些要么是拿到结果回来的,要么是取药后回来咨询的)在上面的流程中,相当于有两个队伍,一个是第一次挂号后等待叫号,另一个是医生诊治后还需要再次诊治的等待队伍图片 12而对于Java监视器,其实也是类似这样一种逻辑当一个线程到达时,如果一个监视器没有被任何线程持有,那么可以直接进入监视器执行任务;如果监视器正在被其他线程持有,那么将会进入“入口区域”,相当于走廊,在走廊排队等待叫号;在监视器中执行的线程,也可能因为某些事情,不得不暂停等待,可以通过调用等待命令;比如经典的“读者--写者”问题,读者必须等待缓冲区“非满”状态,这就相当于大夫开出来了化验单,你要去化验,你要暂时离开医生,医生也就因此空闲了;此时这个线程就进入了这个监视器的“等待区域”一旦离开,医生空闲,监视区域空出来了,所以其他的线程就有机会进入监视区域运行了;一个监视区域内运行的线程,也可以执行唤醒命令,通过唤醒命令可以将等待区域的线程重新有机会进入监视区域简言之

  • 一个监视区域前后各有一个区域:入口区域,等待区域:
  • 如果监视区域有线程,那么入口区域需要等待,否则可以进入;
  • 监视区域内执行的线程可以通过命令进入等待队列,也可以将等待队列的线程唤醒,唤醒后的线程就相当于是入口区域的队列一样,可以等待进入监控区域;

需要注意的是:并不是说监控区域内的线程一定要在或者会在最后一个时刻才会唤醒等待区域的线程,他随时都可以将等待区域内的线程唤醒**也就是说唤醒别人的同时,并不意味着他离开了监控区域,所以JVM的这种监控器实现机制也叫做“发信号并继续”**而且需要注意的是,等待线程并不是唤醒后就立即醒来,当唤醒线程执行结束退出监视区域后,等待线程才会醒来可以想一下,线程进入等待区域必然是有某些原因不满足,所以才会等待,但是唤醒线程并不是最后一步才唤醒的,既然是在继续执行,方才条件满足唤醒了,那现在是否还满足?另外如果唤醒线程退出监控区域之后,反而出现了第三个线程抢先进入了监控区域怎么办?这个线程也是有可能对资源进行改变的,执行结束后可能等待线程的条件是否仍旧还是满足的?这都是不得而知的,所以也可能继续进入等待也可能退出等待区域,只能说除非逻辑有问题,不然只能够说在唤醒的那一刻,看起来是满足了的

并行:多个CPU 实例或者多台计算机同时运行一段处理逻辑,该中情况是真正的在时间上同时值行哦

这篇文章用一个类比来解释“监控”的基本概念。

进出监视器流程

图片 13

  • 线程到达监控区域开始处,通过途径1进入入口区域,如果没有任何线程持有监控区域,通过途径2进入监控区域,如果被占用,那么需要在入口区域等待;
  • 一个活动线程在监控区域内,有两种途径退出监控区域,当条件不满足时,可以通过途径3借助于等待命令进入等待或者顺利执行结束后通过途径5退出并释放监视器
  • 当监视器空闲时,入口区域的等待集合将会竞争进入监视器,竞争成功的将会进入监控区域,失败的继续等待(如果有等待的线程被唤醒,将会一同参与竞争)
  • 对于等待区域,要么通过途径3进入,要么通过途径4退出,只有这两条途径,而且只有一个线程持有监视器时才能执行等待命令,也只有再次持有监视器时才能离开等待区
  • 对于等待区域中的线程,如果是有超时设置的等待,时间到达后JVM会自动通过唤醒命令将他唤醒,不需要其他线程主动处理

并发:通过CPU的调度算法,让用户看上去好像是在并行,但是实际上CPU并不是真正的同时,下面引用网友一张图:

1. 什么是显示器?

关于唤醒

JVM中有两种唤醒命令,notify和notify all,唤醒一个和唤醒所有唤醒更多的是一种标志、提示、请求,而不是说唤醒后立即投入运行,前面也已经讲过了,  如果条件再次不满足或者被抢占。对于JVM如何选择下一个线程,依照具体的实现而定,是虚拟机层面的内容。比如按照FIFO队列?按照优先级?各种权重综合?等等方式而且需要注意的是,除非是明确的知道只有一个等待线程,否则应该使用notify all,否则,就可能出现某个线程等待的时间过长,或者永远等下去的几率。

图片 14

监视器可以看作是包含一个特殊房间的建筑物。

语法糖

对于开发者来说,最大的好处就是线程的同步与调度这些是内置支持的,监视器和锁是语言附属的一部分,而不需要开发者去实现synchronized关键字就是同步,借助于他就可以达到同步的效果,这应该算是语法糖了对于同步代码块,JVM借助于monitorenter和monitorexit,而对于同步方法则是借助于其他方式,调用方法前去获取锁只需要如下图使用关键字 synchronized就好,这些指令都不需要我们去做图片 15

线程安全:线程安全一般是对变量或者一段代码而言,指在并发的情况下,该代码被多线程使用,但是线程的调度顺序并不会应用最终结果,我们编写程序只需要考虑系统的内存和CPU是否够用即可,反过来,非线程安全即使线程的先后值行可能造成不一样的结果

这个特殊的房间一次只能容纳一个客户。

有关锁的几个概念

  • 死锁
  • 锁死
  • 活锁
  • 饥饿
  • 锁泄露

死锁共享资源竞争时,比如两个锁a和b,A线程持有了a等待b,而B持有了b而等待a,此时就会出现互相等待的情况,这就叫做死锁锁死当一个线程等待某个资源时,或者等待其他线程的唤醒时,如果迟迟等不到结果,就可能永远的等待沉睡下去,这就是锁死活锁虽然线程一直在持续运行,处于RUNNABLE,但是如果任务迟迟不能继续进行,比如每次回来条件都不满足,比如一直while循环进行不下去,这就是活锁饥饿如果一个线程因为某种条件等待或者睡眠了,但是却再也没有得到CPU的临幸,迟迟得不到调度,或者永远都没有得到调度,这就是饥饿锁泄露如果一个线程获得锁之后,执行完临界区的代码,但是却并没有释放锁,就会导致其他等待该锁的线程无法获得锁,这叫做锁泄露

同步:在Java中的同步指的是通过人为的控制程序值行,保证共同资源来对多线程访问是线程安全的,一般同步有多种实现比如 synchronized 关键字还有CAS算法等

TAG标签:
版权声明:本文由必威发布于必威-编程,转载请注明出处:实例或者多台计算机同时运行一段处理逻辑,大