zl程序教程

您现在的位置是:首页 >  Java

当前栏目

java中的多线程详解

2023-04-18 15:21:26 时间

一. 线程的创建

线程的创建方式有两种:

一种是继承Thread类,重写run()方法【这里的run()方法只是普通的方法】,在主方法中,创建该类的对象,调用对象的start()方法。

二种是实现Runnable接口,重写run()方法,在主方法中,以该实现类为参数,创建Thread线程,调用Thread的start()方法。

方式一:示例代码如下

 1 public class Thread01 {
 2     public static void main(String[] args) {
 3         Cat cat = new Cat();
 4         System.out.println("main: "+Thread.currentThread().getName());
 5         cat.start();
 6         System.out.println("hello world");
 7     }
 8 }
 9 
10 class Cat extends Thread{
11     private int num = 0;
12     @Override
13     public void run() {
14         while(true){
15             try {
16                 sleep(1000);
17             } catch (InterruptedException e) {
18                 e.printStackTrace();
19             }
20             System.out.println("我的是小猫" + (++num)+"Thread: " + Thread.currentThread().getName());
21             if(num >= 8){
22                 break;
23             }
24         }
25     }
26 }

方式二:示例代码如下

 1 public class Thread03 {
 2     public static void main(String[] args) {
 3         Thread thread = new Thread(new Dog());
 4         thread.start();
 5 
 6     }
 7 }
 8 class Animal_ {}
 9 class Dog extends Animal_ implements Runnable {
10     @Override
11     public void run() {
12         System.out.println("dog");
13     }
14 }

方式二的另一种写法:【匿名内部类作为参数】

 1 public class Thread03 {
 2     public static void main(String[] args) {
 3 
 4         // 使用匿名内部类,实现Runnable接口,重写run()方法,作为参数。
 5         new Thread(new Runnable() {
 6             @Override
 7             public void run() {
 8                 System.out.println("main: " + Thread.currentThread().getName());
 9             }
10         }).start();
14     }
15 }
16 class Animal_ {}
17 class Dog extends Animal_ implements Runnable {
18     @Override
19     public void run() {
20         System.out.println("dog");
21     }
22 }

两种方式的区别,继承Thread类,只能是单一继承,接口可以实现多个,因此遇到已有继承父类的子类,则必须实现Runnable接口,来创建进程。

方式二的模拟代码如下【静态代理模式】:加深对方式二的理解

public class ProxyThreadTest {
    public static void main(String[] args) {
        Cat1 cat1 = new Cat1();
        ProxyThread proxyThread = new ProxyThread(cat1);// 接口的多态
        proxyThread.start();

    }
}
class Animal{}
// 单一继承,若想使用线程,实现Runnable接口.
class Cat1 extends Animal implements Runnable {
    public Cat1() {
    }

    @Override
    public void run() {
        System.out.println("老虎嗷嗷叫");
    }
}
class ProxyThread implements Runnable{
    private Runnable target = null;
    @Override
    public void run() {
        if(target != null){
            target.run(); // 动态绑定
        }
    }

    public ProxyThread(Runnable target) {
        this.target = target;
    }

    public void start(){
        start0();
    }

    private void start0() {
        run();
    }
}

二. 线程的方法

start():开启线程

run():Runnable接口定义的方法,注意run()只是普通的方法,如果想让线程开始运作,应该是调用start()方法

yield():礼让【退让】,在cpu资源不足的情况下,让出自己的cpu和资源

join():插队,强制让调用该方法的线程执行

三. 守护线程

守护线程:主线程执行完毕,守护线程即使没有执行完成,也要结束

示例代码如下:

public class ThreadMethod01 {
    public static void main(String[] args) throws InterruptedException {
        MyDaemon myDaemon = new MyDaemon();
        // 设置守护线程,必须在开启线程之前
        myDaemon.setDaemon(true);
        myDaemon.start();
        for(int i = 0;i < 10;i++){
            System.out.println("宝强辛苦工作");
            Thread.sleep(1000);
        }
    }
}

class MyDaemon extends Thread {
    @Override
    public void run() {
        for(;;){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println(e.getMessage());
            }
            System.out.println("马蓉宋喆聊天...");
        }
    }
}

 四. 线程的生命周期

vip

五. 互斥锁

vip

 vip

vip

 我的理解互斥锁:对象锁和类锁

对象锁:用synchronized修饰普通方法【同步方法】或者在普通方法中修饰代码块【同步代码块】===>【锁在对象上】

类锁:用synchronized修饰静态方法【同步方法】或者在静态方法中修饰代码块【同步代码块】===>【锁在类上】

synchronized不能用来修饰变量。

六. 售票管理

三个窗口同时售票,票总数100张,打印出具体销售过程

1)实现Runnable接口实现

错误写法:

