ThreadSafe解决线程安全的问题
创建一个Runnable接口的实现类,实现一个卖票的案例
package demo06.ThreadSafe;/*
*@author wanghongyuan
*@Create 2021/1/5 8:02
*/
/*
实现买票案例
*/
public class RunnableImpl implements Runnable{
// 定义一个多个线程共享的票源
private int ticket = 100;
// 设置线程任务买票
@Override
public void run() {
// 使用死循环,让卖票操作重复执行
while (true){
// 先判断票是否存在
if (ticket> 0){
// 提高安全问题出现的概率,让程序睡眠
try{
Thread.sleep(mills:10);
}catch (InterruptedException e){
e.printStackTrace();
}// 票存在,卖票,ticket--
System.out.println(Thread.currentThread().getName()+"正在卖票-->"+ticket+"张票");
ticket--;
}
}
}
}
创建一个main执行卖票案例
通过这个案例我们发现,如果三个线程同时共享一个票源,不经过处理的话,三个线程同时抢占CPU资源,就会出现线程安全问题,会有多个线程同时卖一张相同票的情况。
package ThreadDemo;/*
@Author wanghongyuan
@Date 2021/1/5
*/
/*
买票出现了线程安全的问题
卖出了不存在和重复的票
解决线程安全问题的第一种方案:使用同步代码块
格式:
synchronized(锁对象){
可能会出现线程安全问题的代码(访问了共享数据的代码)
}
注意:
1.通过代码块中的锁对象,可以使用任意的对象。
2.但是必须保证多个线程使用的锁对象是同一个
3.锁对象的作用:
把同步代码块锁住,只让一个线程在同步代码块中执行
*/
public class Demo01ThreadSafe {
public static void main(String[] args) {
RunnableImpl run = new RunnableImpl();
Thread T0= new Thread(run);
Thread T1= new Thread(run);
Thread T2= new Thread(run);
T0.start();
T1.start();
T2.start();
}
}
使用同步代码块解决线程安全问题
package ThreadDemo;/*
@Author wanghongyuan
@Date 2021/1/5
*/
/*
买票出现了线程安全的问题
卖出了不存在和重复的票
解决线程安全问题的第一种方案:使用同步代码块
格式:
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--;
}
}
}
}
}
使用静态方法
package newww;/*
@Author wanghongyuan
@Date 2021/1/5
*/
/*
买票出现了线程安全的问题
卖出了不存在和重复的票
解决线程安全问题的第三种方案:使用静态方法
格式:
synchronized(锁对象){
可能会出现线程安全问题的代码(访问了共享数据的代码)
}
注意:
1.通过代码块中的锁对象,可以使用任意的对象。
2.但是必须保证多个线程使用的锁对象是同一个
3.锁对象的作用:
把同步代码块锁住,只让一个线程在同步代码块中执行
*/
public class RunnableImpl implements Runnable{
private static int ticket = 100;
// 创建一个锁对象
Object obj = new Object();
@Override
public void run() {
while (true){
payTicketStatic();
}
}
/*
静态的同步方法
锁对象是谁?
不能是this
this是创建对象之后产生的,静态方法优先于对象
静态方法的锁对象是本类的class属性-->class文件对象(反射)
*/
public static /*synchronized*/ void payTicketStatic() {
while (true){
// 创建一个同步代码块 RunnableImpl.class这个也是一个对象。
synchronized (RunnableImpl.class){
if (ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在售23票"+ticket+"张");
ticket--;
}
}
}
}
}
使用Lock解决线程安全问题
package Demo09.Lock;/*
@Author wanghongyuan
@Date 2021/1/5
*/
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
卖票案例出现了线程安全的问题
卖出了不存在和重复的票
解决线程安全的问题第四种方案:使用Lock锁
java.util.concurrent.Locks.Lock接口
Lock中的方法:
void lock()获取锁
void unlock()释放锁
java.util.concurrent.Locks.ReentrantLock implement lock接口
使用步骤:
1.在成员位置创建一个ReentrantLock对象
2.在可能会出现安全问题的代码前调用lock接口中的方法lock获取锁
3.在可能会出现安全问题的代码后调用lock接口中的方法unlock释放锁
*/
public class RunnableImpl 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()+"正在1卖第"+ticket+"张票");
ticket--;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
// 3.在可能会出现安全问题的代码后调用lock接口中的方法unlock释放锁
l.unlock();// 无论程序是否异常,都释放锁
}
}
}
/* while (true){
// 2.在可能会出现安全问题的代码前调用lock接口中的方法lock获取锁
l.lock();
if (ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
ticket--;
}
// 3.在可能会出现安全问题的代码后调用lock接口中的方法unlock释放锁
l.unlock();
}*/
}
}