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 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();*/

        }

}

java多线程抢票代码 java多线程买票例子_d3

           

java多线程抢票代码 java多线程买票例子_System_02

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){

//执行这条语句的同时,共享数据自减,属于一个原子操作

                }

        }

        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执行完毕才会执行下面的语句;开始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);


        }

}




java多线程抢票代码 java多线程买票例子_java_03

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);


        }

}



java多线程抢票代码 java多线程买票例子_java_04

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



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--;

                        }

                }

        }

}



java多线程抢票代码 java多线程买票例子_System_05




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){

//用同一个对象初始化线程,可以共用数据,下面run方法就可以不用把obj定义成static,因为所有线程都共用数据

                }

        }

}

class Thread3 implements Runnable{

        public static int Num;

        public static int X;

  //不用定义成静态的

        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--;

                        }

                }

        }

}




java多线程抢票代码 java多线程买票例子_System_06