public class SellTicket {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new SellTicketWin());
        Thread thread2 = new Thread(new SellTicketWin());
        Thread thread3 = new Thread(new SellTicketWin());

        thread1.start();
        thread2.start();
        thread3.start();


    }
}

class SellTicketWin extends Thread{
    private int num = 100;
    private boolean loop = true;

    public void sell(){
        if (num <= 0) {
            System.out.println(Thread.currentThread().getName() + "窗口,票卖完了");
            loop = false;
            return;
        }
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            System.out.println(e.getMessage());
        }
        System.out.println(Thread.currentThread().getName() + "窗口卖了一张票,剩余票数: " + (--num));
    }
    @Override
    public void run() {
        while (loop) {
            sell();
        }
    }
}

此时是3个对象,分别对应一个线程,num不是唯一且共享的,不合题意。

正确写法:

SellTicketWin sellTicketWin = new SellTicketWin();
Thread thread1 = new Thread(sellTicketWin);
Thread thread2 = new Thread(sellTicketWin);
Thread thread3 = new Thread(sellTicketWin);

用唯一实现Runnable接口的类对象作为参数,保证num的唯一且共享。

如果不加锁,则运行结果如下:

Thread-0窗口卖了一张票,剩余票数: 99
Thread-1窗口卖了一张票,剩余票数: 97
Thread-2窗口卖了一张票,剩余票数: 98
Thread-2窗口卖了一张票,剩余票数: 96
Thread-0窗口卖了一张票,剩余票数: 95
Thread-1窗口卖了一张票,剩余票数: 94
Thread-2窗口卖了一张票,剩余票数: 93
Thread-0窗口卖了一张票,剩余票数: 91
Thread-1窗口卖了一张票,剩余票数: 92
Thread-0窗口卖了一张票,剩余票数: 90
Thread-1窗口卖了一张票,剩余票数: 89
Thread-2窗口卖了一张票,剩余票数: 88
Thread-0窗口卖了一张票,剩余票数: 87
Thread-1窗口卖了一张票,剩余票数: 86
Thread-2窗口卖了一张票,剩余票数: 85
Thread-0窗口卖了一张票,剩余票数: 84
Thread-1窗口卖了一张票,剩余票数: 83
Thread-2窗口卖了一张票,剩余票数: 82
Thread-0窗口卖了一张票,剩余票数: 80
Thread-1窗口卖了一张票,剩余票数: 81
Thread-2窗口卖了一张票,剩余票数: 79
Thread-0窗口卖了一张票,剩余票数: 77
Thread-1窗口卖了一张票,剩余票数: 78
Thread-2窗口卖了一张票,剩余票数: 76
Thread-0窗口卖了一张票,剩余票数: 75
Thread-1窗口卖了一张票,剩余票数: 75
Thread-2窗口卖了一张票,剩余票数: 74
Thread-1窗口卖了一张票,剩余票数: 73
Thread-0窗口卖了一张票,剩余票数: 72
Thread-2窗口卖了一张票,剩余票数: 71
Thread-0窗口卖了一张票,剩余票数: 70
Thread-1窗口卖了一张票,剩余票数: 69
Thread-2窗口卖了一张票,剩余票数: 68
Thread-1窗口卖了一张票,剩余票数: 67
Thread-0窗口卖了一张票,剩余票数: 67
Thread-2窗口卖了一张票,剩余票数: 66
Thread-0窗口卖了一张票,剩余票数: 65
Thread-1窗口卖了一张票,剩余票数: 65
Thread-2窗口卖了一张票,剩余票数: 64
Thread-1窗口卖了一张票,剩余票数: 63
Thread-0窗口卖了一张票,剩余票数: 63
Thread-2窗口卖了一张票,剩余票数: 62
Thread-1窗口卖了一张票,剩余票数: 61
Thread-0窗口卖了一张票,剩余票数: 61
Thread-2窗口卖了一张票,剩余票数: 60
Thread-0窗口卖了一张票,剩余票数: 59
Thread-1窗口卖了一张票,剩余票数: 58
Thread-2窗口卖了一张票,剩余票数: 57
Thread-0窗口卖了一张票,剩余票数: 55
Thread-1窗口卖了一张票,剩余票数: 56
Thread-2窗口卖了一张票,剩余票数: 54
Thread-0窗口卖了一张票,剩余票数: 53
Thread-1窗口卖了一张票,剩余票数: 52
Thread-2窗口卖了一张票,剩余票数: 51
Thread-0窗口卖了一张票,剩余票数: 50
Thread-1窗口卖了一张票,剩余票数: 50
Thread-2窗口卖了一张票,剩余票数: 49
Thread-1窗口卖了一张票,剩余票数: 48
Thread-0窗口卖了一张票,剩余票数: 48
Thread-2窗口卖了一张票,剩余票数: 47
Thread-0窗口卖了一张票,剩余票数: 46
Thread-1窗口卖了一张票,剩余票数: 46
Thread-2窗口卖了一张票,剩余票数: 45
Thread-0窗口卖了一张票,剩余票数: 43
Thread-1窗口卖了一张票,剩余票数: 44
Thread-2窗口卖了一张票,剩余票数: 42
Thread-0窗口卖了一张票,剩余票数: 40
Thread-1窗口卖了一张票,剩余票数: 41
Thread-2窗口卖了一张票,剩余票数: 39
Thread-1窗口卖了一张票,剩余票数: 38
Thread-0窗口卖了一张票,剩余票数: 38
Thread-2窗口卖了一张票,剩余票数: 37
Thread-0窗口卖了一张票,剩余票数: 36
Thread-1窗口卖了一张票,剩余票数: 35
Thread-2窗口卖了一张票,剩余票数: 34
Thread-1窗口卖了一张票,剩余票数: 33
Thread-0窗口卖了一张票,剩余票数: 33
Thread-2窗口卖了一张票,剩余票数: 32
Thread-0窗口卖了一张票,剩余票数: 31
Thread-1窗口卖了一张票,剩余票数: 31
Thread-2窗口卖了一张票,剩余票数: 30
Thread-1窗口卖了一张票,剩余票数: 29
Thread-0窗口卖了一张票,剩余票数: 29
Thread-2窗口卖了一张票,剩余票数: 28
Thread-1窗口卖了一张票,剩余票数: 27
Thread-0窗口卖了一张票,剩余票数: 27
Thread-2窗口卖了一张票,剩余票数: 26
Thread-0窗口卖了一张票,剩余票数: 24
Thread-1窗口卖了一张票,剩余票数: 25
Thread-2窗口卖了一张票,剩余票数: 23
Thread-1窗口卖了一张票,剩余票数: 22
Thread-0窗口卖了一张票,剩余票数: 21
Thread-2窗口卖了一张票,剩余票数: 20
Thread-1窗口卖了一张票,剩余票数: 19
Thread-0窗口卖了一张票,剩余票数: 18
Thread-2窗口卖了一张票,剩余票数: 17
Thread-0窗口卖了一张票,剩余票数: 16
Thread-1窗口卖了一张票,剩余票数: 15
Thread-2窗口卖了一张票,剩余票数: 14
Thread-1窗口卖了一张票,剩余票数: 13
Thread-0窗口卖了一张票,剩余票数: 13
Thread-2窗口卖了一张票,剩余票数: 12
Thread-0窗口卖了一张票,剩余票数: 11
Thread-1窗口卖了一张票,剩余票数: 10
Thread-2窗口卖了一张票,剩余票数: 9
Thread-1窗口卖了一张票,剩余票数: 8
Thread-0窗口卖了一张票,剩余票数: 7
Thread-2窗口卖了一张票,剩余票数: 6
Thread-1窗口卖了一张票,剩余票数: 5
Thread-0窗口卖了一张票,剩余票数: 4
Thread-2窗口卖了一张票,剩余票数: 3
Thread-1窗口卖了一张票,剩余票数: 2
Thread-0窗口卖了一张票,剩余票数: 1
Thread-2窗口卖了一张票,剩余票数: 0
Thread-2窗口,票卖完了
Thread-1窗口卖了一张票,剩余票数: -1
Thread-0窗口卖了一张票,剩余票数: -1
View Code

