1、线程安全问题的概述
多线程访问了共享的数据,就会产生线程安全问题
2、线程安全问题的代码实现
package exception2.threadsSafe;
/**
* created by apple on 2020/6/22,实现卖票任务
*/
public class RunnableImp implements Runnable{
//定义多个线程共享得票源
private int ticket = 100;
@Override
public void run() {
//先判断票是否存在 。循环重复卖票。。让卖票操作重复执行
while (true){
if (ticket > 0){
//提高安全问题出现的概率,让程序睡眠..会有很大的线程安全,有很多重复的票和-1 的票。
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票存在,买票 ticket
System.out.println(Thread.currentThread().getName()+"正在卖第" + ticket + "票");
ticket--;
}
}
}
}
package exception2.threadsSafe;
/**
* 模拟卖票案例
* 创建3个线程,同时开启,对共享的票,出售
* created by apple on 2020/6/22
*/
public class Demo01Ticket {
public static void main(String[] args) {
//创建Runnable接口的实现类对象
RunnableImp rp = new RunnableImp();
//创建Thread类对象,构造方法中传递Runnable接口的实现类对象 。。创建的一个实现类,传到3个线程里,保证票让3个线程卖
Thread t0 = new Thread(rp);
Thread t1 = new Thread(rp);
Thread t2 = new Thread(rp);
//调用start方法开启多线程
t0.start();
t1.start();
t2.start();
}
}
输出:会出现重复的票和-1 的票
Thread-0正在卖第100票
Thread-2正在卖第100票
Thread-1正在卖第100票
...
Thread-1正在卖第0票
Thread-0正在卖第-1票
卖票出现了重复的票和不存在的票
3、线程安全问题产生的原理
对共享资源的抢夺可能同时
解决线程安全问题_3种方法 :同步代码块 、同步方法,、lock锁机制
4、解决线程安全问题_1同步代码块
sychronized(锁对象){
可能会出现线程安全问题的代码,即访问了共享数据的代码
}
注意:
- 1、同步代码块中的锁对象,可以使用任意的对象
- 2、但是需保证多个线程使用的锁对象是同一个
- 3、锁对象的作用: 把同步代码锁住,只让一个线程在同步代码块中执行
package exception2.sychronized;
/**
* created by apple on 2020/6/22
* 卖票案例出现了线程安全问题
* 卖出了重复的票和不存在的票
* 解决线程安全问题的第一种方案,用同步代码块
* 格式:
* sychronized(锁对象){
* 可能会出现线程安全问题的代码,即访问了共享数据的代码
* }
* 注意:
* 1、同步代码块中的锁对象,可以使用任意的对象
* 2、但是需保证多个线程使用的锁对象是同一个
* 3、锁对象的作用:
* 把同步代码锁住,只让一个线程在同步代码块中执行
*/
public class RunnableImp implements Runnable{
//定义多个线程共享得票源
private int ticket = 100;
//创建一个锁对象
Object obj = new Object();
@Override
public void run() {
//先判断票是否存在 。循环重复卖票。。让卖票操作重复执行
while (true){
//同步代码块,保证线程安全
synchronized (obj){
if (ticket > 0){
//提高安全问题出现的概率,让程序睡眠..会有很大的线程安全,有很多重复的票和-1 的票。
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票存在,买票 ticket
System.out.println(Thread.currentThread().getName()+"正在卖第" + ticket + "票");
ticket--;
}
}
}
}
}
这样就解决了线程安全问题
5、同步技术的原理
同步代码块,获取锁对象, 释放锁对象
6、解决线程安全问题_2同步方法
解决线程安全问题的第二种方案,用同步方法
- 实现步骤:
- 1、把访问了共享数据的代码,抽取出来,放到一个方法中
- 2、在方法上添加sychronized修饰符
- 定义方法的格式:
修饰符 sychronized 返回值类型 方法名(参数列表){
访问了共享数据的代码
}
package exception2.sychronized02;
/**
* created by apple on 2020/6/22
* 卖票案例出现了线程安全问题
* 卖出了重复的票和不存在的票
* 解决线程安全问题的第二种方案,用同步方法
* 实现步骤:
* 1、把访问了共享数据的代码,抽取出来,放到一个方法中
* 2、在方法上添加sychronized修饰符
* 定义方法的格式:
* 修饰符 sychronized 返回值类型 方法名(参数列表){
* 访问了共享数据的代码
* }
*
* 注意:
* 1、通过代码块中的锁对象,可以使用任意的对象
* 2、但是需保证多个线程使用的锁对象是同一个
* 3、锁对象的作用:
* 把同步代码锁住,只让一个线程在同步代码块中执行
*/
public class RunnableImp implements Runnable{
//定义多个线程共享得票源
private int ticket = 100;
//创建一个锁对象
@Override
public void run() {
//先判断票是否存在 。循环重复卖票。。让卖票操作重复执行
while (true){
payTicket();
}
}
//定义一个同步方法.同步方法也会把方法内部的代码锁住。
//只让一个线程执行。
/*
同步方法的锁对象,是线程的实现类对象。new RunnableImp()
*/
public synchronized void payTicket(){
if (ticket > 0){
//提高安全问题出现的概率,让程序睡眠..会有很大的线程安全,有很多重复的票和-1 的票。
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票存在,买票 ticket
System.out.println(Thread.currentThread().getName()+"正在卖第" + ticket + "票");
ticket--;
}
}
}
7、静态同步方法
8、解决线程安全问题_3Lock锁
java.util.cicurrent.locks.Lock接口, 有方法获取锁 lock, unlock 释放锁
lock实现提供了比使用synchronized方法和语句可获得更广泛的锁操作
package exception2.sychronized03;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* created by apple on 2020/6/22
* 卖票案例出现了线程安全问题
* 卖出了重复的票和不存在的票
* 解决线程安全问题的第三种方案,用lock锁
* java.util.concurrent.locks.lock接口
* 比sychronized更好用。
*lock 接口中的 方法:
lock():获取锁
unlock():释放锁
* 使用步骤:
* 1、接口的实现类,
java.util.concurrent.locks.Reentrantlock 实现了lock接口
在成员位置创建一个Reentrantlock对象
* 2、在可能出现线程安全问题的代码前,调用lock接口中的方法lock(),获取锁
* 3、在可能出现安全问题的代码后,调用lock接口中的方法unlock(),释放锁
*/
public class RunnableImp implements Runnable{
//定义多个线程共享得票源
private int ticket = 100;
//1、 在成员位置创建一个Reentrantlock对象
Lock l = new ReentrantLock();
@Override
public void run() {
//2、在可能出现线程安全问题的代码前,调用lock接口中的方法lock(),
l.lock();
//先判断票是否存在 。循环重复卖票。。让卖票操作重复执行
while (true){
//2、在可能出现线程安全问题的代码前,调用lock接口中的方法lock(),
l.lock();
if (ticket > 0){
//提高安全问题出现的概率,让程序睡眠..会有很大的线程安全,有很多重复的票和-1 的票。
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票存在,买票 ticket
System.out.println(Thread.currentThread().getName()+"正在卖第" + ticket + "票");
ticket--;
}
l.unlock();
}
}
}
优化下上面的写锁,把 l.unlock()放在finally中, 无论程序是否异常,都会释放锁
package exception2.sychronized03;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* created by apple on 2020/6/22
* 卖票案例出现了线程安全问题
* 卖出了重复的票和不存在的票
* 解决线程安全问题的第三种方案,用lock锁
* java.util.concurrent.locks.lock接口
* 比sychronized更好用。
*lock 接口中的 方法:
lock():获取锁
unlock():释放锁
* 使用步骤:
* 1、接口的实现类,
java.util.concurrent.locks.Reentrantlock 实现了lock接口
在成员位置创建一个Reentrantlock对象
* 2、在可能出现线程安全问题的代码前,调用lock接口中的方法lock(),获取锁
* 3、在可能出现安全问题的代码后,调用lock接口中的方法unlock(),释放锁
*/
public class RunnableImp implements Runnable{
//定义多个线程共享得票源
private int ticket = 100;
//1、 在成员位置创建一个Reentrantlock对象
Lock l = new ReentrantLock();
@Override
public void run() {
//2、在可能出现线程安全问题的代码前,调用lock接口中的方法lock(),
l.lock();
//先判断票是否存在 。循环重复卖票。。让卖票操作重复执行
while (true){
//2、在可能出现线程安全问题的代码前,调用lock接口中的方法lock(),
l.lock();
if (ticket > 0){
//提高安全问题出现的概率,让程序睡眠..会有很大的线程安全,有很多重复的票和-1 的票。
try {
Thread.sleep(10);
//票存在,卖票 ticket
System.out.println(Thread.currentThread().getName()+"正在卖第" + ticket + "票");
ticket--;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//无论代码是否异常,都会是释放锁对象
l.unlock();
}
}
}
}











网友评论