创建多线程以及多线程的应用(火车售票)---线程的同步机制,线程的安全问题

it2022-05-05  159

一、继承的方式实现多窗口售票:

package com.atguigu.Thread;

 

//题目要求:模拟火车站售票窗口,开启三个窗口售票,总票数为100

 

class Window extends Thread{

    static int ticket=100;

   

    public void run() {

        while(true) {

            if(ticket>0) {

                System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);

            }else {

                break;

            }

        }

    }

}

 

public class TestWindow {

    public static void main(String[] args) {

        Window w1=new Window();

        Window w2=new Window();

        Window w3=new Window();

       

        w1.setName("售票窗口1");

        w2.setName("售票窗口2");

        w3.setName("售票窗口3");

       

        w1.start();

        w2.start();

        w3.start();

       

    }

}

 

 

二、实现的方式创建多线程:

   要想启动一个多线程必须调用start( )方法。

1、创建多线程的方式二:通过实现的方式。(implements)

  (1)、创建一个实现了Runnable接口的类

  (2)、实现接口的抽象方法

  (3)、创建一个Runnable接口实现类的对象

  (4)、将此对象作为形参传递给Thread类的构造器中,创建Thread类的对象,此对象即为一个线程

  (5)、调用start()方法,启动线程并执行run()

  (6)、如果想有多个线程,就用Thread类构造多个带形参的对象

2、实现的方式创建多线程:

实现的方式创建多线程代码:

package com.atguigu.Thread; //创建多线程的方式二:通过实现的方式。(implements) /*  * 1、创建一个实现了Runnable接口的类  * 2、实现接口的抽象方法  * 3、创建一个Runnable接口实现类的对象  * 4、将此对象作为形参传递给Thread类的构造器中,创建Thread类的对象,此对象即为一个线程  * 5、调用start()方法,启动线程并执行run()  * 6、如果想有多个线程,就用Thread类构造多个带形参的对象  * */ //1、创建一个实现了Runnable接口的类 class PrintNum1 implements Runnable{ //  2、实现接口的抽象方法     public void run() {         //子线程执行的代码         for(int i=0;i<=100;i++) {             if(i%2==0) {                 System.out.println(Thread.currentThread().getName()+":"+i);             }         }     } } public class TestThread {     public static void main(String[] args) { //      3、创建一个Runnable接口实现类的对象         PrintNum1 p=new PrintNum1(); //      p.start(); //      p.run();         //要想启动一个多线程,必须调用start()方法 //      4、将此对象作为形参传递给Thread类的构造器中,创建Thread类的对象,此对象即为一个线程         Thread t1=new Thread(p); //      5、调用start()方法,启动线程并执行run()         t1.start();//启动线程,执行Thread对象生成时构造器形参的对象的run()方法。             //再创建一个线程         Thread t2=new Thread(p);         t2.start();     } }

Thread构造方法中传了一个实现Runnable接口的参数。

 

对比继承的方式 vs 实现的方式:

Gdhs 1、联系:public class Thread implements Runnable

都是2、哪个方式好?实现的方式优于继承的方式

Why?  ①、避免了java单继承的局限性

            ②、如果多个线程要操作同一份资源(或数据),要适合使用实现的方式。

 

3、用实现的方式进行窗口售票:

package com.atguigu.Thread; //使用实现的方式进行售票窗口的实现 class Window1 implements Runnable{     int ticket=100; //在重写的run()方法之外。     @Override     public void run() {         // TODO Auto-generated method stub         while(true) {             if(ticket>0) {                 System.out.println(Thread.currentThread().getName()+"售票,票号:"+ticket--);             }else {                 break;             }         }     } } public class TestWindow1 {     public static void main(String[] args) {         Window1 w=new Window1();         Thread t1=new Thread(w);//形参w为实现Runnable接口的Window1类的对象         t1.start();         Thread t2=new Thread(w);         t2.start();         Thread t3=new Thread(w);         t3.start();     } }

 

三、使用多线程的优点_线程的生命周期:

 

 

四、线程的同步机制_同步代码块:

一个程序运行无数次结果没有错,并不代表程序不存在隐患。程序运行结果有没有错与程序存不存在隐患不是一回事。

无论是用实现的方式的线程还是用继承的方式的线程;要用同步机制一定是只能有一个锁,synchronized中包含的是操作共享数据的代码块。

1、存在线程的安全问题:打印车票时,会出现重票、错票。

 1、线程安全问题存在的原因?

   由于一个线程在操作共享数据的过程中未执行完的情况下,另外的线程参与进来,共享数据存在了安全问题。

 

 2、如何解决线程的安全问题?

   必须让一个线程操纵共享数据完毕后,其他线程才有机会参与共享数据的操作。

 3java如何实现线程的安全,线程的同步机制

      方式一:同步代码块

 

      方式二:同步方法

 

 

小括号内可以实任何一个类的对象。

synchronized (同步监视器) {//经过synchronized处理以后此线程就是安全的

   //操作共享数据的代码块

}

同步监视器是一把锁,必须保证多个对象共用同一把锁。

如上图:(1)、obj为任何一个类的对象,起到监视器的作用;(2)、当没有进入的时候,锁obj控制绿灯亮;(3)、当若t1进入的时候,锁obj给t1,t1进入后锁门,obj控制门红灯亮,让t1在内部运行;即使中途sleep( )也没事,直到全运行完后打开门锁出来,锁obj再控制门绿灯亮;(4)、再一次新的抢房间……内容同上。