会有超票的风险,引入了锁,即synchronized关键字,如何使用呢?

注意,实现Runnable接口的实现类对象在main中只创建了一次,就当作创建3线程的参数,因此实现类对象是唯一的,由此想到对象锁

即 用synchronized修饰普通方法【同步方法】或者在普通方法中修饰代码块【同步代码块】===>【锁在对象上】

public class SellTicket {
    public static void main(String[] args) {
        SellTicketWin sellTicketWin = new SellTicketWin();
        Thread thread1 = new Thread(sellTicketWin);
        Thread thread2 = new Thread(sellTicketWin);
        Thread thread3 = new Thread(sellTicketWin);

        thread1.start();
        thread2.start();
        thread3.start();

    }
}

class SellTicketWin implements Runnable{
    private int num = 100;
    private boolean loop = true;

    // 对象锁,上锁方式一:
    // 用synchronized修饰普通方法【同步方法】
    // public synchronized void sell(){}
    public /*synchronized*/ void sell(){
        // 对象锁,上锁方式二:
        // 在普通方法中,用synchronized修饰代码块【同步代码块】
        // synchronized (this) {},()是对象名或者当前对象this.
        synchronized (/*this*/ (Integer)num) {
            if (num <= 0) {
                System.out.println(Thread.currentThread().getName() + "窗口,票卖完了");
                loop = false;
                return;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                System.out.println(e.getMessage());
            }
            System.out.println(Thread.currentThread().getName() + "窗口卖了一张票,剩余票数: " + (--num));
        }
    }
    @Override
    public void run() {
        while (loop) {
            sell();
        }
    }
}
Thread-2窗口卖了一张票,剩余票数: 99
Thread-2窗口卖了一张票,剩余票数: 98
Thread-0窗口卖了一张票,剩余票数: 97
Thread-1窗口卖了一张票,剩余票数: 95
Thread-2窗口卖了一张票,剩余票数: 94
Thread-0窗口卖了一张票,剩余票数: 96
Thread-1窗口卖了一张票,剩余票数: 93
Thread-1窗口卖了一张票,剩余票数: 92
Thread-0窗口卖了一张票,剩余票数: 91
Thread-2窗口卖了一张票,剩余票数: 90
Thread-1窗口卖了一张票,剩余票数: 90
Thread-2窗口卖了一张票,剩余票数: 89
Thread-0窗口卖了一张票,剩余票数: 88
Thread-1窗口卖了一张票,剩余票数: 87
Thread-2窗口卖了一张票,剩余票数: 86
Thread-1窗口卖了一张票,剩余票数: 85
Thread-0窗口卖了一张票,剩余票数: 84
Thread-1窗口卖了一张票,剩余票数: 83
Thread-2窗口卖了一张票,剩余票数: 82
Thread-0窗口卖了一张票,剩余票数: 81
Thread-1窗口卖了一张票,剩余票数: 81
Thread-2窗口卖了一张票,剩余票数: 80
Thread-0窗口卖了一张票,剩余票数: 79
Thread-2窗口卖了一张票,剩余票数: 78
Thread-1窗口卖了一张票,剩余票数: 77
Thread-0窗口卖了一张票,剩余票数: 76
Thread-2窗口卖了一张票,剩余票数: 76
Thread-1窗口卖了一张票,剩余票数: 75
Thread-0窗口卖了一张票,剩余票数: 75
Thread-2窗口卖了一张票,剩余票数: 74
Thread-1窗口卖了一张票,剩余票数: 73
Thread-0窗口卖了一张票,剩余票数: 72
Thread-2窗口卖了一张票,剩余票数: 71
Thread-0窗口卖了一张票,剩余票数: 70
Thread-1窗口卖了一张票,剩余票数: 70
Thread-2窗口卖了一张票,剩余票数: 69
Thread-0窗口卖了一张票,剩余票数: 69
Thread-2窗口卖了一张票,剩余票数: 68
Thread-1窗口卖了一张票,剩余票数: 67
Thread-0窗口卖了一张票,剩余票数: 66
Thread-2窗口卖了一张票,剩余票数: 66
Thread-1窗口卖了一张票,剩余票数: 65
Thread-0窗口卖了一张票,剩余票数: 65
Thread-1窗口卖了一张票,剩余票数: 64
Thread-2窗口卖了一张票,剩余票数: 64
Thread-0窗口卖了一张票,剩余票数: 63
Thread-1窗口卖了一张票,剩余票数: 62
Thread-2窗口卖了一张票,剩余票数: 61
Thread-0窗口卖了一张票,剩余票数: 60
Thread-2窗口卖了一张票,剩余票数: 59
Thread-1窗口卖了一张票,剩余票数: 59
Thread-2窗口卖了一张票,剩余票数: 58
Thread-0窗口卖了一张票,剩余票数: 57
Thread-0窗口卖了一张票,剩余票数: 56
Thread-2窗口卖了一张票,剩余票数: 55
Thread-1窗口卖了一张票,剩余票数: 54
Thread-0窗口卖了一张票,剩余票数: 53
Thread-2窗口卖了一张票,剩余票数: 52
Thread-1窗口卖了一张票,剩余票数: 51
Thread-0窗口卖了一张票,剩余票数: 50
Thread-0窗口卖了一张票,剩余票数: 49
Thread-1窗口卖了一张票,剩余票数: 48
Thread-2窗口卖了一张票,剩余票数: 47
Thread-2窗口卖了一张票,剩余票数: 46
Thread-0窗口卖了一张票,剩余票数: 46
Thread-2窗口卖了一张票,剩余票数: 45
Thread-1窗口卖了一张票,剩余票数: 44
Thread-0窗口卖了一张票,剩余票数: 43
Thread-1窗口卖了一张票,剩余票数: 43
Thread-2窗口卖了一张票,剩余票数: 42
Thread-0窗口卖了一张票,剩余票数: 41
Thread-1窗口卖了一张票,剩余票数: 40
Thread-2窗口卖了一张票,剩余票数: 40
Thread-1窗口卖了一张票,剩余票数: 39
Thread-0窗口卖了一张票,剩余票数: 39
Thread-2窗口卖了一张票,剩余票数: 38
Thread-1窗口卖了一张票,剩余票数: 38
Thread-0窗口卖了一张票,剩余票数: 37
Thread-2窗口卖了一张票,剩余票数: 37
Thread-1窗口卖了一张票,剩余票数: 36
Thread-0窗口卖了一张票,剩余票数: 36
Thread-1窗口卖了一张票,剩余票数: 35
Thread-2窗口卖了一张票,剩余票数: 34
Thread-0窗口卖了一张票,剩余票数: 32
Thread-1窗口卖了一张票,剩余票数: 33
Thread-1窗口卖了一张票,剩余票数: 31
Thread-2窗口卖了一张票,剩余票数: 31
Thread-1窗口卖了一张票,剩余票数: 30
Thread-0窗口卖了一张票,剩余票数: 30
Thread-1窗口卖了一张票,剩余票数: 29
Thread-2窗口卖了一张票,剩余票数: 28
Thread-1窗口卖了一张票,剩余票数: 27
Thread-0窗口卖了一张票,剩余票数: 27
Thread-1窗口卖了一张票,剩余票数: 26
Thread-2窗口卖了一张票,剩余票数: 26
Thread-1窗口卖了一张票,剩余票数: 25
Thread-0窗口卖了一张票,剩余票数: 24
Thread-2窗口卖了一张票,剩余票数: 23
Thread-1窗口卖了一张票,剩余票数: 22
Thread-2窗口卖了一张票,剩余票数: 21
Thread-0窗口卖了一张票,剩余票数: 21
Thread-1窗口卖了一张票,剩余票数: 20
Thread-2窗口卖了一张票,剩余票数: 20
Thread-0窗口卖了一张票,剩余票数: 19
Thread-1窗口卖了一张票,剩余票数: 19
Thread-1窗口卖了一张票,剩余票数: 18
Thread-2窗口卖了一张票,剩余票数: 18
Thread-0窗口卖了一张票,剩余票数: 17
Thread-1窗口卖了一张票,剩余票数: 17
Thread-2窗口卖了一张票,剩余票数: 16
Thread-0窗口卖了一张票,剩余票数: 15
Thread-1窗口卖了一张票,剩余票数: 14
Thread-2窗口卖了一张票,剩余票数: 13
Thread-0窗口卖了一张票,剩余票数: 12
Thread-1窗口卖了一张票,剩余票数: 12
Thread-2窗口卖了一张票,剩余票数: 11
Thread-0窗口卖了一张票,剩余票数: 11
Thread-1窗口卖了一张票,剩余票数: 10
Thread-2窗口卖了一张票,剩余票数: 9
Thread-1窗口卖了一张票,剩余票数: 8
Thread-0窗口卖了一张票,剩余票数: 8
Thread-1窗口卖了一张票,剩余票数: 7
Thread-2窗口卖了一张票,剩余票数: 7
Thread-0窗口卖了一张票,剩余票数: 6
Thread-1窗口卖了一张票,剩余票数: 6
Thread-2窗口卖了一张票,剩余票数: 5
Thread-0窗口卖了一张票,剩余票数: 5
Thread-2窗口卖了一张票,剩余票数: 4
Thread-1窗口卖了一张票,剩余票数: 4
Thread-0窗口卖了一张票,剩余票数: 3
Thread-2窗口卖了一张票,剩余票数: 3
Thread-0窗口卖了一张票,剩余票数: 2
Thread-1窗口卖了一张票,剩余票数: 2
Thread-1窗口卖了一张票,剩余票数: 1
Thread-2窗口卖了一张票,剩余票数: 0
Thread-1窗口,票卖完了
Thread-2窗口,票卖完了
Thread-0窗口,票卖完了
View Code

