实现卖票案例出现了线程安全问题,卖出了不存在和重复的票
-
解决线程安全的第一种方法:使用同步代码块
格式:
synchronized(锁对象){
可能会出现线程安全问题的代码(访问了共享数据的代码)
}注意:
1.同步代码块中的锁对象,可以使用任意对象
2.但是必须保证多个线程使用的锁对象是同一个
3.锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行原理总结:同步的线程,没有执行完毕不会释放锁,同步外的线程没有锁进不去同步
例:
public class RunnableImpl implements Runnable{
//定义一个多个线程共享的票源
private int ticket=100;
//创建一个锁对象
Object obj=new Object();
@Override
public void run() {
//使用死循环,让卖票操作重复执行
while (true){
//同步代码块
synchronized (obj){
//先判断票是否存在
if (ticket>0){
//提高安全问题出现的概率,让程序睡眠
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卖票
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
}
}
}
}
}
2.解决线程安全的第二种方法:使用同步方法
使用步骤:
1.把访问了共享数据的代码抽取出来,放到一个方法中
2.在方法上添加一个synchronized修饰符
格式:定义方法的格式
修饰符 synchronized 返回值类型 方法名(参数列表){
访问了共享数据的代码
}
原理:同步方法也会把方法内部的代码锁住,只让一个线程执行,同步方法的锁对象就是实现类对象new RunnableImpl,也就是this
例:
public class RunnableImpl implements Runnable{
//定义一个多个线程共享的票源
private int ticket=100;
//创建一个锁对象
Object obj=new Object();
@Override
public void run() {
//使用死循环,让卖票操作重复执行
while (true) {
payTicket();
}
}
//定义一个同步方法
public synchronized void payTicket(){
//先判断票是否存在
if (ticket > 0) {
//提高安全问题出现的概率,让程序睡眠
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卖票
System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");
ticket--;
}
}
}
-
解决线程安全的第三种方法:使用Lock锁
使用步骤:
1.在成员位置创建一个ReentrantLock对象
2.在可能会出现线程安全问题的代码前调用Lock接口中的方法Lock获取锁
3.在可能会出现线程安全问题的代码后调用Lock接口中的方法unLock释放锁
例:
public class RunnableImpl2 implements Runnable{
//定义一个多个线程共享的票源
private int ticket=100;
// 1.在成员位置创建一个ReentrantLock对象
Lock l=new ReentrantLock();
@Override
public void run() {
//使用死循环,让卖票操作重复执行
while (true){
//2.在可能会出现线程安全问题的代码前调用Lock接口中的方法Lock获取锁
l.lock();
//先判断票是否存在
if (ticket>0){
//提高安全问题出现的概率,让程序睡眠
try {
Thread.sleep(10);
//卖票
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
// 3.在可能会出现线程安全问题的代码后调用Lock接口中的方法unLock释放锁
l.unlock();
}
}
}
}
}