精华内容
下载资源
问答
  • 进程间通信(一):进程之间的冲突与处理方式  ——《现代操作系统第二章第节》  1、问题的提出  我们想象一假脱机打印程序,当一...另一进程负责周期性地检查是否文件需要被打印,如果就打印并将其在目

                                    进程间通信(一):进程之间的冲突与处理方式

                                                               ——《现代操作系统第二章第三节》

         1、问题的提出

         我们想象一个假脱机打印程序,当一个进程需要打印一个文件时,它会将该文件放在一个假脱机目录下。另一个进程负责周期性地检查是否有文件需要被打印,如果有就打印并将其在目录中删除。简单设想,脱机目录中有很多槽位,每个槽位中存放文件名,假设它们有两个共享的变量:out,指向下一个要被打印的文件;in,指向下一个空闲的槽位。如图,下一个被打印的应该是4号槽,下一个入队的应该是7号槽。

                                          

          现在,假设进程A、B将把文件A、B入队,假设A先读到的信息是7,并且A将7存入自己的一个记忆变量中,而这时,系统认为已经分给了A足够的时间,于是中断A切换置进程B。进程读到的信息是7,将7存入自身的一个记忆变量中,并将int更新至8。至此B已经完成了所有的入队操作,转而去干其他的事情。当A继续执行时,它还认为应该将文件存到7号槽,于是A文件成功地覆盖住了B文件,而我们的B文件,永远都不会被打印。问题就出现了。

          2、抽象一些概念

          竞争条件:类似于上述情况,即两个或者多个进程读写某些共享数据,而最后的结果取决于进程的运行的精确时序,称为竞争条件。(把条件理解成情况,竞争情况,貌似更加容易理解一些=。=)

          互斥:互斥是一种手段,它使共享数据的进程无法同时对其共享的数据进行处理。

          临界区:即访问共享内存的程序片。也就是说,通过合理的安排,使得两个进程不可能同时处在临界区中,就能避免竞争条件。

          忙等待:连续测试一个变量直至某值出现位止。(如语句while(t!=0){},那么当t不为零时,while()之后的语句将永远不会执行,这种情况书中好像也叫挂起)

          自旋锁:用于忙等待的锁。(在3-(3)中,turn即使自旋锁)

                 

              3、忙等待的互斥(几种实现互斥的方法)

             (1)屏蔽中断

              原理:进程进入临界区后立即屏蔽所有中断,离开后打开中断。

              缺点:a、多核的系统无效(其他进程任然可以占用其他的CPU继续访问公共内              

                       存)

                    b、用户程序来控制中断会很危险(使想一下,一个进程屏蔽中断后不再       

                       打开中断,那你的系统就GG了)

              结论:屏蔽中断对系统本身是一项很有用的技术,但对用户进程不是一种合适的

                    通用互斥机制。

             (2)锁变量

              原理:屏蔽中断的软件实现机制。

                    假定一个共享(锁)变量,初值为0,代表临界区内无进程,进程进入临                

                 界区后将其改变为1,代表临界区内有进程;倘若进程在进入临界区之前,              

                 锁变量值为1,该进程将等待其值变为0。

    未能实现的原因:与假脱机目录的疏漏一样,如果一个进程进入临界区,但是在       

                 它把锁变量置1之前被中断,另一个进程进入临界区后将0置1,这样,

                 当前一个进程再次运行时它也将锁变量置1,这样临界区内依然存在两

                 个进程。

             (3)严格轮换

              原理:共享turn变量,用来记录轮到那个进程进入临界区。

                       

               当turn=0时,只有进程0能进入临界区,进程0在离开临界区前将turn 

                  置1,从而标志,轮到进程1进入临界区。

               缺点:严格地轮换,可能导致临界区外的进程阻塞需要进入临界区的进程(例 

                   如:当turn=0时,意味着只有进程0能进入临界区,这时如果进程0还要

                   100年才会进入临界区,而进程1需要马上进入,那进程1还要白白等100 

                   年.)

              总结:当一个进程比另一个进程慢了许多的情况下,不宜用这种方式。

             (4)Peterson解法

              这是Peterson本人发明的一种简单的互斥算法。

     

                     

              我们分情况跑一遍程序:

              a、进程0通过调用enter_region()进入临界区,此时1也想调用enter_region() 

             来进入临界区,但interested[0]为TRUE,因此被while循环挂起,当进程0出临

             界区时调用leave_region(),将interested[0]设为FALSE,进程1即可及时进入临界

             区。

              b、当进程0在调用enter_region()过程的任意时刻被中断,进程1进入临界区

              后进程0再次进行时,依然会被挂起。(实际上while循环体中的两条判断句就 

              保证了,当一个进程在临界区中时,另一个想进入临界区的进程必然会被挂起)。

     

             (5)TSL指令

              原理简述:

              TSL(test and set lock),是十分适合多处理器计算机实现互斥的硬件支持。它会 

             在一个进程进入临界区时,锁住内存总线,从而禁止其他CPU在本指令结束之前 

             访问内存。

             对于TSL指令,本文之做简单的原理性描述(虽然老师上课讲的比较详细),想进

             一步了解关于TSL指令,可以看一下书中本节内容,有详细阐述。

        4、综述

            要想拟定一个方案,使它既能避免竞争条件,又能保证进程运行与协作的效率,必须要满足4个条件。

             (1)、任何两个进程不能同时处于临界区

             (2)、不应对CPU的速度和数量做任何假定

             (3)、临界区外运行的进程不能阻塞其他进程

             (4)、不得使进程无限期等待进入临界区

        (下图为避免竞争条件的模型图)

     

                

     

    展开全文
  • 互斥

    2020-11-07 19:35:54
    保护资源4.1 关系简介4.2 保护没有关联关系的多资源4.3 保护关联关系的多资源4.4 小结 一.原子问题 在并发编程Bug源头中介绍过,原子问题的源头是线程切换,解决方案禁用线程切换。 CPU控制线程切换,无论单核...

    一.原子问题

    并发编程Bug源头中介绍过,原子问题的源头是线程切换,解决方案禁用线程切换。

    CPU控制线程切换,无论单核CPU还是多核CPU,保证同一时刻只有一个线程执行,称为互斥,就能够保证对共享变量的修改时互斥,就能保证原子性。

    二.锁模型

    互斥的解决方案是锁,把一段需要互斥执行的代码称为临界区。

    图片

    这个锁模型展示的是锁和锁要保护的资源是有关系,很多并发Bug是因为关联关系,产生奇怪的问题。

    三.解决方案

    2.1 synchronized

    锁是一种通用的技术方案,Java语言提供的synchronized 关键字,就是锁的一种实现。

    //Java 编译器会在 synchronized 修饰的方法或代码块前后自动加上加锁 //lock() 和解锁 unlock(),这样做的好处就是加锁 lock() 和解锁 //unlock() 一定是成对出现的。
    class X {
      // 修饰非静态方法
      synchronized void foo() {
        // 临界区
      }
      // 修饰静态方法
      synchronized static void bar() {
        // 临界区
      }
      // 修饰代码块
      Object obj = new Object();
      void baz() {
        synchronized(obj) {
          // 临界区
        }
      }
    }  
    

    Java隐式规则

    • 当synchronized 修饰静态方法时候,锁定的是当前类Class对象,在上面的例子中就是Class X;

    • 当synchronized 修饰非静态方法时候,锁定的是当前实例对象this。

    class SafeCalc {
      long value = 0L;
      synchronized long get() {
        return value;
      }
      synchronized void addOne() {
        value += 1;
      }
    }
    

    get() 方法和 addOne() 方法都需要访问 value 这个受保护的资源,这个资源用 this 这把锁来保护。线程要进入临界区 get() 和 addOne(),必须先获得 this 这把锁,这样 get() 和 addOne() 也是互斥的。

    四.保护资源

    4.1 关系简介

    受保护资源和锁之间关系是N:1的关系,多把锁来保护同一个资源,在并发领域不行,同一把锁来保护多个资源是可以的。

    4.2 保护没有关联关系的多个资源

    账户类 Account 有两个成员变量,分别是账户余额 balance 和账户密码 password。取款 withdraw() 和查看余额 getBalance() 操作会访问账户余额 balance,我们创建一个 final 对象 balLock 作为锁(类比球赛门票);而更改密码 updatePassword() 和查看密码 getPassword() 操作会修改账户密码 password,我们创建一个 final 对象 pwLock 作为锁(类比电影票)。不同的资源用不同的锁保护,各自管各自的,很简单。

    class Account {
      // 锁:保护账户余额
      private final Object balLock
        = new Object();
      // 账户余额  
      private Integer balance;
      // 锁:保护账户密码
      private final Object pwLock
        = new Object();
      // 账户密码
      private String password;
    
      // 取款
      void withdraw(Integer amt) {
        synchronized(balLock) {
          if (this.balance > amt){
            this.balance -= amt;
          }
        }
      } 
      // 查看余额
      Integer getBalance() {
        synchronized(balLock) {
          return balance;
        }
      }
    
      // 更改密码
      void updatePassword(String pw){
        synchronized(pwLock) {
          this.password = pw;
        }
      } 
      // 查看密码
      String getPassword() {
        synchronized(pwLock) {
          return password;
        }
      }
    }
    

    4.3 保护有关联关系的多个资源

    例如银行业务里面的转账操作,账户 A 减少 100 元,账户 B 增加 100 元。这两个账户就是有关联关系的。

    用 Account.class 作为共享的锁(有问题,后续解释)

    class Account {
      private int balance;
      // 转账
      void transfer(Account target, int amt){
        synchronized(Account.class) {
          if (this.balance > amt) {
            this.balance -= amt;
            target.balance += amt;
          }
        }
      } 
    }
    

    该解决方案,存在性能问题,因为把所有银行业务转账行为,都变成串行了。
    现实世界里,账户转账操作是支持并发的,而且绝对是真正的并行,银行所有的窗口都可以做转账操作。只要我们能仿照现实世界做转账操作,串行的问题就解决了。

    并行解决银行转账问题

    把问题具象化,假设柜员在拿账本的时候,可能遇到以下三种情况:

    • 文件架上恰好有转出账本和转入账本,那就同时拿走;
    • 如果文件架上只有转出账本和转入账本之一,那这个柜员就先把文件架上有的账本拿到手,同时等着其他柜员把另外一个账本送回来;
    • 转出账本和转入账本都没有,那这个柜员就等着两个账本都被送回来。

    图片

    @Data
    public class AccountClassLock3 {
        private int balance;
        public AccountClassLock3(int balance) {
            this.balance = balance;
        }
        public void transfer(AccountClassLock3 target,int amt){
            //锁定转出账户
            synchronized (this){
                //锁定转入账户
                synchronized (target){
                    if(this.balance > amt){
                        this.balance -= amt;
                        target.balance += amt;
                    }
                }
            }
        }
        public static void main(String[] args) throws InterruptedException {
            for (int i = 0; i < 10000; i++) {
                AccountClassLock3 a = new AccountClassLock3(200);
                AccountClassLock3 b = new AccountClassLock3(200);
                AccountClassLock3 c = new AccountClassLock3(200);
    
                Thread t1 = new Thread(()->{
                    a.transfer(b,100);
                });
                Thread t2 = new Thread(()->{
                    b.transfer(c,100);
                });
                t1.start();
                t2.start();
                
                t2.join();
                System.out.println(a.getBalance());
                System.out.println(b.getBalance());
                System.out.println(c.getBalance());
                System.out.println("---------------");
            }
        }
    }
    

    这样的锁时细粒度锁,使用细粒度锁可以提高并行度,是优化性能的一个重要手段,但是使用细粒度锁是有代价的,这个代价就是可能会导致死锁。

    死锁实验

    现实世界里的一种特殊场景。如果有客户找柜员张三做个转账业务:账户 A 转账户 B 100 元,此时另一个客户找柜员李四也做个转账业务:账户 B 转账户 A 100 元,于是张三和李四同时都去文件架上拿账本,这时候有可能凑巧张三拿到了账本 A,李四拿到了账本 B。张三拿到账本 A 后就等着账本 B(账本 B 已经被李四拿走),而李四拿到账本 B 后就等着账本 A(账本 A 已经被张三拿走),他们要等多久呢?他们会永远等待下去…因为张三不会把账本 A 送回去,李四也不会把账本 B 送回去。我们姑且称为死等吧。

    @Data
    public class AccountClassLock3 {
        private int balance;
        public AccountClassLock3(int balance) {
            this.balance = balance;
        }
        public void transfer(AccountClassLock3 target,int amt){
            //锁定转出账户
            synchronized (this){
                //锁定转入账户
                synchronized (target){
                    if(this.balance > amt){
                        this.balance -= amt;
                        target.balance += amt;
                    }
                }
            }
        }
        public static void main(String[] args) throws InterruptedException {
            for (int i = 0; i < 100000; i++) {
                AccountClassLock3 a = new AccountClassLock3(200);
                AccountClassLock3 b = new AccountClassLock3(200);
                AccountClassLock3 c = new AccountClassLock3(200);
    
                Thread t1 = new Thread(()->{
                    a.transfer(b,100);
                });
    
    
                Thread t2 = new Thread(()->{
                    b.transfer(a,100);
                });
                t1.start();
                t2.start();
                t2.join();
                System.out.println(a.getBalance());
                System.out.println(b.getBalance());
                //System.out.println(c.getBalance());
                System.out.println("---------------");
            }
        }
    }
    

    图片

    这是示例运行情况,dump记录,线程正在WAITING,解决方案后续文章介绍。

    4.4 小结

    原子性本质,不是不可分割,不可分割只是外在表现,其本质是多个资源间有一致性要求,操作的中间状态对外不可见。

    参考

    《Java并发编程实战》

    公众号

    图片

    微信公众号(bigdata_limeng)

    展开全文
  • 前言 最近在看黑马的并发编程笔记,讲...在同时线程运行过程中,如何达到互斥和同步呢? 加锁即可 在此使用黑马笔记中room例子来说明锁。(ps: 以前就了解锁,但总会记乱,发现使用形象化记忆后就很清楚) 解

    前言

    • 最近在看黑马的并发编程笔记,讲得很好,对此进行一下总结
    • 总结的目的不是抄一遍笔记,而是谈谈自己的理解加深记忆
    • 涉及
      • 线程互斥与同步
      • synchronized使用细节及原理
      • Reentrylock使用细节
      • 三种模式思路

    线程互斥与同步

    互斥:指的是多个线程不能同时访问共享变量
    同步:指的是多个线程按指定的顺序执行操作

    在同时有多个线程运行过程中,如何达到互斥和同步呢?

    • 加锁即可

    在此使用黑马笔记中room例子来说明锁。(ps: 以前就了解锁,但总会记乱,发现使用形象化记忆后就很清楚)

    在这里插入图片描述
    解决互斥

    • 锁就相当于上图的房子,里面放着会被并发访问的共享变量
    • 此时绿色区域(owner)无线程,此时多个线程想并发访问房子里的共享变量,那么只允许其中一个线程进入房子访问,并把房门锁上。
    • 剩下的没有拿到锁的线程只能在entrylist中排队
    • owner中的线程访问结束后会离开房子,并告诉entrylist的线程可以进房子了
    • entrylist的线程开始新一轮的挣锁,如此反复
    • 这样就能解决互斥的问题

    解决同步
    (这涉及到为什么wait(),notify()方法需要用锁,就是因为只有用了锁才能完成同步,那么怎么完成的呢?)

    • 多个线程同时启动,如果希望B线程在A线程之后执行
    • 那么当B先抢到锁,即先进入了房子,此时A只能在entrylist中排队
    • 为了让A先执行,那么可以先让B进入蓝色区域,即waitset中等待,并且把门打开,告诉entrylist中的线程可以进来了
    • 那么A进来后,执行完任务,临走时通知waitset中的B,B再回到绿色区域执行任务就能保证有序了
    • 这样就能解决同步问题

    那么room这个数据结构其实就是synchronized的核心了,接下来总结synchronized原理的时候会一直用room的例子

    synchronized

    很多人对synchronized原理的理解也就停留在知道字节码有个monitor关键字来管理锁,再浅一点的只知道怎么用,再者懂得深一点的却记不住。我之前就是想深入了解一下但觉得苦涩,就看不下去了,看了黑马的笔记我觉得这玩意儿其实很简单,所以好的老师还是比较重要的。那么在此我也记录一下怎么更好的去理解synchronized的底层原理

    从字节码我们可以知道synchronized的底层就是关联了一个monitor,那么这玩意儿是个什么东西,怎么实现锁的功能呢?

    首先,可以把monitor的数据结构简化成上图的room,具体点描述如下图
    在这里插入图片描述

    • synchronized(锁对象)的时候,相当于让锁对象绑定了一个monitor(具体绑定方法不打算在后面总结)
    • 那么多个线程中方法涉及到该锁对象时,都会来访问锁对象对应的monitor
    • 此时线程thread-2抢到了锁,操作就是让monitor中的owner字段指向thread-2线程,意味着当前线程获取到了基于该monitor的锁
    • 其他没抢到锁的,monitor会将他们放在Entrylist中等待,这些线程只能在队列中等着
    • thread-2线程完成操作后就会退出,并通知entrylist的线程重新抢锁
      • 如果在执行过程中,线程调用了wait()方法,monitor就会将他们放入waitset中等待别人唤醒
      • (看回room结构)owner进入waitset后会把门打开,让entrylist的线程进来
      • 直到某时刻owner中有线程调用notify()方法,waitset中的线程才会被唤醒,唤醒后会进入entrylist中重新抢锁

    以上就是synchronized的原理。有人就会问了,你说的这些文字我都懂啊,搞个图摆在这也没啥用。

    接下来我将从上图直接回答下面的常见的问题

    • wait()和notify()为什么都得在synchronized后使用?
      • wait()就是将线程放入waitset中,那么waitset是在room里面的,不上锁怎么能进room中?同理,不进入room,在门外怎么使用notify()怎么能叫醒waitset中的线程?
    • wait()会释放锁吗?
      • 废话,不开门的话,怎么放线程进来,就更别提唤醒了
    • notifyALL()为什么不会唤醒其他锁对象的线程?
      • 进哪个room才能叫哪个waitset,进了Aroom当然只能叫醒A的waiset了
    • 说说synchronized的原理?
      • 把图画出来就行了
    • 线程什么时候从runnable变成waiting,什么时候变成block?
      • 看图,进入waiset就是wait,所以调wait()就变成waiting状态
      • 进入entrylist就是block,所以被唤醒后以及没抢到锁都变block
    • 。。。。。。。。。。。。

    注意了,这里涉及monitor的原理都是synchronized最根本的原理,也称重量级锁,可以看到monitor会频繁切换线程状态,效率比较低。后来synchronized改进了,在使用monitor前还有好几种方案,分别为偏向锁,轻量锁,以及自旋优化。这部分也是面试常考点,也容易记乱,但用图例去记就很清楚。

    那么接下来就说说synchronized的改进

    轻量锁与偏向锁

    轻量锁与偏向锁的核心都是先不让线程冲突的时候直接去找monitor,而是先用锁对象的对象头字段来解决冲突

    (写博客好累啊。。。算了我就总结一些自己觉得关键的地方吧)

    轻量锁

    • 对于轻量锁而言,每个线程维护了一个锁记录,抢占锁的过程就是用CAS将自己的信息与锁对象的对象头mark word部分交换
    • 这样其他慢一步的线程CAS会失败,就意识到锁已经被占了
    • 可重入只需要在占锁的时候判断锁对象的markword记录的是不是自己的线程id即可,是的话就能够获取锁,也就是叠加一个锁记录
    • 释放锁就意味着删除锁记录,直到锁记录清空,就将锁对象头部被修改的字段变回原样
    • 轻量锁是认为不会有竞争,如果发生了线程竞争,锁需要升级,不然上述方法没有像monitor的entrylist来管理其他竞争暂时没拿到锁的线程
    • 锁升级就是锁膨胀,直接调monitor来管理,就将owner指向当前线程,然后竞争线程去entrylist排队
    • 其中涉及自旋优化,就是线程竞争时,第二个线程不用立刻去entrylist中,这样又要涉及上下文切换,可以自旋一会看锁能否抢到

    偏向锁

    • 轻量锁每次占锁都要用一次CAS来更新锁对象头,如果本来就没啥竞争那CAS就是无用的操作了
    • 为了解决这个问题,线程抢锁成功后直接把自己的ID刻在锁对象头中,需要判断重入时只需判断ID是否相同即可

    写不动了。。。好累,算了

    展开全文
  • 文章目录一、线程安全与互斥(案例说明)1....2、设置三个窗口同时卖电影票 3、观察卖电影票的过程 2.代码实现 代码如下(SellTicket线程): public class SellTicket implements Runnable { // 定义100张票 p

    一、线程安全与互斥(案例说明)

    1.经典案例

    1、有100张电影票
    2、设置三个窗口同时卖电影票
    3、观察卖电影票的过程

    2.代码实现

    代码如下(SellTicket线程):

    public class SellTicket implements Runnable {
    	// 定义100张票
    	private int tickets = 100;
    
    	@Override
    	public void run() {
    		while (tickets > 0) {
    			String name = Thread.currentThread().getName();
    			System.out.println(name + "正在出售第" + tickets + "票");
    			tickets--;
    		}
    	}
    }
    

    代码如下(SellTicket线程测试):

    public class Test02 {
    
    	public static void main(String[] args) {
    		SellTicket sellTicket = new SellTicket();
    		Thread t1 = new Thread(sellTicket,"窗口1");
    		Thread t2 = new Thread(sellTicket,"窗口2");
    		Thread t3 = new Thread(sellTicket,"窗口3");
    		t1.start();
    		t2.start();
    		t3.start();
    	}
    }
    

    输出:
    在这里插入图片描述

    小结

    观察SellTicket线程测试输出我圈出来的那部分出现了错误错误卖票,由于输出版面有限,其实还有一个错误就是可能会卖出负的票数

    多个线程同时访问共享资源很可能出现线程安全问题

    二、解决思路

    对多操作共享的语句为了保证各线程都执行完成,在某线程执行过程中,其他线程不可以参与执行

    1.解决办法——同步代码块

    同步代码块中的锁对象可以是任意对象。在进入代码块时需要获得锁对象,如果得到锁对象则等待;在退出代码块时自动释放锁对象。
    任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行结束后,该线程自然会释放对同步监视器对象的锁定。
    在java程序运行中可以同任何对象作为同步监视器对象,只要保证共享资源的这几个线程锁的是同一同步监视对象即可。
    语法:

    		synchronized (this) {
    			需要被同步的代码
    		}
    

    2.同步代码块(SellTicket线程)

    代码如下(同步代码块(SellTicket线程)):

    public class SellTicket implements Runnable {
    	// 定义100张票
    	private int tickets = 100;
    
    	@Override
    	public void run() {
    		// 同步代码块(加锁)
    		synchronized (this) { 
    			while (tickets > 0) {
    				String name = Thread.currentThread().getName();
    				System.out.println(name + "正在出售第" + tickets + "票");
    				tickets--;
    			}
    		}
    	}
    }
    

    代码如下(同步代码块(SellTicket线程)测试):

    public class Test02 {
    
    	public static void main(String[] args) {
    		SellTicket sellTicket = new SellTicket();
    		Thread t1 = new Thread(sellTicket,"窗口1");
    		Thread t2 = new Thread(sellTicket,"窗口2");
    		Thread t3 = new Thread(sellTicket,"窗口3");
    		t1.start();
    		t2.start();
    		t3.start();
    	}
    }
    

    代码如下(同步代码块(SellTicket线程)测试输出):

    窗口1正在出售第100票
    窗口1正在出售第99票
    窗口1正在出售第98票
    窗口1正在出售第97票
    窗口1正在出售第96票
    窗口1正在出售第95票
    窗口1正在出售第94票
    窗口1正在出售第93票
    窗口1正在出售第92票
    窗口1正在出售第91票
    窗口1正在出售第90票
    窗口1正在出售第89票
    窗口1正在出售第88票
    窗口1正在出售第87票
    窗口1正在出售第86票
    窗口1正在出售第85票
    窗口1正在出售第84票
    窗口1正在出售第83票
    窗口1正在出售第82票
    窗口1正在出售第81票
    窗口1正在出售第80票
    窗口1正在出售第79票
    窗口1正在出售第78票
    窗口1正在出售第77票
    窗口1正在出售第76票
    窗口1正在出售第75票
    窗口1正在出售第74票
    窗口1正在出售第73票
    窗口1正在出售第72票
    窗口1正在出售第71票
    窗口1正在出售第70票
    窗口1正在出售第69票
    窗口1正在出售第68票
    窗口1正在出售第67票
    窗口1正在出售第66票
    窗口1正在出售第65票
    窗口1正在出售第64票
    窗口1正在出售第63票
    窗口1正在出售第62票
    窗口1正在出售第61票
    窗口1正在出售第60票
    窗口1正在出售第59票
    窗口1正在出售第58票
    窗口1正在出售第57票
    窗口1正在出售第56票
    窗口1正在出售第55票
    窗口1正在出售第54票
    窗口1正在出售第53票
    窗口1正在出售第52票
    窗口1正在出售第51票
    窗口1正在出售第50票
    窗口1正在出售第49票
    窗口1正在出售第48票
    窗口1正在出售第47票
    窗口1正在出售第46票
    窗口1正在出售第45票
    窗口1正在出售第44票
    窗口1正在出售第43票
    窗口1正在出售第42票
    窗口1正在出售第41票
    窗口1正在出售第40票
    窗口1正在出售第39票
    窗口1正在出售第38票
    窗口1正在出售第37票
    窗口1正在出售第36票
    窗口1正在出售第35票
    窗口1正在出售第34票
    窗口1正在出售第33票
    窗口1正在出售第32票
    窗口1正在出售第31票
    窗口1正在出售第30票
    窗口1正在出售第29票
    窗口1正在出售第28票
    窗口1正在出售第27票
    窗口1正在出售第26票
    窗口1正在出售第25票
    窗口1正在出售第24票
    窗口1正在出售第23票
    窗口1正在出售第22票
    窗口1正在出售第21票
    窗口1正在出售第20票
    窗口1正在出售第19票
    窗口1正在出售第18票
    窗口1正在出售第17票
    窗口1正在出售第16票
    窗口1正在出售第15票
    窗口1正在出售第14票
    窗口1正在出售第13票
    窗口1正在出售第12票
    窗口1正在出售第11票
    窗口1正在出售第10票
    窗口1正在出售第9票
    窗口1正在出售第8票
    窗口1正在出售第7票
    窗口1正在出售第6票
    窗口1正在出售第5票
    窗口1正在出售第4票
    窗口1正在出售第3票
    窗口1正在出售第2票
    窗口1正在出售第1

    3.解决办法——同步函数

    对于同步方法(函数)来说,无需显示指定同步监视器,静态方法的同步监视器对象是当前类的class对象,非静态方法的同步监视器对象调用当前方法的this对象
    语法:

    	synchronized 函数返回值 函数名([参数列表]) {
    		需要被同步的代码块
    	}
    

    2.同步函数(SellTicket线程)

    代码如下(同步函数(SellTicket线程)):

    public class SellTicket implements Runnable {
    	// 定义100张票
    	private int tickets = 2000;
    
    	@Override
    	public void run() {
    			while (true) {
    				sellTicket();
    		}
    	}
    	// 同步函数(加锁)
    	public synchronized void sellTicket() {
    		if(tickets>0) {
    			String name = Thread.currentThread().getName();
    			System.out.println(name + "正在出售第" + tickets + "票");
    			tickets--;
    		}
    	}
    }
    

    代码如下(同步函数(SellTicket线程)测试):

    public class Test02 {
    
    	public static void main(String[] args) {
    		SellTicket sellTicket = new SellTicket();
    		Thread t1 = new Thread(sellTicket,"窗口1");
    		Thread t2 = new Thread(sellTicket,"窗口2");
    		Thread t3 = new Thread(sellTicket,"窗口3");
    		t1.start();
    		t2.start();
    		t3.start();
    	}
    }
    

    代码如下(同步函数(SellTicket线程)测试输出):

    窗口1正在出售第100票
    窗口3正在出售第99票
    窗口3正在出售第98票
    窗口3正在出售第97票
    窗口3正在出售第96票
    窗口3正在出售第95票
    窗口3正在出售第94票
    窗口3正在出售第93票
    窗口3正在出售第92票
    窗口3正在出售第91票
    窗口3正在出售第90票
    窗口3正在出售第89票
    窗口3正在出售第88票
    窗口3正在出售第87票
    窗口3正在出售第86票
    窗口3正在出售第85票
    窗口3正在出售第84票
    窗口3正在出售第83票
    窗口3正在出售第82票
    窗口3正在出售第81票
    窗口3正在出售第80票
    窗口3正在出售第79票
    窗口3正在出售第78票
    窗口3正在出售第77票
    窗口3正在出售第76票
    窗口3正在出售第75票
    窗口3正在出售第74票
    窗口3正在出售第73票
    窗口3正在出售第72票
    窗口3正在出售第71票
    窗口3正在出售第70票
    窗口3正在出售第69票
    窗口3正在出售第68票
    窗口3正在出售第67票
    窗口3正在出售第66票
    窗口3正在出售第65票
    窗口3正在出售第64票
    窗口3正在出售第63票
    窗口3正在出售第62票
    窗口3正在出售第61票
    窗口3正在出售第60票
    窗口3正在出售第59票
    窗口3正在出售第58票
    窗口3正在出售第57票
    窗口3正在出售第56票
    窗口3正在出售第55票
    窗口3正在出售第54票
    窗口3正在出售第53票
    窗口3正在出售第52票
    窗口3正在出售第51票
    窗口3正在出售第50票
    窗口3正在出售第49票
    窗口3正在出售第48票
    窗口3正在出售第47票
    窗口3正在出售第46票
    窗口3正在出售第45票
    窗口3正在出售第44票
    窗口3正在出售第43票
    窗口3正在出售第42票
    窗口3正在出售第41票
    窗口3正在出售第40票
    窗口3正在出售第39票
    窗口3正在出售第38票
    窗口3正在出售第37票
    窗口3正在出售第36票
    窗口3正在出售第35票
    窗口3正在出售第34票
    窗口3正在出售第33票
    窗口3正在出售第32票
    窗口3正在出售第31票
    窗口3正在出售第30票
    窗口3正在出售第29票
    窗口3正在出售第28票
    窗口3正在出售第27票
    窗口3正在出售第26票
    窗口3正在出售第25票
    窗口3正在出售第24票
    窗口3正在出售第23票
    窗口3正在出售第22票
    窗口3正在出售第21票
    窗口3正在出售第20票
    窗口3正在出售第19票
    窗口3正在出售第18票
    窗口3正在出售第17票
    窗口3正在出售第16票
    窗口3正在出售第15票
    窗口3正在出售第14票
    窗口3正在出售第13票
    窗口3正在出售第12票
    窗口3正在出售第11票
    窗口3正在出售第10票
    窗口3正在出售第9票
    窗口3正在出售第8票
    窗口3正在出售第7票
    窗口3正在出售第6票
    窗口3正在出售第5票
    窗口3正在出售第4票
    窗口3正在出售第3票
    窗口3正在出售第2票
    窗口3正在出售第1

    总结

    1、同步提升了安全性,但是降低了效率
    2、任意的一个对象都可以作为锁对象
    3、多个线程操作共享数据的锁必须是同一个锁,否则加锁无效
    4、只有真正存在线程安全问题的时候才使用同步代码块或者同步函数,否则会降低效率
    5、多线程程序执行具有随机性,谁抢到就谁执行,测试输出就可以观察出来

    展开全文
  • 【操作系统/OS笔记12】同步互斥种实现方法:禁用硬件中断、基于软件的解决方案、更高级的抽象 本次笔记内容: 9.5 临界区 9.6 方法1:禁用硬件中断 9.7 方法2:基于软件的解决方案 9.8 方法3:更高级的抽象 临界...
  • 首先要明白线程的工作原理,jvmmain memory,而每线程自己的working memory,一线程对一variable进行操作时,都要在自己的working memory里面建立一copy,操作完之后再写入main memory。多线程...
  • 场景1:web服务通过http请求数据,将请求对象放在内部的队列中,并且单独的工作线程从队列中取请求对象进行处理.比如,一交易系统创建一购买订单并放入队列中,这时一独立的消费线程获取订单信息并且将...
  • 互斥锁 文章目录互斥锁1. 线程不安全的大原因1.大特性问题举例1. 原子性:2. 可见性3. 有序性2. Java解决有序性和可见性的方案3.... 保护关联关系的多资源1. 误区方式2. 同一把锁方式3....
  • 原子性:提供了互斥访问,同一时刻只能线程来对它进行操作 可见性:一线程对主内存的修改可以及时的被其他线程观察到 有序性:一线程观察其他线程中的指令执行顺序,由于指令重排序的存在,该观察结果一般...
  • key对应的数据存在,但在redis中过期,此时若大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这时候大并发的请求可能会瞬间把后端DB压垮。 使用互斥锁(mutex key) 比如redis中的...
  • 首先也来看看如何使用信号量,信号量Semaphore常用有三个函数,使用很方便。下面是这几个函数的原型和使用说明。 第一个 CreateSemaphore 函数功能:创建信号量 函数原型: HANDLE CreateSemaphore( ...
  • 分布式锁应用方案

    2018-04-16 15:29:44
    大概三点: 1.多任务的环境中才需要?这多任务可以指多线程或者多进程。 2.任务都需要对同一共享资源进行写操作时。对共享资源多线程进行修改时会引发并发安全问题。 3.对资源的访问都是互斥的。 当一线程...
  • 目录 一、简介 二、Redis集群环境下分布式锁...1)、互斥性:同一时刻多客户端对共享资源的访问存在互斥性; 2)、防死锁:对锁设置超时时间,防止客户端一直占用着锁,即防止死锁; 3)、可重入性:一客户端上的
  •  单核RTOS的SMP扩展,需要解决系统初始化、任务调度、CPU间的同步与互斥、以及中断处理等方面问题,本文将针对这些问题提出相应的解决方案,并在uC/OS-II上进行SMP扩展实现。 4.1 系统初始化  虽然SMP系统中...
  • 物联网技术成为第次信息产业发展的浪潮,在不久的将来、物联网技术将会像互联网一样走进人类社会,深入我们的生活,本文根据物联网智能小区的设计理念进行了一些探索,并对智能小区的设计实现提供了思路或解决方案...
  • 解决方案: 读者写者问题: 1.允许多读者对于数据进行访问 2.只允许一写者访问 3.任一写者在完成工作之前不允许其他读者或写者工作 4.写者执行写操作之前,应该让已读者和写者全部退出 读者与读者...
  • 生产者进程使用信号量 empty ,初始化为n,因为一开始n空闲单元。当 empty = 0 时,生产者进程阻塞。 3. 消费者取出产品的问题 消费者进程使用信号量 full ,初始化为0,因为一开始一产品都没有。当 full = 0 ...
  • 击穿其实就是某个高访问量的缓存数据,它的缓存过期时间到了,这时候仍旧大量对这数据的请求发送过来,当这些请求发现缓存中没有对应数据的时候就直接去访问数据库了,这一瞬间可能就会对数据库造成巨大的压力...
  • 一、定义 当多线程...原子性:提供了互斥访问,同一时刻只能线程来对他进行操作可见性:一线程对主内存的修改可以及时的被其他线程观察到有序性:一线程观察其他线程中的指令执行顺序,由于指令重排序...
  • 小车主要是由一块小钢炮开发板、一块PCB板、一块STA350BW语音板、三个舵机、小车支架组成,相关物料见“相关文件”下载。舵机粘在了面板上,将语音板粘在舵机上,喇叭粘在语音板上,所有的接线全是用细线焊接出来的...
  • 分布式锁一般有三种实现方式: 1)数据库锁 2)基于Redis的分布式锁 3)基于ZooKeeper的分布式锁 分布式锁应该是怎么样的?? 互斥性,可以保证在分布式部署的应用集群中,同一方法在同一时间只...
  • 经典进程同步问题 1 生产者-消费者问题 生产者-消费者问题是相互...假设生产者消费者之间一共用缓冲池,具有n缓冲区,利用互斥信号量mutex实现诸进程对缓冲池的互斥作用。empty表示空缓冲池数,full表示满...
  • 监控:三个最重要的监控指标和监控策略,以及分别的预案/处理措施 预案:三个最重要的预案是什么 问题定位:三个最重要的排查内容是什么 部署 集群的各类角色,用途,是否可选,角色之间相互依赖和互斥关系 集群...
  • 保证分布式锁有效的三个属性 Safety Properties:安全性,此处也就是互斥性,任意时刻只能一个客户端可以持有锁 Liveness Property A:无死锁,即使持有锁的客户端崩溃或被分区,也可以获得锁 Liveness Property ...
  • 通过 RT-Thread官网上的文档中心,我学习了 多线程及其调度、信号量、邮箱、消息队列、内存管理、定时器等,学完之后,种感觉就是看过了,我都懂,但是感觉把这些都融合不起来,通过这次比赛的项目实践,这些知识...
  • 假设 m 组互斥现象,每组中的那两数字的面紧贴在一起,骰子就不能稳定的垒起来。 atm想计算一下多少种不同的可能的垒骰子方式。 两种垒骰子方式相同,当且仅当这两种方式中对应高度的骰子的对应数字的朝向都...
  • 一:三个问题 进程间通信简单的说有三个问题。第一个问题是一个进程如何把信息传递给另一个。第二个要处理的问题是是,要确保两个或更多的的进程在关键互动中不会出现交叉(即是进程互斥的问题),第三个问题是与...

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 129
精华内容 51
关键字:

有三个互斥方案