9. 多线程 Part 3 同步与死锁 --- 学习笔记



9.6 同步与死锁

         一个多线程的程序如果是通过Runnabl接口实现的,则意味着类中的属性将被多个线程共享,那么这样一来就会造成一种问题,如果这多个线程操作同一资源时就有可能出现资源的同步问题。例如, 卖票程序,如果多个线程同时操作时就有可能出现卖出票为负数的问题。

      9.6.1 问题的产生

范例:通过Runnable接口实现多线程,并产生3个线程对象,同时卖5张票。

class MyThread implements Runnable{
    private int ticket = 5;
    public void run(){
        for (int i = 0; i < 7; i++){
            if (ticket > 0){
            	try{
            		Thread.sleep(3000);
        		}catch(InterruptedException e){
            		e.printStackTrace();
        		}
        		System.out.println("卖票: ticket = " + ticket--);
            }
        }
    }
}
public class SyncDemo01{
    public static void main(String args[]){
        MyThread my = new MyThread();
        new Thread(my, "Thread-A").start();
        new Thread(my, "Thread-B").start();
        new Thread(my, "Thread-C").start();
    }
}

运行结果截图

卖票: ticket = 2
卖票: ticket = 1
卖票: ticket = 0
<span style="color:#ff0000;">卖票: ticket = -1</span>

-------------------------------------------------

     从程序的运行结果中可以发现,程序中加入了延迟操作,所以在运行的最后出现了负数的情况,那么为什么现在会产生这样的问题呢?

       上面程序对于票数的操作步骤如下:

  1. 判断票数是否大于0,大于0则表示还有票可以卖
  2. 如果票数大于0,则将票卖出。

      但是,代码中,在步骤1和步骤2之间加入了延迟操作,那么一个线程就有可能在还没有对票数进行减法操作之前,其他线程就已经将票数减少了,这样一来就会出现票数为负的情况。

             

     如果想解决这样的问题,就必须使用同步。

        所谓同步就是指多个操作在同一个时间段内只能有一个线程进行,其他线程要等待此线程完成之后才可以继续执行,如下图所示:

   


      9.6.2 使用同步解决问题

         解决资源共享的同步操作,可以使用同步代码块和同步方法两种方式完成。

  •  同步代码块

所谓的代码块就是指使用“{}”括起来的一段代码,根据其位置和声明的不同,可以分为普通代码块、构造块、静态块3种,如果在代码块前面加上synchronized关键字,则此代码块就称为同步代码块。  其格式如下

synchronized(同步对象){
      需要同步的代码;
}

*******从上面的格式可以看出,在使用同步代码块时必须制定一个同步对象,但一般都将当前对象(this)设置成同步对象!!!*******

范例:使用同步代码块解决的同步问题

class MyThread implements Runnable{
    private int ticket = 5;
    public void run(){
        for (int i = 0; i < 7; i++){
        	synchronized(this){
            	if (ticket > 0){
                	try{
                    	Thread.sleep(500);
                	}catch(InterruptedException e){
                    	e.printStackTrace();
                	}
                    System.out.println("卖票: ticket = " + ticket--);
            	}
        	}
        }
    }
}
public class SyncDemo01{
    public static void main(String args[]){
        MyThread my = new MyThread();
        new Thread(my, "Thread-A").start();
        new Thread(my, "Thread-B").start();
        new Thread(my, "Thread-C").start();
    }
}

运行结果:

-------------------------------------------------
卖票: ticket = 5
卖票: ticket = 4
卖票: ticket = 3
卖票: ticket = 2
卖票: ticket = 1
-------------------------------------------------

          将上面代码的取值和修改值的操作代码进行了同步,所以不会再出现卖出票为负数的情况了。

  •  同步方法

               除了可以将需要的代码设置成同步代码块外,也可以使用synchronized关键字将一个方法声明成同步方法。其格式如下:

synchronized 方法返回值  方法名称(参数列表){
    方法体;
}

范例: 使用同步方法解决卖票出负数的情况