可见运行结果完全正确。

2)继承Thread类实现

错误写法:

public class SellTicket {
    public static void main(String[] args) {
        SellTicketWin thread1 = new SellTicketWin();
        SellTicketWin thread2 = new SellTicketWin();
        SellTicketWin thread3 = new SellTicketWin();

        thread1.start();
        thread2.start();
        thread3.start();


    }
}

class SellTicketWin extends Thread{
    private int num = 100;
    private boolean loop = true;

    public void sell(){
        if (num <= 0) {
            System.out.println(Thread.currentThread().getName() + "窗口,票卖完了");
            loop = false;
            return;
        }
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            System.out.println(e.getMessage());
        }
        System.out.println(Thread.currentThread().getName() + "窗口卖了一张票,剩余票数: " + (--num));
    }
    @Override
    public void run() {
        while (loop) {
            sell();
        }
    }
}

此时,是3个对象,3个num成员,这里的num并不是唯一且共享的,不合题意。

正确写法:

private static int num = 100;
private static boolean loop = true;

使用static关键字,使得num变成静态变量,随着类的加载而加载,唯一且共享。

如果不加锁,运行结果如下:

Thread-1窗口卖了一张票,剩余票数: 99
Thread-0窗口卖了一张票,剩余票数: 97
Thread-2窗口卖了一张票,剩余票数: 98
Thread-2窗口卖了一张票,剩余票数: 96
Thread-0窗口卖了一张票,剩余票数: 95
Thread-1窗口卖了一张票,剩余票数: 94
Thread-2窗口卖了一张票,剩余票数: 93
Thread-1窗口卖了一张票,剩余票数: 92
Thread-0窗口卖了一张票,剩余票数: 91
Thread-1窗口卖了一张票,剩余票数: 89
Thread-2窗口卖了一张票,剩余票数: 90
Thread-0窗口卖了一张票,剩余票数: 88
Thread-0窗口卖了一张票,剩余票数: 87
Thread-1窗口卖了一张票,剩余票数: 86
Thread-2窗口卖了一张票,剩余票数: 85
Thread-2窗口卖了一张票,剩余票数: 84
Thread-1窗口卖了一张票,剩余票数: 83
Thread-0窗口卖了一张票,剩余票数: 82
Thread-2窗口卖了一张票,剩余票数: 81
Thread-0窗口卖了一张票,剩余票数: 80
Thread-1窗口卖了一张票,剩余票数: 79
Thread-2窗口卖了一张票,剩余票数: 78
Thread-1窗口卖了一张票,剩余票数: 77
Thread-0窗口卖了一张票,剩余票数: 76
Thread-1窗口卖了一张票,剩余票数: 75
Thread-0窗口卖了一张票,剩余票数: 74
Thread-2窗口卖了一张票,剩余票数: 73
Thread-2窗口卖了一张票,剩余票数: 72
Thread-1窗口卖了一张票,剩余票数: 72
Thread-0窗口卖了一张票,剩余票数: 71
Thread-2窗口卖了一张票,剩余票数: 70
Thread-1窗口卖了一张票,剩余票数: 68
Thread-0窗口卖了一张票,剩余票数: 69
Thread-0窗口卖了一张票,剩余票数: 67
Thread-1窗口卖了一张票,剩余票数: 66
Thread-2窗口卖了一张票,剩余票数: 65
Thread-1窗口卖了一张票,剩余票数: 64
Thread-0窗口卖了一张票,剩余票数: 63
Thread-2窗口卖了一张票,剩余票数: 62
Thread-1窗口卖了一张票,剩余票数: 61
Thread-0窗口卖了一张票,剩余票数: 61
Thread-2窗口卖了一张票,剩余票数: 60
Thread-1窗口卖了一张票,剩余票数: 59
Thread-0窗口卖了一张票,剩余票数: 58
Thread-2窗口卖了一张票,剩余票数: 57
Thread-0窗口卖了一张票,剩余票数: 56
Thread-1窗口卖了一张票,剩余票数: 56
Thread-2窗口卖了一张票,剩余票数: 55
Thread-1窗口卖了一张票,剩余票数: 53
Thread-0窗口卖了一张票,剩余票数: 54
Thread-2窗口卖了一张票,剩余票数: 52
Thread-1窗口卖了一张票,剩余票数: 51
Thread-0窗口卖了一张票,剩余票数: 51
Thread-2窗口卖了一张票,剩余票数: 50
Thread-0窗口卖了一张票,剩余票数: 48
Thread-1窗口卖了一张票,剩余票数: 49
Thread-2窗口卖了一张票,剩余票数: 47
Thread-1窗口卖了一张票,剩余票数: 46
Thread-0窗口卖了一张票,剩余票数: 45
Thread-2窗口卖了一张票,剩余票数: 44
Thread-0窗口卖了一张票,剩余票数: 43
Thread-1窗口卖了一张票,剩余票数: 42
Thread-2窗口卖了一张票,剩余票数: 41
Thread-1窗口卖了一张票,剩余票数: 39
Thread-0窗口卖了一张票,剩余票数: 40
Thread-2窗口卖了一张票,剩余票数: 38
Thread-1窗口卖了一张票,剩余票数: 37
Thread-0窗口卖了一张票,剩余票数: 36
Thread-2窗口卖了一张票,剩余票数: 35
Thread-0窗口卖了一张票,剩余票数: 34
Thread-1窗口卖了一张票,剩余票数: 33
Thread-2窗口卖了一张票,剩余票数: 32
Thread-0窗口卖了一张票,剩余票数: 31
Thread-1窗口卖了一张票,剩余票数: 30
Thread-2窗口卖了一张票,剩余票数: 29
Thread-0窗口卖了一张票,剩余票数: 28
Thread-1窗口卖了一张票,剩余票数: 28
Thread-2窗口卖了一张票,剩余票数: 27
Thread-1窗口卖了一张票,剩余票数: 26
Thread-0窗口卖了一张票,剩余票数: 25
Thread-2窗口卖了一张票,剩余票数: 24
Thread-0窗口卖了一张票,剩余票数: 23
Thread-1窗口卖了一张票,剩余票数: 23
Thread-2窗口卖了一张票,剩余票数: 22
Thread-1窗口卖了一张票,剩余票数: 21
Thread-0窗口卖了一张票,剩余票数: 20
Thread-2窗口卖了一张票,剩余票数: 19
Thread-0窗口卖了一张票,剩余票数: 18
Thread-1窗口卖了一张票,剩余票数: 17
Thread-2窗口卖了一张票,剩余票数: 16
Thread-1窗口卖了一张票,剩余票数: 15
Thread-0窗口卖了一张票,剩余票数: 14
Thread-2窗口卖了一张票,剩余票数: 13
Thread-0窗口卖了一张票,剩余票数: 12
Thread-1窗口卖了一张票,剩余票数: 11
Thread-2窗口卖了一张票,剩余票数: 10
Thread-0窗口卖了一张票,剩余票数: 9
Thread-1窗口卖了一张票,剩余票数: 8
Thread-2窗口卖了一张票,剩余票数: 7
Thread-1窗口卖了一张票,剩余票数: 6
Thread-0窗口卖了一张票,剩余票数: 6
Thread-2窗口卖了一张票,剩余票数: 5
Thread-1窗口卖了一张票,剩余票数: 3
Thread-0窗口卖了一张票,剩余票数: 4
Thread-2窗口卖了一张票,剩余票数: 2
Thread-0窗口卖了一张票,剩余票数: 1
Thread-0窗口,票卖完了
Thread-1窗口卖了一张票,剩余票数: 0
Thread-2窗口卖了一张票,剩余票数: -1

