Java 多线程通信安全问题

Java 多线程通信安全问题

问题描述:

在日产生活中,我们都买过火车票,票只会显示有票和售完。在程序中,如果我们只开放一个窗口,肯定不会有什么问题,但在实际操作中,肯定是多个窗口进行售票,我们就不得不用多线程的方法来进行卖票。这时候就会出现问题了,我们不对线程做任何的限制,多个线程之间没有信息通信交流,就会出现票数为负数的问题。

多线程代码如下

public static void main(String[] args) {
    
    
    Ticket t1 = new Ticket();
    //开设四个窗口来售票
    new Thread(t1).start();
    new Thread(t1).start();
    new Thread(t1).start();
    new Thread(t1).start();
}
static class Ticket implements Runnable{
    
    
    private int count = 10;
    @Override
    public void run() {
    
    
        while (count>0){
    
    
            try {
    
    
                //为了使线程更容易出问题,让线程慢一点
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("售票中");
            count--;
            System.out.println("出票成功,还剩:"+count+"张票");
        }
    }
}

这时候就会出现问题了:

在这里插入图片描述

我们可以看出,即使票数为0了,线程还在拼命卖票,

解决安全问题的三种方法

解决方法本质上就是让程序排队执行,给代码的某一块加一个锁

同步代码块:

synchronized( 锁对象 )关键字 给锁对象加一个锁的标记,在有线程执行任务时,锁上,让后面的线程等着,一旦锁上的线程完成任务出来了,就会打开锁,让线程继续抢。不断进行这样的操作直到程序结束。

代码如下

public static void main(String[] args) {
    
    
    Ticket t1 = new Ticket();
    //开设四个窗口来售票
    new Thread(t1).start();
    new Thread(t1).start();
    new Thread(t1).start();
    new Thread(t1).start();
}
static class Ticket implements Runnable{
    
    
    private int count = 10;
    //创建一个锁对象o  o只能设置一个,不然就起不到锁的效果  比如说把这行代码放入while循环里面,就变成每一个线程一把锁
    Object o = new Object();
    @Override
    public void run() {
    
    
        while (true) {
    
    
            //下面一行注释掉的代码就是给每一个线程创建一个对象,就起不到排队的效果
            //Object o = new Object();
            //给o加一个锁
            synchronized (o) {
    
    
                if (count > 0) {
    
    
                    try {
    
    
                        //为了使线程更容易出问题,让线程慢一点
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                    System.out.println("售票中");
                    count--;
                    System.out.println(Thread.currentThread().getName()+"出票成功,还剩:" + count + "张票");
                }else {
    
    
                    break;
                }
                }
            }
    }
}

运行结果如下:

在这里插入图片描述

同步方法:

同步方法,顾名思义就是把方法进行同步,把sychronized关键字修饰在方法上。

public static void main(String[] args) {
    
    
    Ticket t1 = new Ticket();
    //开设四个窗口来售票
    new Thread(t1).start();
    new Thread(t1).start();
    new Thread(t1).start();
    new Thread(t1).start();
}
static class Ticket implements Runnable{
    
    
    private int count = 10;
    @Override
    public void run() {
    
    
        while (true) {
    
    
            boolean flag = sale();
            if (!flag){
    
    
                break;
            }
        }
    }
    //把if语句单独抽为一个方法,加synchronizxed关键字修饰
    public synchronized boolean sale(){
    
    
        if (count > 0) {
    
    
            try {
    
    
                //为了使线程更容易出问题,让线程慢一点
                Thread.sleep(500);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("售票中");
            count--;
            System.out.println(Thread.currentThread().getName()+"出票成功,还剩:" + count + "张票");
            return true;
        }
        return false;
    }
}

运行结果如下:

在这里插入图片描述

(这里多个线程都是对t1对象进行操作的,如果把对象new三个的话,同步方法就不会有效了)

显式锁:

同步代码块和同步方法都是隐式锁,显式锁就是把锁的方法调用出来,自己控制锁的位置。

public static void main(String[] args) {
    
    
    Ticket t1 = new Ticket();
    new Thread(t1).start();
    new Thread(t1).start();
    new Thread(t1).start();
    new Thread(t1).start();
}

static class Ticket implements Runnable {
    
    
    private int count = 10;
    //调用Lock类
    private Lock l = new ReentrantLock();

    @Override
    public void run() {
    
    
        while (true) {
    
    
            //从代码这个位置开始锁
            l.lock();
            if (count > 0) {
    
    
                try {
    
    
                    Thread.sleep(500);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                System.out.println("售票中");
                count--;
                System.out.println(Thread.currentThread().getName() + "出票成功,还剩:" + count + "张票");
            }else {
    
    
                break;
            }
            //从代码这里解锁
            l.unlock();
        }
    }
}

运行结果如下:

在这里插入图片描述

显式锁和隐式锁的区别

隐式锁(synchronized)显式锁(lock)具体使用方法来解决多线程通信的安全问题在上面已经演示的很清楚了,那它们有什么区别呢?

它们有如下几个区别:

1.诞生的时间

隐式锁在Java诞生时就有了,是在JAM层面操作的,属于JAM层面的锁,而显式锁是在JDK5之后才有的功能,属于API级别的锁。

2.使用方式

在使用显示和隐式的时候,区别就在于使用者要不要手动写代码去获取锁和释放锁的操作。

3.是否公平

隐式锁: 非公平锁

显式锁:它的公平与否是可以通过代码来设置的,默认是非公平锁。在其构造方法的时候可以传入Boolean值来改变,true:公平锁、false:非公平锁。

4.性能比较

隐式锁是给JVM来操作的,显式锁是通过代码直接实现的,在JDK1.5 之前,隐式锁性能较低,但到了JDK1.6的更新之后,Java官方修改了底层代码,现在的就性能来说,已经没什么区别了,官方也说以后会更支持隐式锁(synchrnoized),会优化更多的东西

5.可否中断

显式锁:显式锁是不可中断的,除非抛出异常或者正常运行完成。

隐式锁:隐式锁是可以中断的。中断方式:1. 调用设置超时方法tryLock(long time,TimeUnit) time :需要等待的时间 TimeUnit时间参数的单位

了JDK1.6的更新之后,Java官方修改了底层代码,现在的就性能来说,已经没什么区别了,官方也说以后会更支持隐式锁(synchrnoized),会优化更多的东西

5.可否中断

显式锁:显式锁是不可中断的,除非抛出异常或者正常运行完成。

隐式锁:隐式锁是可以中断的。中断方式:1. 调用设置超时方法tryLock(long time,TimeUnit) time :需要等待的时间 TimeUnit时间参数的单位

  1. 调用interrupt()方法来中断程序的运行

猜你喜欢

转载自blog.csdn.net/weixin_46687295/article/details/106162371