class MyThread implements Runnable{
    private int ticket = 5;
    public void run(){
        for (int i = 0; i < 7; i++){
        	this.sale();
        }
    }
    private synchronized void sale(){
        if (ticket > 0){
            try{
                Thread.sleep(500);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            System.out.println("卖票:ticket = " + ticket--);
        }
    }
}
public class SyncDemo02{
    public static void main(String args[]){
        MyThread my = new MyThread();
        new Thread(my, "Thread-A").start();
        new Thread(my, "Thread-B").start();
        new Thread(my, "Thread-C").start();
    }
}

运行结果如下,与上面同步代码块实现了完全相同的功能。

-------------------------------------------------
卖票:ticket = 5
卖票:ticket = 4
卖票:ticket = 3
卖票:ticket = 2
卖票:ticket = 1
-------------------------------------------------

  • 方法定义的完整格式

访问权限  {public|default|protected|private} [final] [static] [synchronized] 返回值类型|void  方法名称(参数类型 参数名称,…) [throws Exception1, Exception2] {

      [return [返回值|返回调用处]]

}

      9.6.3 死锁

           同步可以保证资源共享操作的正确性,但是过多同步也会产生问题。例如, 现在张三想要李四的画,李四想要张三的书;张三对李四说“把你的画给我,我就给你书”,李四也对张三说了:“把你的书给我,我就给你画”,此时,张三在等着李四的答复,而李四也在等着张三的答复;那么这样下去最终结果就是,张三得不到李四的画,李四也得不到张三的书。这就是死锁的概念!!

          所谓死锁就是指两个线程都在等待彼此先完成,造成了程序的停滞,一般程序的死锁都是在程序运行时出现的。

范例:死锁

class Zhangsan{
    public void say(){
        System.out.println(" 张三对李四说:“你给我画,我就把书给你。”");
    }
    public void get(){
        System.out.println("张三得到画了!!");
    }
}

class Lisi{
    public void say(){
        System.out.println("李四对张三说:“你给我书,我就把画给你。”");
    }
    public void get(){
        System.out.println("李四得到书了!!");
    }
}
public class ThreadDeadLock implements Runnable{
    private static Zhangsan zs = new Zhangsan();
    private static Lisi ls = new Lisi();
    private boolean flag = false;   //标记,用于判断哪个对象先执行
    
    public void run(){
        if(flag){
            synchronized (zs){
                zs.say();
                try{
                    Thread.sleep(500);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                synchronized(ls){
                    zs.get();
                }
            }
        }else{
            synchronized(ls){
                ls.say();
                try{
                    Thread.sleep(500);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                synchronized(zs){
                    ls.get();
                }
            }
        }
    }
    public static void main(String args[]){
        ThreadDeadLock t1 = new ThreadDeadLock();
        ThreadDeadLock t2 = new ThreadDeadLock();
        t1.flag = true;
        t2.flag = false;
        new Thread(t1, "Thread-A").start();
        new Thread(t2, "Thread-B").start();
    }
}


程序运行结果:

-------------------------------------------------
 张三对李四说:“你给我画,我就把书给你。”
李四对张三说:“你给我书,我就把画给你。”

[以下代码不再执行,程序进入死锁状态]
    从程序的运行结果中可以发现,两个线程都在彼此等着对方的执行完成,这样,程序就无法向下继续执行,从而造成了死锁的现象。

  • 关于同步与死锁, 多个线程共享同一资源时需要进行同步,以保证资源操作的完整性;但是过多的同步就有可能产生死锁。。。


关于线程的死锁、互锁、互斥锁, 以及它们产生的原因、排错方法等等要自己查资料看看!!!!!!!!!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕业设计,基于SpringBoot+Vue+MySQL开发的体育馆管理系统,源码+数据库+毕业论文+视频演示 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本体育馆管理系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达到事半功倍的效果。此体育馆管理系统利用当下成熟完善的SpringBoot框架,使用跨平台的可开发大型商业网站的Java语言,以及最受欢迎的RDBMS应用软件之一的Mysql数据库进行程序开发。实现了用户在线选择试题并完成答题,在线查看考核分数。管理员管理收货地址管理、购物车管理、场地管理、场地订单管理、字典管理、赛事管理、赛事收藏管理、赛事评价管理、赛事订单管理、商品管理、商品收藏管理、商品评价管理、商品订单管理、用户管理、管理员管理等功能。体育馆管理系统的开发根据操作人员需要设计的界面简洁美观,在功能模块布局上跟同类型网站保持一致,程序在实现基本要求功能时,也为数据信息面临的安全问题提供了一些实用的解决方案。可以该程序在帮助管理者高效率地处理工作事务的同时,也实现了数据信息的整体化,规范化与自动化。 关键词:体育馆管理系统;SpringBoot框架;Mysql;自动化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值