Process finished with exit code 0
View Code

会有超票现象,此时num是静态变量,由此想到加类锁

即用synchronized修饰静态方法【同步方法】或者在静态方法中修饰代码块【同步代码块】===>【锁在类上】

public class SellTicket {
    public static void main(String[] args) {
        SellTicketWin thread1 = new SellTicketWin();
        SellTicketWin thread2 = new SellTicketWin();
        SellTicketWin thread3 = new SellTicketWin();

        thread1.start();
        thread2.start();
        thread3.start();


    }
}

class SellTicketWin extends Thread{
    private static int num = 100;
    private static boolean loop = true;

    // 类锁方式一:将sell改为静态方法,并且用synchronized修饰【同步方法】
    // public static synchronized void sell(){}
    public static /*synchronized*/ void sell(){
        // 类锁方式二:在静态方法中,用synchronized修饰代码块【同步代码块】
        // synchronized (SellTicketWin.class) {},()是类名.class
        synchronized (SellTicketWin.class) {
            if (num <= 0) {
                System.out.println(Thread.currentThread().getName() + "窗口,票卖完了");
                loop = false;
                return;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                System.out.println(e.getMessage());
            }
            System.out.println(Thread.currentThread().getName() + "窗口卖了一张票,剩余票数: " + (--num));
        }
    }
    @Override
    public void run() {
        while (loop) {
            sell();
        }
    }
}
Thread-2窗口卖了一张票,剩余票数: 99
Thread-2窗口卖了一张票,剩余票数: 98
Thread-2窗口卖了一张票,剩余票数: 97
Thread-2窗口卖了一张票,剩余票数: 96
Thread-2窗口卖了一张票,剩余票数: 95
Thread-2窗口卖了一张票,剩余票数: 94
Thread-2窗口卖了一张票,剩余票数: 93
Thread-2窗口卖了一张票,剩余票数: 92
Thread-2窗口卖了一张票,剩余票数: 91
Thread-1窗口卖了一张票,剩余票数: 90
Thread-0窗口卖了一张票,剩余票数: 89
Thread-1窗口卖了一张票,剩余票数: 88
Thread-1窗口卖了一张票,剩余票数: 87
Thread-1窗口卖了一张票,剩余票数: 86
Thread-2窗口卖了一张票,剩余票数: 85
Thread-2窗口卖了一张票,剩余票数: 84
Thread-2窗口卖了一张票,剩余票数: 83
Thread-1窗口卖了一张票,剩余票数: 82
Thread-1窗口卖了一张票,剩余票数: 81
Thread-0窗口卖了一张票,剩余票数: 80
Thread-0窗口卖了一张票,剩余票数: 79
Thread-0窗口卖了一张票,剩余票数: 78
Thread-0窗口卖了一张票,剩余票数: 77
Thread-1窗口卖了一张票,剩余票数: 76
Thread-2窗口卖了一张票,剩余票数: 75
Thread-2窗口卖了一张票,剩余票数: 74
Thread-2窗口卖了一张票,剩余票数: 73
Thread-2窗口卖了一张票,剩余票数: 72
Thread-2窗口卖了一张票,剩余票数: 71
Thread-2窗口卖了一张票,剩余票数: 70
Thread-1窗口卖了一张票,剩余票数: 69
Thread-1窗口卖了一张票,剩余票数: 68
Thread-1窗口卖了一张票,剩余票数: 67
Thread-0窗口卖了一张票,剩余票数: 66
Thread-0窗口卖了一张票,剩余票数: 65
Thread-1窗口卖了一张票,剩余票数: 64
Thread-2窗口卖了一张票,剩余票数: 63
Thread-2窗口卖了一张票,剩余票数: 62
Thread-2窗口卖了一张票,剩余票数: 61
Thread-1窗口卖了一张票,剩余票数: 60
Thread-1窗口卖了一张票,剩余票数: 59
Thread-1窗口卖了一张票,剩余票数: 58
Thread-1窗口卖了一张票,剩余票数: 57
Thread-1窗口卖了一张票,剩余票数: 56
Thread-1窗口卖了一张票,剩余票数: 55
Thread-0窗口卖了一张票,剩余票数: 54
Thread-1窗口卖了一张票,剩余票数: 53
Thread-2窗口卖了一张票,剩余票数: 52
Thread-2窗口卖了一张票,剩余票数: 51
Thread-2窗口卖了一张票,剩余票数: 50
Thread-2窗口卖了一张票,剩余票数: 49
Thread-2窗口卖了一张票,剩余票数: 48
Thread-2窗口卖了一张票,剩余票数: 47
Thread-2窗口卖了一张票,剩余票数: 46
Thread-2窗口卖了一张票,剩余票数: 45
Thread-1窗口卖了一张票,剩余票数: 44
Thread-1窗口卖了一张票,剩余票数: 43
Thread-0窗口卖了一张票,剩余票数: 42
Thread-1窗口卖了一张票,剩余票数: 41
Thread-2窗口卖了一张票,剩余票数: 40
Thread-2窗口卖了一张票,剩余票数: 39
Thread-2窗口卖了一张票,剩余票数: 38
Thread-2窗口卖了一张票,剩余票数: 37
Thread-2窗口卖了一张票,剩余票数: 36
Thread-2窗口卖了一张票,剩余票数: 35
Thread-2窗口卖了一张票,剩余票数: 34
Thread-2窗口卖了一张票,剩余票数: 33
Thread-1窗口卖了一张票,剩余票数: 32
Thread-0窗口卖了一张票,剩余票数: 31
Thread-1窗口卖了一张票,剩余票数: 30
Thread-1窗口卖了一张票,剩余票数: 29
Thread-2窗口卖了一张票,剩余票数: 28
Thread-2窗口卖了一张票,剩余票数: 27
Thread-1窗口卖了一张票,剩余票数: 26
Thread-1窗口卖了一张票,剩余票数: 25
Thread-0窗口卖了一张票,剩余票数: 24
Thread-1窗口卖了一张票,剩余票数: 23
Thread-2窗口卖了一张票,剩余票数: 22
Thread-1窗口卖了一张票,剩余票数: 21
Thread-1窗口卖了一张票,剩余票数: 20
Thread-0窗口卖了一张票,剩余票数: 19
Thread-1窗口卖了一张票,剩余票数: 18
Thread-1窗口卖了一张票,剩余票数: 17
Thread-1窗口卖了一张票,剩余票数: 16
Thread-1窗口卖了一张票,剩余票数: 15
Thread-1窗口卖了一张票,剩余票数: 14
Thread-2窗口卖了一张票,剩余票数: 13
Thread-2窗口卖了一张票,剩余票数: 12
Thread-2窗口卖了一张票,剩余票数: 11
Thread-2窗口卖了一张票,剩余票数: 10
Thread-2窗口卖了一张票,剩余票数: 9
Thread-1窗口卖了一张票,剩余票数: 8
Thread-0窗口卖了一张票,剩余票数: 7
Thread-0窗口卖了一张票,剩余票数: 6
Thread-1窗口卖了一张票,剩余票数: 5
Thread-2窗口卖了一张票,剩余票数: 4
Thread-2窗口卖了一张票,剩余票数: 3
Thread-1窗口卖了一张票,剩余票数: 2
Thread-0窗口卖了一张票,剩余票数: 1
Thread-1窗口卖了一张票,剩余票数: 0
Thread-1窗口,票卖完了
Thread-2窗口,票卖完了
Thread-0窗口,票卖完了
View Code

可见运行结果完全正确。