zoukankan      html  css  js  c++  java
  • Java 多线程(二)—— 线程的同步

    实现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,继承的方式不要使用同步方法。

  • 相关阅读:
    Swift _ OC _ 混编
    CoreAnimation 寄宿图
    CoreAnimation 开篇
    iOS 杂笔-26(苹果禁用热更新)
    Swift_TableView(delegate,dataSource,prefetchDataSource 详解)
    Swift_ScrollView _ API详解
    插入排序_c++
    选择排序_c++
    冒泡排序_c++
    Swift_协议
  • 原文地址:https://www.cnblogs.com/java-chen-hao/p/9896254.html
Copyright © 2011-2022 走看看