上文创建多线程买票的例子中注释会出现错票、重票的问题,本文来讲讲如何解决此问题。本文例子:利用多线程模拟 3 个窗口卖票

实现Runnable接口

public class TestThread2 {public static void main(String [] args){Window window=new Window();Thread thread1=new Thread(window,"窗口一");Thread thread2=new Thread(window,"窗口二");Thread thread3=new Thread(window,"窗口三");thread1.start();thread2.start();thread3.start();}
}class Window implements  Runnable{int ticket=50;@Overridepublic void run(){while (true){if(ticket > 0){try {Thread.currentThread().sleep(100);//模拟卖票需要一定的时间} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);}else {break;}}}
}

运行结果:

窗口二售票,票号为:13
窗口三售票,票号为:12
窗口一售票,票号为:11
窗口二售票,票号为:10
窗口一售票,票号为:10
窗口三售票,票号为:10
窗口三售票,票号为:9
窗口一售票,票号为:8
窗口二售票,票号为:7
窗口三售票,票号为:6
窗口一售票,票号为:5
窗口二售票,票号为:4
窗口三售票,票号为:3
窗口一售票,票号为:2
窗口二售票,票号为:1
窗口三售票,票号为:0
窗口一售票,票号为:-1

结果分析:这里出现了票数为0和负数还有重票的情况,这在现实生活中肯定是不存在的,那么为什么会出现这样的情况呢?

  当票号为10时:A线程、B线程、C线程同时进入到if(ticket > 0)的代码块中,A线程已经执行了打印输出语句,但是还没有做ticket--操作;
  这时B线程就开始执行了打印操作,那么就会出现两个线程打印票数一样,即卖的是同一张票
  当票号为1时:A线程、B线程,C线程同时进入到if(ticket > 0)的代码块中,A线程执行了打印语句,并且已经做完了ticket--操作,则此时ticket=0;
  B线程再打印时就出现了0的情况,同理C线程打印就会出现-1的情况。

解决办法:即我们不能同时让超过两个以上的线程进入到 if(ticket > 0)的代码块中,不然就会出现上述的错误。必须让一个线程操作共享数据完毕以后,其他线程才有机会参与共享数据的操作。我们可以通过以下两个办法来解决:

  1、使用 同步代码块

  2、使用 同步方法

使用 同步代码块

synchronized(同步监视器){//需要被同步的代码块(即为操作共享数据的代码)
}

  同步监视器:由任意一个类的对象来充当,哪个线程获取此监视器,谁就执行大括号里被同步的代码。俗称:锁

  要求:1、所有的线程必须公用同一把锁!不能相对于线程是变化的对象;

        2、并且只需锁住操作共享数据的代码,锁多了或少了都不行;

实例:

1、实现的方式

public class TestWindow {public static void main(String [] args){Window1 window=new Window1();Thread thread1=new Thread(window,"窗口一");Thread thread2=new Thread(window,"窗口二");Thread thread3=new Thread(window,"窗口三");thread1.start();thread2.start();thread3.start();}
}class Window1 implements  Runnable{int ticket=100;//共享数据
    @Overridepublic void  run(){while (true){synchronized (this){//this表示当前对象,此时表示创建的 windowif(ticket > 0){try {//模拟卖票需要一定的时间Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);}}}}
}

注意:在实现的方式中,考虑同步的话,可以使用this充当锁,但在继承的方式中,会创建多个对象,慎用this

2、继承的方式

public class TestWindow1 {public static void main(String [] args){Window2 window1=new Window2();Window2 window2=new Window2();window1.start();window2.start();}
}class Window2 extends Thread{static int ticket=100;//共享数据;注意声明为 static,表示几个窗口共享static Object object=new Object();//用static 可以表示唯一
    @Overridepublic void  run(){while (true){//synchronized (this){//this表示当前对象,此时表示创建的 window1和window2synchronized (object){//锁必须是唯一,不能每个线程都使用自己的一把锁if(ticket > 0){try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);}}}}
}

注意:1、继承的方式会创建多个实例,所以共享资源需要用static来修饰,表示共享

      2、继承的方式会创建多个实例,所以 this 表示不同的实例对象,这里表示widow1和window2,所以不能使用this当锁,此时可以定义一个 static 修饰的对象当锁

使用 同步方法

语法:即用  synchronized  关键字修饰方法

将操作共享数据的方法声明为synchronized。即此方法为同步方法,能够保证当其中一个线程执行此方法时,其他线程再外等待直至此线程执行完此方法。
注意:同步方法的锁:this

实例:

1、实现的方式

public class TestWindow2 {public static void main(String [] args){Window3 window=new Window3();Thread thread1=new Thread(window,"窗口一");Thread thread2=new Thread(window,"窗口二");Thread thread3=new Thread(window,"窗口三");thread1.start();thread2.start();thread3.start();}
}class Window3 implements  Runnable{int ticket=100;//共享数据
    @Overridepublic void  run(){while (true){show();}}public synchronized void show(){//this充当锁,此时表示创建的 window;// 如果用继承的方式,使用同步方法,这里表示创建的 window1和window2,继承的方式不要使用同步方法if(ticket > 0){try {Thread.currentThread().sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);}}
}

注意:1、synchronized 的锁为this,这里表示创建的对象实例window;

     2、继承的时候t this 表示创建的window1和window2,继承的方式不要使用同步方法。

转载于:https://www.cnblogs.com/java-chen-hao/p/9896254.html

Java 多线程(二)—— 线程的同步相关推荐

  1. java多线程之线程的同步与锁定(转)

    一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. 例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. publicc ...

  2. Java多线程之线程同步机制(锁,线程池等等)

    Java多线程之线程同步机制 一.概念 1.并发 2.起因 3.缺点 二.三大不安全案例 1.样例一(模拟买票场景) 2.样例二(模拟取钱场景) 3.样例三(模拟集合) 三.同步方法及同步块 1.同步 ...

  3. Java多线程02(线程安全、线程同步、等待唤醒机制)

    Java多线程2(线程安全.线程同步.等待唤醒机制.单例设计模式) 1.线程安全 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量 ...

  4. Java多线程:线程安全和非线程安全的集合对象

    转载自  Java多线程:线程安全和非线程安全的集合对象 一.概念: 线程安全:就是当多线程访问时,采用了加锁的机制:即当一个线程访问该类的某个数据时,会对这个数据进行保护,其他线程不能对其访问,直到 ...

  5. java多线程方式轮询,深入理解JAVA多线程之线程间的通信方式

    一,介绍 本总结我对于JAVA多线程中线程之间的通信方式的理解,主要以代码结合文字的方式来讨论线程间的通信,故摘抄了书中的一些示例代码. 二,线程间的通信方式 ①同步 这里讲的同步是指多个线程通过sy ...

  6. 初学Java多线程:线程简介

     Java多线程初学者指南系列教程http://developer.51cto.com/art/200911/162925.htm 初学Java多线程:线程简介 2009-06-29 17:49 ...

  7. java多线程及线程池使用

    Java多线程及线程池的使用 Java多线程 一.Java多线程涉及的包和类 二.Java创建多线程的方式 三.Java线程池 1. 创建线程池ThreadPoolExecutor的7个参数 2. 线 ...

  8. Java多线程之线程池配置合理线程数

    Java多线程之线程池配置合理线程数 目录 代码查看公司服务器或阿里云是几核的 合理线程数配置之CPU密集型 合理线程数配置之IO密集型 1. 代码查看公司服务器或阿里云是几核的 要合理配置线程数首先 ...

  9. java多线程与线程间通信

    转自(http://blog.csdn.net/jerrying0203/article/details/45563947) 本文学习并总结java多线程与线程间通信的原理和方法,内容涉及java线程 ...

  10. delphiXE关于线程和多线程、线程的同步与异步执行

    delphiXE关于线程和多线程.线程的同步与异步执行 一.最好的参照案例 {$BDS}\source\fmx\FMX.Helpers.Android.pas 如下四个独立方法: type   TMe ...

最新文章

  1. SAP库存表之间的逻辑关系
  2. J. Cheminform. | 基于化学基因组学中深度和浅层学习预测药物特异性
  3. SAP PM创建/计划MO
  4. (0071)iOS开发之Category VS Extension区别理解
  5. Ubuntu系统环境变量配置文件(转)
  6. 二维数组删除_「leetcode」数组:总结篇!(一文搞懂数组题目)
  7. java培训每日总结,这是一份1000多字的Java培训总结,字字珠玑
  8. java jdom dom4j_实例分析jdom和dom4j的使用和区别
  9. html 转盘抽奖开发,html 大转盘抽奖
  10. 服务器在美国怎样网页加速,美国服务器如何优化网站访问速度
  11. Android 自定义View:实现一个 FM 刻度尺
  12. 科研论文中的图片如何保证高清
  13. 【软件之道】Word模板的制作及使用
  14. linux intel wifi驱动,ubuntu 8.04下面 Intel WIFI link 5100无线网卡驱动安装
  15. 基于ssm医药药品管理系统
  16. 扎克伯格 java_程序员的5种级别,扎克伯格比尔盖茨并非最高等级!
  17. 亚马逊雨林大火为何上不了热搜?我们都被某些媒体给忽悠了!
  18. spring源码故事-面筋哥IoC容器的一天(上)
  19. android工程文件assts,Android初始化FaceSDK报错
  20. 江苏机器人竞赛南航_南航金城学院学子在第十届江苏省机器人大赛中获佳绩

热门文章

  1. ajax回显500因为#与$的原因
  2. python怎么识别log函数_python中对数函数怎么表示
  3. ROS入门笔记(九):编写ROS的第一个程序hello world(重点)
  4. 【pytest】Hook 方法之 pytest_addoption :注册命令行参数
  5. mysql 过滤单引号_python实现mysql的单引号字符串过滤方法
  6. 控制使用期限_学校厨房设备延长其使用寿命的方法有哪些呢?
  7. 利用insert、update和delete命令可以同时对多个表进行操作_使用自然语言进行程序合成...
  8. 手机端html5 面试,今日头条 张祖俭 - H5动画在移动平台上的性能优化实践
  9. java中set怎么建int型_使用Array.setInt来填充Java中的数组
  10. 《天天数学》连载40:二月九日