线程安全
在多条线程访问的情况下,程序不能按照我们预期的结果进行,如下图所示:
线程安全的解决方案
1.同步代码块
synchronized关键字,就是用来控制线程同步的,保证我们的线程在多线程环境下,不被多个线程同时执行,确保我们数据的完整性。
格式:synchronized(锁对象){}
简单点就是创建一个对象,然后把这个对象当成一个餐厅大妈,每个线程看成一个个人。当一个个人排队打饭的时候,只有等每个人打饭完后,才能轮到下一个,也就是强制排队。
static class Ticket implements Runnable{
//票数
private int count = 10;
private Object o = new Object();
@Override
public void run() {
while (true) {
synchronized (o) {
if (count > 0) {
//买票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//出票
count--;
//出票成功
System.out.println(Thread.currentThread().getName()+"出票成功,余票:" + count);
}
else{
break;
}
}
}
}
}
2.同步方法
把要处理的活动封装成一个方法,然后用synchronized对方法进行修饰,然后把方法返回一个boolean型的数据。相对于第一种的同步代码块而言,不用再去创建对象,直接用synchronized修饰方法就可以进行排队打饭,只有一个线程结束了,下一个线程才能够进来。
static class Ticket implements Runnable{
//票数
private int count = 10;
@Override
public void run() {
while (true) {
boolean flag = sale();
if (!flag){
break;
}
}
}
public synchronized boolean sale(){
if (count > 0) {
//买票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//出票
count--;
//出票成功
System.out.println(Thread.currentThread().getName()+"出票成功,余票:" + count);
return true;
}
else{
return false;
}
}
}
3.Lock锁
Lock是一个接口,无法创建实例,所以在这边我们创建了它的子类ReentrantLock,然后继承了父类的Lock的方法lock(),用来获取锁。unlock()用来释放锁。使用的方法就是线程进入时,用lock()锁住,结束时用unlock()进行解锁。
static class Ticket implements Runnable{
//票数
private int count = 10;
//显示锁Lock 子类 ReentrantLock
Lock l = new ReentrantLock();
@Override
public void run() {
while (true) {
l.lock();
if (count > 0){
//买票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//出票
count--;
//出票成功
System.out.println("出票成功,余票:" + count);
}else{
break;
}
l.unlock();
}
}
}
欢迎评价