解决线程安全问题的第一种方案:使用同步代码块,保证安全
格式:
synchronized (锁对象){
可能出现线程安全问题的代码(访问了共享数据的代码)
}
注意:
1.同步代码块中的锁对象,可以使用任意的对象
2.必须保证多个线程使用的锁对象是同一个
3.锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行
缺点:程序频繁的判断,获取,释放锁,代码的效率会降低
public class RunnableImp2 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 (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");
ticket--;
}
}
}
}
}
主程序:
package com.itheima.demo01.Exception.Synchronized;
public class Demo01Ticket {
public static void main(String[] args) {
RunnableImp2 run = new RunnableImp2();
Thread t0 = new Thread(run);
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
t0.start();
t1.start();
t2.start();
}
}
通过同步保护不会出现卖票冲突的问题,如果不使用,会出现3个线程同时卖一张票的情况。
/*
解决线程安全问题的第二种方案:使用同步方法
使用步骤:
1.把访问了共享数据的代码抽取出来,放到一个方法中
2.在方法上添加synchronized修饰符
格式:
修饰符 synchronized 返回值类型 方法名(参数列表){
可能出现线程安全问题的代码(访问了共享数据的代码)
}
注意:
同步方法也会把方法内部的代码锁住只会让一个线程执行
同步方法的锁对象 new synchronized 也就是this
- */
public class RunnableImp2 implements Runnable {
//定义一个多个线程共享的资源
private int ticket = 100;
//设置线程任务:卖票
@Override
public void run() {
//使用死循环,让卖票操作重复执行
while(true){
payTicket();
}
//定义一个同步方法
}
public synchronized void payTicket() {
if (ticket > 0) {
//提高安全问题出现的概率,让程序睡眠
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");
ticket--;
}
}
}
/*
解决线程安全问题的第三种方案:使用Lock锁
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
Lock接口中的方法:
void Lock()获取锁
void unLock()释放锁
java.util.concurrent.locks.ReentrantLock implements Lock接口
使用步骤:
1.在成员位置创建一个ReentrantLock对象
2.在可能会出现安全问题的代码前调用Lock接口中的方法获取锁
3.在可能会出现安全问题的代码后调用Lock接口中的方法释放锁
- */
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class RunnableImp2 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 (Exception e) {
e.printStackTrace();
}finally {
l.unlock();//无论是否出现异常,都把锁释放
}
}
}
}
}