【 java 多线程】同步方式解决多线程的安全问题

发布于:2022-11-28 ⋅ 阅读:(344) ⋅ 点赞:(0)

📋 个人简介

  • 💖 作者简介:大家好,我是阿牛,全栈领域优质创作者。😜
  • 📝 个人主页:馆主阿牛🔥
  • 🎉 支持我:点赞👍+收藏⭐️+留言📝
  • 📣 系列专栏:java 小白到高手的蜕变🍁
  • 💬格言:要成为光,因为有怕黑的人!🔥
    请添加图片描述

前言

上节我们写了一个比较经典的多窗口卖票案例,但我们最后发现他有重票和错票的情况,这是由于线程的不安全造成的,本节我将总结一下用同步的方式来处理线程安全问题!主要就是synchronized这个关键字。

问题概述

创建三个窗口卖票,总票数为100张。
1.问题:卖票过程中,出现了重票、错票﹣->出现了线程的安全问题
2.问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来。

在这里插入图片描述

3.如何解决:当一个线程 a 在操 ticket 的时候,其他线程不能参与进来。直到线程 a 操作完成后,其他线程才可以开始操作 ticket 。这种情況即使线程 a 出现了阻塞,也不能被改变。

同步代码块方式解决线程安全问题

synchronized(同步监视器){
//需要被同步的代码
}

说明:
1.操作共享数据的代码,即为需要被同步的代码
2.共享数据:多个线程共同繰作的变量。比如: ticket 就是共享数据。
3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。要求:多个线程必须要共用同一把锁。
4. 在实现 Runnable 接口创建多线程的方式中,我们可考虑使用 this 充当同步监视器。
注:当然为了方便,这个锁我们一般可以用this(还要看是继承Thread还是实现Runnable方式,继承Thread会有多个对象,此时this作为锁不唯一)、类名.class。
这里在说明一下类名.class。不是说这个锁是任何一个类的对象吗,其实,在java中,类也是一个对象。在 Java 中,每个 class 都有一个相应的 Class 对
象。也就是说,当我们编写一个类,编译完成后,在生成的.class 文件中,就会产生一个 Class 对象,用于表示这个类的类型信息。
其实任何一个类,都会有一个 Class 对象于这个类对像,在这个 Class 对象中,保存着实例化该类时所需要的基本信息, 类名.class 其实返回的是一个类的 Class 对象。

同步代码块解决继承Thread类的线程安全问题

public class SellTickets {
    public static void main(String[] args) {
        MyTh t1 = new MyTh();
        MyTh t2 = new MyTh();
        MyTh t3 = new MyTh();

        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");

        t1.start();
        t2.start();
        t3.start();
    }


}
class MyTh extends Thread{
    private static int tickets = 100; //这里总票数一定要是静态的(static),因为多个线程要共享这一个变量!
    //private static Object obj = new Object(); //可以作为锁
    @Override
    public void run() {
        while(true){
            // synchronized (obj)
            // 或者
            synchronized (MyTh.class){  //这里锁不能用this,有三个对象,this作为锁不是共享的
                if(tickets>0){
                    System.out.println(this.getName() + "-票号为:" + tickets);
                    tickets--;
                }else{
                    break;
                }
            }

        }

    }
}

在这里插入图片描述
可以看到此时结果无重票。

同步代码块解决实现Runnable接口的线程安全问题

package 多线程;

public class SellTickets {
    public static void main(String[] args) {
        MyTh m = new MyTh();
        Thread t1 = new Thread(m);
        Thread t2 = new Thread(m);
        Thread t3 = new Thread(m);

        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");

        t1.start();
        t2.start();
        t3.start();
    }


}

class MyTh implements Runnable{
    private int tickets = 100; //这里总票数不必一定要是静态的(static),因为我们是实现的Runnable接口,最后只创建一个对象
    //private static Object obj = new Object(); //可以作为锁
    @Override
    public void run() {
        while(true){
            // synchronized (obj)
            // synchronized (MyTh.class)
            synchronized (this){ //此时当前类的对象唯一,可以用this作为锁
                if(tickets>0){
                    System.out.println(Thread.currentThread().getName() + "-票号为:" + tickets);
                    tickets--;
                }else{
                    break;
                }
            }
        }

    }
}

在这里插入图片描述

同步方法解决线程安全问题。

将你要进行同步的代码放到同步方法里就行。同步方法要用synchronized修饰!

关于同步方法的总结:
1.同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
2.非静态的同步方法,同步监视器是: this ;静态的同步方法,同步监视器是:当前类本身。

同步方法解决继承Thread类的线程安全问题

public class SellTickets {
    public static void main(String[] args) {
        MyTh t1 = new MyTh();
        MyTh t2 = new MyTh();
        MyTh t3 = new MyTh();

        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");

        t1.start();
        t2.start();
        t3.start();
    }


}

class MyTh extends Thread{
    private static int tickets = 100;
    @Override
    public void run() {
        while(true){
            Sell();
            if(tickets<=0){
                break;
            }
        }
    }

    public static synchronized void Sell(){ //此时,同步方法可以必须是静态的,同步监视器(锁)是MyTh.class(static方法里面不能有this)。因为锁不能为this。
        if(tickets>0){
            System.out.println(Thread.currentThread().getName() + "-票号为:" + tickets);
            tickets--;
        }
    }
}

同步方法解决实现Runnable接口的线程安全问题

public class SellTickets {
    public static void main(String[] args) {
        MyTh m = new MyTh();
        Thread t1 = new Thread(m);
        Thread t2 = new Thread(m);
        Thread t3 = new Thread(m);

        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");

        t1.start();
        t2.start();
        t3.start();
    }


}

class MyTh implements Runnable{
    private int tickets = 100; //这里总票数不必一定要是静态的(static),因为我们是实现的Runnable接口,最后只创建一个对象
    @Override
    public void run() {
        while(true){
            Sell();
            if(tickets<=0){
                break;
            }
        }
    }

    public synchronized void Sell(){ //此时,同步方法可以是非静态的,同步监视器(锁)是this
        if(tickets>0){
            System.out.println(Thread.currentThread().getName() + "-票号为:" + tickets);
            tickets--;
        }
    }
}

结语

如果你觉得博主写的还不错的话,可以关注一下当前专栏,博主会更完这个系列的哦!也欢迎订阅博主的其他好的专栏。

🏰系列专栏
👉软磨 css
👉硬泡 javascript
👉flask框架快速入门

本文含有隐藏内容,请 开通VIP 后查看