java多线程之线程安全问题

1、线程安全概述

操作系统中线程的调度是抢占式执行的,或者说是随机的,这就造成线程调度执行时线程的执行顺序是不确定的,有一些代码执行顺序不同不影响程序运行的结果,但也有一些代码执行顺序发生改变了重写的运行结果会受影响,这就造成程序会出现bug,对于多线程并发时会使程序出现bug的代码称作线程不安全的代码,这就是线程安全问题。

2、案例:卖票

package threaddemo;

public class Ticket implements Runnable{
    private int Ticket = 100;
    @Override
    public void run() {
        while(true){
            if(Ticket == 0){
                break;
            }else {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                Ticket--;
                System.out.println(Thread.currentThread().getName()+"在卖票,还剩"+Ticket+"张票");
            }
        }
    }

    public static class Damo {
        public static void main(String[] args) {
            Ticket ticket = new Ticket();
            Thread t1 = new Thread(ticket);
            Thread t2 = new Thread(ticket);
            Thread t3 = new Thread(ticket);
            t1.start();
            t2.start();
            t3.start();

        }
    }
}

我们定义了一个类Ticket实现Runnable接口,里面定义一个成员变量:private int ticketCount = 100;

在Ticket类中重写run()方法实现卖票,①判断票数大于0,就卖票,并告知是哪个窗口卖的 ②票数减一 ③卖光之后,线程停止\n\n注意:我们考虑到在出售一张票的时候,需要一点时间的延迟,每次出票需要100毫秒,用sleep方法去实现。

实现过程问题分析:

出现一张票卖两次

出现负号票

问题:①相同的票出现多次

②出现了负数票

分析问题:

假如我们把图标里的三个箭头当成三个线程,

程序开始运行时,绿色线程通过if语句到了try然后在这里睡了100ms,这时红色线程获得CPU运行权也通过if语句到达try,同理紫色线程也来到这里。

当绿色线程睡完100ms时,接着往下运行,就当快到达打印语句前时,在这个时间点,红色线程紧跟其后,紫色线程也不例外(在这个过程中,当绿色线程运行完ticket--语句后ticket的值为99,接着红色线程运行ticket--语句后ticket值为98,紫色也是如此,最终在还没打印时,ticket的值为97)

这时重复票就出现了。

当我们把ticket的值改为1时。

我们再运行程序

这时绿色线程已经到达打印语句并输出:线程一 在卖票,还剩下0张票 ,由于后面两个线程在ticket=0之前就经过了if语句,最终还是进来了,进来了就得执行了里面得语句吧,执行ticket--语句ticket的值就成为负值。

在这些安全问题其本质就是多条线程操作共享数据而产生的错误。

如何解决多线程安全问题呢?基本思想:让程序没有安全问题的环境

怎么实现?~把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可。~java提供了同步代码块的方式来解决。

锁多条语句操作共享数据,可以使用同步代码块实现:

~~格式为

synchronized(任意对象){
多条语句操作共享数据的代码
}

默认情况是打开的,只要有一个线程进去执行代码了,锁就会关闭,当线程执行玩了出来了,锁才会自动打开。

同步的好处和弊端:~利:解决了多线程的数据安全问题

~弊:当线程很多时,因为每个线程都会去判断同步上的锁,这是很浪费资源的,无行中会降低程序的运行效率。

加锁:

package threaddemo;

public class Ticket implements Runnable{
    private int Ticket = 100;
    private Object obj = new Object();
    @Override
    public void run() {
        while (true) {
            synchronized (obj) {//多个线程必须使用同一个锁
                if (Ticket == 0) {
                    break;
                } else {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    Ticket--;
                    System.out.println(Thread.currentThread().getName() + "在卖票,还剩" + Ticket + "张票");
                }
            }
        }
    }
    public static class Damo {
        public static void main(String[] args) {
            Ticket ticket = new Ticket();
            Thread t1 = new Thread(ticket);
            Thread t2 = new Thread(ticket);
            Thread t3 = new Thread(ticket);
            t1.start();
            t2.start();
            t3.start();

        }
    }
}

没有重复或负数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值