在多线程共享资源时,会产生安全问题,因此要避免产生安全问题
模拟卖票,存在安全问题的代码:
public class Runnableimpl implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (true){
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+"在出售第"+ticket+"张票");
ticket--;
}
}
}
}
多线程执行:
public class TicketTest {
public static void main(String[] args) {
Runnable run = new Runnableimpl();
Thread t0 = new Thread(run);
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
t0.start();
t1.start();
t2.start();
}
}
可能产生的问题:
在进入if语句后,当前线程睡眠,释放cpu,其余线程开始执行,也进入了if语句,可能会导致出售的票是同一张,或出现第0、-1张票的情况。
第一种解决方案:使用同步代码块
/*
格式:
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){
System.out.println(Thread.currentThread().getName()+"在出售第"+ticket+"张票");
ticket--;
}
}
}
}
}
第二种解决方案:使用同步方法
/*
使用步骤:
1.把访问了共享数据的代码抽取出来,放到一个方法中
2.在方法上添加synchronized修饰符
格式:
修饰符 synchronized 返回值类型 方法名(参数列表){
可能出现线程安全问题的代码
}
*/
public class Runnableimpl implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (true){
payTicket();
}
}
public synchronized void payTicket(){
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+"在出售第"+ticket+"张票");
ticket--;
}
}
}
第三种解决方案:使用Lock锁
/*
第三种解决方案:
使用步骤:
1.在成员位置创建一个Reentrantlock对象
2.在可能会出现安全问题的代码前调用lock接口中的方法lock获取锁
3.在可能会出现安全问题的代码后调用lock接口中的方法unlock释放锁
*/
public class Runnableimpl implements Runnable{
private int ticket = 100;
Lock l = new ReentrantLock();
@Override
public void run() {
while (true){
l.lock();
if(ticket > 0){
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()+"在出售第"+ticket+"张票");
ticket--;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
l.unlock();
}
}
}
}
}