什么是线程安全问题?
我们通过代码来了解一下,下面是一个模拟卖票的案例,
首先我们先定义一个票Ticked类实现Runnable接口:
class Ticket implements Runnable{
int ticked = 50;//一共50张票
@Override
public void run() {
while (true) {
if(ticked > 0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticked--;
System.out.println("卖了一张票,还剩" + ticked + "张票");
}else{
System.out.println("票已售罄!");
break;
}
}
}
}
然后定义一个测试类:模拟三个窗口卖票
public class Test1 {
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();
}
}
运行一下看效果:
可以看到我们的余票有两次剩余两张,还出现了余票为负数的情况,这就是线程安全问题,那么为什么会出现线程安全问题呢?
这是因为:多线程操作共享数据时,导致共享数据出错。
那么怎么解决这个问题呢?
有三种方法:
- 同步代码块
- 同步方法
- Lock锁
我们先看第一种:使用同步代码块
只需要将操作共享数据的代码放在 synchronized 里面就可以了
class Ticket implements Runnable{
int ticked = 50;
@Override
public void run() {
while (true) {
synchronized (""){//这里小括号里面放的是 锁 对象,任何对象都可以是锁,但这个对象要是唯一的
if(ticked > 0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticked--;
System.out.println("卖了一张票,还剩" + ticked + "张票");
}else{
System.out.println("票已售罄!");
break;
}
}
}
}
}
同步锁是谁?
对于非 static 方法,同步锁就是 this
对于 static 方法,我们使用当前方法所在类的字节码对象(类名.class)
再来看一下运行结果:完全没有问题
第二种:将操作共享数据的代码抽取出来放到一个方法里面就可以了
class Ticket implements Runnable{
int ticked = 50;
@Override
public void run() {
while (true) {
sellTicked();
}
}
private synchronized void sellTicked() {
if(ticked > 0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticked--;
System.out.println("卖了一张票,还剩" + ticked + "张票");
}
}
}
运行看效果:是不是也完全没问题
第三种:使用Lock锁,用法其实与第一种差不多,我们看代码:
提示:这里讲一下Lock的两个方法:
- public void lock() :加同步锁。
- public void unlock() :释放同步锁。
class Ticket implements Runnable{
int ticked = 50;
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
if(ticked > 0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticked--;
System.out.println("卖了一张票,还剩" + ticked + "张票");
}else{
System.out.println("票已售罄!");
break;
}
lock.unlock();
}
}
}
运行看效果:完全没问题