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();
}
}
}

没有重复或负数。