2、实现的方式实现线程---同步机制:

package com.atguigu.Thread; //模拟火车站售票窗口,开启三个窗口售票,总票数为100张 /* * 存在线程的安全问题:打印车票时,会出现重票、错票。 * 1、线程安全问题存在的原因? *   由于一个线程在操作共享数据的过程中未执行完的情况下,另外的线程参与进来,共享数据存在了安全问题。 * * 2、如何解决线程的安全问题? *   必须让一个线程操纵共享数据完毕后,其他线程才有机会参与共享数据的操作。 * * 3、java如何实现线程的安全,线程的同步机制 *       方式一:同步代码块 *       synchronized(同步监视器){ *           //需要被同步的代码块(即为操作共享数据的代码) *       } *       1、共享数据:多个线程共同操作的同一个数据(变量)。 *       2、同步监视器:由任何一个类的对象来充当。哪个线程获取此监视器,谁就执行大括号里被同步的代码。 *       要求:所有的线程必须共用同一把锁!!!      * 方式二:同步方法 * */ class Window2 implements Runnable{     /*定义的变量与类对象都必须在重写的run方法外。*/     int ticket=100; //(ticket为共享数据)在重写的run()方法之外。 //  Object obj=new Object();     @Override     public void run() {         // TODO Auto-generated method stub         while(true) { //          Object obj=new Object();在run内部定义类的对象是错误的             //方式一、使用synchronized(){ }来保证 线程安全。             synchronized (/*obj*/this/*也可以*/) {//obj可以是任何类的对象。                 //this表示当前对象的,本题目为w                 if(ticket>0) {                    /*  不加被注释的部分运行100次结果不错,但并不代表程序不存在隐患;加了只是把错误的几率放大了。 */                                    try {                                        Thread.currentThread().sleep(10);                                    } catch (InterruptedException e) {                                        // TODO Auto-generated catch block                                        e.printStackTrace();                                    }                                    System.out.println(Thread.currentThread().getName()+"售票,票号:"+ticket--);                                }             }                     }     } } public class TestWindow2 {     public static void main(String[] args) {         Window2 w=new Window2();         Thread t1=new Thread(w);//形参w为实现Runnable接口的Window1类的对象         t1.start();         Thread t2=new Thread(w);         t2.start();         Thread t3=new Thread(w);         t3.start();     } }

线程同步问题:锁必须是唯一的。在实现的方式中,考虑同步的话,可以使用this来充当锁。但是在继承的方式中,考虑同步的话,可能会有多个对象,慎用this来充当锁,会有错。

3、继承的方式实现线程---同步机制问题:

(1)、错误的:不能实现同步机制。

package com.atguigu.Thread; //模拟火车站售票窗口,开启三个窗口售票,总票数为100张 /*  * 存在线程的安全问题:打印车票时,会出现重票、错票。  * */ class Window3 extends Thread{     static int ticket=100;          public void run() {         while(true) {             synchronized (this) {//用同步包裹起来 /*此处使用this结果错误,因为现在是继承实现的线程,创建了很多对象,this表示w1\w2\w3;还不是一把锁。*/                 if (ticket > 0) {                    try {                        Thread.currentThread().sleep(10);                    } catch (InterruptedException e) {                        // TODO Auto-generated catch block                        e.printStackTrace();                    }                    System.out.println(Thread.currentThread().getName() + "售票,票号为:" + ticket--);                 } else {                    break;                 }             }                }     } } public class TestWindow3 {     public static void main(String[] args) { /*      因为有三个对象,不能实现锁是唯一的。    */         Window3 w1=new Window3();         Window3 w2=new Window3();         Window3 w3=new Window3();                 w1.setName("售票窗口1");         w2.setName("售票窗口2");         w3.setName("售票窗口3");                 w1.start();         w2.start();         w3.start();             } }

(2)、正确的:可以实现同步机制。(加了红底黄字的部分正确了)

package com.atguigu.Thread; //模拟火车站售票窗口,开启三个窗口售票,总票数为100张 /*  * 存在线程的安全问题:打印车票时,会出现重票、错票。  * */ class Window3 extends Thread{     static int ticket=100;      static Object obj=new Object();     public void run() {         while(true) { //          synchronized (this) {//用同步包裹起来             synchronized(obj) { /*此处使用this结果错误,因为现在是继承实现的线程,创建了很多对象,this表示w1\w2\w3;还不是一把锁。*/                 if (ticket > 0) {                     try {                        Thread.currentThread().sleep(10);                    } catch (InterruptedException e) {                        // TODO Auto-generated catch block                        e.printStackTrace();                    }                    System.out.println(Thread.currentThread().getName() + "售票,票号为:" + ticket--);                 } else {                    break;                 }             }         }     } } public class TestWindow3 {     public static void main(String[] args) {         Window3 w1=new Window3();         Window3 w2=new Window3();         Window3 w3=new Window3();                 w1.setName("售票窗口1");         w2.setName("售票窗口2");         w3.setName("售票窗口3");                 w1.start();         w2.start();         w3.start();             } }

3、同步方法-不可以用于用继承的方式来实现线程的方法中。

 

 

 

 


最新回复(0)