zoukankan      html  css  js  c++  java
  • Java——多线程小例子

    某电影院正在上映《速度与激情7》,共有100张票。它又三个售票窗口正在售票。请设计一个应用程序来模拟该电影院的售票
    两种方式实现:继承Thread类;实现Runable接口

    两种实现方式的对比:
    方法1:需要多个对象
    方法2:只需要新建一个对象即可,放入三个不同线程;实现了数据和业务模型的分离

    该程序跟实际情况还有一些距离,因为实际情况下,售票数据通过网络传输,总是存在一些延迟的情况。所以在真正售出一张票后,需要一段时间,才可以真正去修改剩余票数。
    继续更新我们的代码:每次买票延迟100ms,后再去修改剩余票数

    问题1
    相同的票 卖了多次
    问题2
    出现了负数的票数
    注意:线程安全问题在理想情况下不易出现,但是一旦出现,影响将非常大

    如何解决线程安全问题:
    分析出问题的原因:多线程,共享数据,操作共享数据并非原子操作(是否有多条语句)
    解法:将出问题的原因或条件破坏掉

    解决问题: 
    同步代码块
    格式:
    Synchronized(对象){   //让这里的代码变成一个原子操作,不会再代码块的某一个地方切换到其他线程;对象可以是Object
            需同步代码块
    }
    同步代码块的对象可以是?
    需要同步的代码块是?


    MainClass.java
    package com.java.ticket;
    public class MainClass {

            public static void main(String[] args) {
                    //方法一  
                    Window window1=new Window(100);
                    Window window2=new Window(100);
                    Window window3=new Window(100);
                    window1.start();
                    window2.start();
                    window3.start();

                    //方法二  sellTicket共享,就不用把ticket声明成静态的 ; 同样,没有互斥
    /*             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();*/
            }
    }
               
    Window.java SellTicket.java
    package com.java.ticket;
    public class Window extends Thread {
            static int ticket;
            Object object=new Object();
            public void run() {
                    super.run();
                    while(ticket>0){
                            //卖票
                            //方式一:
                    /*     synchronized (object){
                            sell();
                            }*/
                            //方式二
                            sell();
                    }
            }
    /*          void sell(){
                    if(ticket>0){
                            System.out.println(getName()+"卖出第 "+ticket--+" 张票");  //执行这条语句的同时,共享数据自减
                 }
            }*/
            public synchronized void sell(){    //最佳
                    if(ticket>0){
                            System.out.println(getName()+"卖出第 "+ticket--+" 张票");  //执行这条语句的同时,共享数据自减,属于一个原子操作
                    }
            }
            public Window(int ticket) {
                    super();
                    this.ticket = ticket;
            }
    }
    package com.java.ticket;
    public class SellTicket implements Runnable {
            Object object=new Object();
            int ticket=100;
            public void run() {
                     //synchronized(object){   //让这里的代码变成一个原子操作,不会再代码块的某一个地方挂起切换到其他线程
                            //同步语句在这里就只有一个窗口卖票,其他窗口无法打断
                            while(ticket>0){
                              //出现卖负票是因为在这里挂起
                              //synchronized(object){    //同步语句加在这里,会出现卖负票
                                    synchronized(object){  //括号里(new Object)就不行,会出现多张相同的票;因为每个线程调用run方法,都会new一个对象,多个线程就会有多个锁
                                            //这里object也可以是this,因为三个线程都是用同一个对象来初始化的,所以obj也不用声明成静态的
                                            if(ticket>0){
                                                    System.out.println(Thread.currentThread().getName()+"卖出第 "+ticket--+" 张票");
                                                    //ticket--;
                                            }                                       
                          }                       
                      }
            }

    }



    test1.java test2.java
    package com.java.threadExercise;
    import java.util.Random;
    public class test2 {
            /**
             * 创建一个任务,它将睡眠1到10秒之间的随机数量的时间,
             * 然后显示它的睡眠时间并退出。创建并运行多个这种任务。
             */
            public static void main(String[] args) {
                    Thread1 t1=new Thread1("No.1");
                    Thread1 t2=new Thread1("No.2");
                    Thread1 t3=new Thread1("No.3");
                    t1.start();
                    t2.start();
                    try {
                            t2.join();  //等待线程t2执行完毕才会执行下面的语句;开始t3线程
                    } catch (InterruptedException e) {
                            e.printStackTrace();
                    }
                    t3.start();
            }
    }
    class Thread1 extends Thread {
            public Thread1(String string){
                    super(string);
            }
            public void run(){
                    super.run();
                    Random r1=new Random();
                    int i=r1.nextInt(11)*1000;   //随机生成10以内的数,后面sleep里面要是毫秒,所以乘以1000
                    try {
                            sleep(i);
                    } catch (InterruptedException e) {
                            e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+" 睡眠时间: "+i/1000);

            }
    }

    package com.java.threadExercise;
    import java.util.Random;
    public class test3 {
            /**
             * 将所有线程修改成守护线程,并验证一旦main函数退出,程序立刻终止。
             */
            public static void main(String[] args) {
                    Thread2 t1=new Thread2("No.1");
                    Thread2 t2=new Thread2("No.2");
                    Thread2 t3=new Thread2("No.3");
                    t1.setDaemon(true);
                    t2.setDaemon(true);
                    t3.setDaemon(true);
                    t1.start();
                    t2.start();
                    t3.start();
                    System.out.println("主线程结束。。。。");
            }
    }
    class Thread2 extends Thread {
            public Thread2(String string){
                    super(string);
            }
            public void run(){
                    super.run();
                    Random r1=new Random();
                    int i=r1.nextInt(11)*1000;   //随机生成10以内的数,后面sleep里面要是毫秒,所以乘以1000
                    try {
                            sleep(i);
                    } catch (InterruptedException e) {
                            e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+" 睡眠时间: "+i/1000);

            }
    }

    //主线程结束,守护线程也就结束了;

    test3.java //test3.java
    public class test4 {
            public static void main(String[] args) {
                    Scanner in=new Scanner(System.in);
                    int num=in.nextInt();
                    Object obj=new Object();
                    int x=in.nextInt();
                    Thread3.getValue(num, x);
                    for(int i=0;i<num;++i){
                            new Thread3("第"+(i+1)+"个下载线程").start();      
                    }
            }
    }
    class Thread3 extends Thread{
            public static int Num;
            public static int X;
            static Object obj=new Object();
            public Thread3(String string){
                    super(string);
            }
            public static void getValue(int num,int x){
                    Num=num;
                    X=x;
            }
            public void run() {
                    super.run();
                    while(X>=0){
                            synchronized(obj){
                      //注意 ,这里要Obj必须是同一个对象才能实现加锁,不然会出现多把锁,失去意义;所以如果有多个线程对象,obj要声明成静态的              
                                    if(X<=0){
                                            System.out.println("下载完成");
                                            System.exit(0);
                                    }
                                    System.out.println(this.getName()+" :剩余"+X+"M未下载");
                                    X--;
                            }
                    }
            }
    }

    public class test4 {
            public static void main(String[] args) {
                    Scanner in=new Scanner(System.in);
                    int num=in.nextInt();
                    Object obj=new Object();
                    int x=in.nextInt();
                    Thread3.getValue(num, x);
                    Thread3 t3=new Thread3();
                    for(int i=0;i<num;++i){
                            new Thread(t3,"第"+(i+1)+"个下载线程").start();   //用同一个对象初始化线程,可以共用数据,下面run方法就可以不用把obj定义成static,因为所有线程都共用数据
                    }
            }
    }
    class Thread3 implements Runnable{
            public static int Num;
            public static int X;
           Object obj=new Object();   //不用定义成静态的
            public static void getValue(int num,int x){
                    Num=num;
                    X=x;
            }
            public void run() {
                    while(X>=0){
                            synchronized(obj){
                      //this也可以,因为是一个对象初始化的线程
                                    if(X<=0){
                                            System.out.println("下载完成");
                                            System.exit(0);
                                    }
         System.out.println(Thread.currentThread().getName()+" :剩余"+X+"M未下载");
                                    X--;
                            }
                    }
            }
    }














  • 相关阅读:
    beta冲刺————第二天(2/5)
    beta冲刺————第一天(1/5)
    个人作业——软件产品案例分析
    c++第七次作业____最后的总结
    计算器图形界面
    object-oriented second work
    第五次作业(文件处理)
    object-oriented first work
    第四次作业--计算器简单计算
    新的Calculator的规范作业
  • 原文地址:https://www.cnblogs.com/meihao1203/p/9181772.html
Copyright © 2011-2022 走看看