博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java 多线程(二)—— 线程的同步
阅读量:6161 次
发布时间:2019-06-21

本文共 4901 字,大约阅读时间需要 16 分钟。

实现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;    @Override    public 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;//共享数据    @Override    public void  run(){        while (true){            synchronized (this){
//this表示当前对象,此时表示创建的 window if(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 可以表示唯一    @Override    public void  run(){        while (true){            //synchronized (this){
//this表示当前对象,此时表示创建的 window1和window2 synchronized (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;//共享数据    @Override    public 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

你可能感兴趣的文章
Qt Style Sheet实践(四):行文本编辑框QLineEdit及自动补全
查看>>
[物理学与PDEs]第3章习题1 只有一个非零分量的磁场
查看>>
onInterceptTouchEvent和onTouchEvent调用时序
查看>>
android防止内存溢出浅析
查看>>
4.3.3版本之引擎bug
查看>>
SQL Server表分区详解
查看>>
STM32启动过程--启动文件--分析
查看>>
垂死挣扎还是涅槃重生 -- Delphi XE5 公布会归来感想
查看>>
淘宝的几个架构图
查看>>
linux后台运行程序
查看>>
Python异步IO --- 轻松管理10k+并发连接
查看>>
Oracle中drop user和drop user cascade的区别
查看>>
登记申请汇总
查看>>
Android Jni调用浅述
查看>>
CodeCombat森林关卡Python代码
查看>>
(二)Spring Boot 起步入门(翻译自Spring Boot官方教程文档)1.5.9.RELEASE
查看>>
Shell基础之-正则表达式
查看>>
JavaScript异步之Generator、async、await
查看>>
讲讲吸顶效果与react-sticky
查看>>
c++面向对象的一些问题1 0
查看>>