多线程>>>多线程的同步机制

多线程>>>多线程的同步机制

栗子:

卖票:

创建个窗口卖票,总票数为100张.使用实现Runnable接口的方式👇

public class TestThread2 {
    public static void main(String[] args) {

        //实例化卖票线程的实现类
        Ticket ticket = new Ticket();

        //新建添加卖票线程的实现类的线程,让两个线程去同时卖票
        Thread thread1 = new Thread(ticket);
        Thread thread2 = new Thread(ticket);
        //调用start方法完成创建,##调用start方法会自动调用重写的run方法
        thread1.start();
        thread2.start();
    }
}

//卖票线程的实现类
class Ticket implements Runnable {
    private int tickets = 100;

    @Override
    public void run() {

        while (true) {
            if (tickets > 0) {
                System.out.println(Thread.currentThread().getName() + "正在卖第" + tickets + "张票");
                tickets--;
            }
        }
    }
}

输出的结果👇

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

Process finished with exit code -1
  • 1.问题:卖票过程中,出现了重票、错票 -->出现了线程的安全问题
  • 2.问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票。
  • 3.如何解决:当一个线程a在操作ticket的时候,其他线程不能参与进来。直到线程a操作完ticket时,其他线程才可以开始操作ticket。这种情况即使线程a出现了阻塞,也不能被改变。

Java解决方案:同步机制

在Java中,我们通过同步机制,来解决线程的安全问题。

方式一:同步代码块

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

这样👇

//卖票线程的实现类
class Ticket implements Runnable {
    private int tickets = 100;
    Object object = new Object();
    //这里的Object是同步监视器

    @Override
    public void run() {
        while (true) {
            synchronized (object) {
                //这里的synchronized应该放在循环里面
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName() + "正在卖第" + tickets + "张票");
                    tickets--;
                }
            }
        }
    }
}

看问题解决了👇

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

Process finished with exit code -1

说明:

  1. 操作共享数据的代码,即为需要被同步的代码。 -->不能包含代码多了,也不能包含代码少了。

  2. 共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。

  3. 同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。

要求:多个线程必须要共用同一把锁。

补充:

  1. 在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
  2. 在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。

方式二:同步方法

如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。

关于同步方法的总结:
  1. 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。

  2. 非静态的同步方法,同步监视器是:this
    静态的同步方法,同步监视器是:当前类本身

方式三:Lock锁 --- JDK5.0新增


面试题:synchronized 与 Lock的异同?

相同:二者都可以解决线程安全问题

不同:

  • synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
    Lock需要手动的启动同步(lock(),同时结束同步也需要手动的实现(unlock())

利弊

同步的方式

  1. 好处是 解决了线程的安全问题。
  2. 操作同步代码时,只能一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。
posted @ 2020-11-16 22:20  zhengz  阅读(114)  评论(0)    收藏  举报