下面我们还是用看电影卖票的案例来谈一谈多线程安全的问题。
方案一:使用同步代码块
//实现卖票案例
/*
卖票案例出现线程安全问题
解决方案一:使用同步代码块
格式:
synchronized(锁对象){
可能会出现线程安全问题的代码(访问了共享数据的代码)
}
注意:
1.通过代码块中的锁对象,可以使用任意的对象
2.但是必须保证多个线程使用的锁对象是同一个
3.锁对象作用:
把同步代码块锁住,只让一个线程在同步代码块中执行
*/
/*
总结:同步中的线程,没有执行完毕不会释放锁,同步外的线程没有锁进不去同步
同步保证了只能有一个线程在同步中执行共享数据,保证了安全
程序频繁的判断锁,获取锁,释放锁,程序的效率会降低
*/
public class RunnableImpl implements Runnable{
//定义一个多线程共享的票源
private int piao = 100;
//创建一个锁对象
Object obj = new Object();
@Override//设置任务买票
public void run() {
while (true){//使用死循环让买票操作重复着执行
//创建同步代码块
synchronized (obj){
if(piao>0){//判断票是否存在
//提高安全问题的出现概率,让程序睡眠
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票存在,买票,piao--
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+piao+"张票");
piao--;
}
}
}
}
}
测试类:
/*
1.单线程程序是不会出现线程安全问题的
2.多线程程序,没有访问共享数据,不会产生问题
3.多线程访问了共享的数据,会产生线程安全问题
*/
//模拟买票案例,创建三个线程,同时开启,对共享的票进行出售
public class Test01 {
public static void main(String[] args) {
//创建Runnable接口的实现类对象
RunnableImpl run = new RunnableImpl();
//创建Thread类对象,构造方法中传递Runnable接口的实现类对象
Thread thread1 = new Thread(run);
Thread thread2 = new Thread(run);
Thread thread3 = new Thread(run);
thread1.start();//开启多线程
thread2.start();
thread3.start();
/*
出现了线程安全问题,卖票出现了重复的票和不存在的票
注意:
线程安全问题是不能产生的,我们可以让一个线程访问共享数据的时候,无论是否失去了cpu的执行权;
让其他的线程只能等待,等待当前程序卖完票,其他线程在进行卖票
保证:使用一个线程在卖票
*/
}
}
方案二:使用同步方法
//实现卖票案例
/*
卖票案例出现了线程安全问题
解决线程安全问题方法二:使用同步方法
使用步骤:
1.把访问了共享数据的代码抽取出来,放到一个方法中
2.在方法上添加synchronized修饰符
格式:定义方法的格式
修饰符 synchronized 返回值类型 方法名(参数列表){
可能会出现线程问题的代码(访问了共享数据的代码)
}
*/
public class RunnableImpl implements Runnable {
//定义一个多线程共享的票源
private int piao = 100;
@Override//设置任务买票
public void run() {
while (true) {//使用死循环让买票操作重复着执行
method();
}
}
/*
同步方法也会把方法内部的代码锁住,只让一个线程执行
同步方法的锁对象是谁?
就是实现类对象new RunnableImpl()
也就是this
*/
public synchronized void method() {//定义一个同步方法
if (piao > 0) {//判断票是否存在
//提高安全问题的出现概率,让程序睡眠
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票存在,买票,piao--
System.out.println(Thread.currentThread().getName() + "-->正在卖第" + piao + "张票");
piao--;
}
}
}
测试类
public static void main(String[] args) {
//创建Runnable接口的实现类对象
RunnableImpl run = new RunnableImpl();
//创建Thread类对象,构造方法中传递Runnable接口的实现类对象
Thread thread1 = new Thread(run);
Thread thread2 = new Thread(run);
Thread thread3 = new Thread(run);
thread1.start();//开启多线程
thread2.start();
thread3.start();
/*
出现了线程安全问题,卖票出现了重复的票和不存在的票
注意:
线程安全问题是不能产生的,我们可以让一个线程访问共享数据的时候,无论是否失去了cpu的执行权;
让其他的线程只能等待,等待当前程序卖完票,其他线程在进行卖票
保证:使用一个线程在卖票
*/
}
}
当然同步方法也可以使用静态方法,原理差不多,大家可以自行摸索
方案三:使用Lock锁
//实现卖票案例
/*
卖票案例出现了线程安全问题
解决线程安全问题方法三:使用Lock锁
java.util.concurrent.locks.Lock接口
Lock实现按提了比使用synchronized方法和 语法可获得的更广泛的锁定操作
Lock接口中的方法:
void lock()获取锁
void unlock()释放锁
java.util.concurrent.locks.Reentrantlock implements Losk接口
使用步骤:
1.在成员位置创建一个Reentrantlock对象
2.在可能出现安全问题的代码前调用Lock接口中的方法lock获取锁
3.在可能出现安全问题的代码后调用Lock接口中的方法unlock释放锁
*/
public class RunnableImpl implements Runnable {
//定义一个多线程共享的票源
private int piao = 100;
//1.在成员位置创建一个Reentrantlock对象
Lock lock = new ReentrantLock();
@Override//设置任务买票
public void run() {
while (true) {//使用死循环让买票操作重复着执行
method();
}
}
public synchronized void method() {//定义一个同步方法
//2.在可能出现安全问题的代码前调用Lock接口中的方法lock获取锁
lock.lock();
if (piao > 0) {//判断票是否存在
//提高安全问题的出现概率,让程序睡眠
try {
Thread.sleep(10);
//票存在,买票,piao--
System.out.println(Thread.currentThread().getName() + "-->正在卖第" + piao + "张票");
piao--;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//3.在可能出现安全问题的代码后调用Lock接口中的方法unlock释放锁
lock.unlock();//无论是否出现异常,都会把锁释放,可以提高程序的效率
}
}
}
}
测试类
/*
1.单线程程序是不会出现线程安全问题的
2.多线程程序,没有访问共享数据,不会产生问题
3.多线程访问了共享的数据,会产生线程安全问题
*/
//模拟买票案例,创建三个线程,同时开启,对共享的票进行出售
public class Test01 {
public static void main(String[] args) {
//创建Runnable接口的实现类对象
RunnableImpl run = new RunnableImpl();
//创建Thread类对象,构造方法中传递Runnable接口的实现类对象
Thread thread1 = new Thread(run);
Thread thread2 = new Thread(run);
Thread thread3 = new Thread(run);
thread1.start();//开启多线程
thread2.start();
thread3.start();
/*
出现了线程安全问题,卖票出现了重复的票和不存在的票
注意:
线程安全问题是不能产生的,我们可以让一个线程访问共享数据的时候,无论是否失去了cpu的执行权;
让其他的线程只能等待,等待当前程序卖完票,其他线程在进行卖票
保证:使用一个线程在卖票
*/
}
}
以上就是我总结出来的三种线程安全问题的解决方案,希望对大家有所帮助,测试结果没有发出来,大家可以自行测试一下,学知识重点还是在于理解,学java就是要多敲敲代码,也许你不会,敲着敲着你就会了。