6.8 线程安全问题概述
- 图示:
6.9线程安全问题的代码实现
- 示例:
package com.xww.demo05;
public class RunnableImpl implements Runnable{
private int ticket = 100;
@Override
public void run() {
//使用死循环,让卖票操作重复执行
while(true){
//先判断票是否存在
if(ticket>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+ticket);
ticket--;
}
}
}
}
package com.xww.demo05;
/*
模拟卖票
创建3个进程,同时开启,对共享票进行出售
*/
public class demoTick {
public static void main(String[] args) {
//创建Runnable接口的实现类对象
RunnableImpl run = new RunnableImpl();
//创建Thread类对象,构造方法中传递Runnable接口的实现类对象
Thread t0 = new Thread(run);
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
//调用start方法开启多线程
t0.start();
t1.start();
t2.start();
}
}
6.10 线程安全问题产生的原理
- 图示:
6.11 线程安全问题_同步代码块
- 同步代码块: synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。
- 格式:
synchronized(锁对象){
可能出现线程安全问题的代码(访问了共享数据的代码);
}
- notes:
- 通过代码块中的锁对象,可以使用任意的对象;
- 但是必须保证多个线程使用的锁对象是同一个;
- 锁对象的作用:把同步代码块锁住,只让一个线程在同步代码块中执行。
- 示例:
package com.xww.demo05;
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(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+ticket);
ticket--;
}
}
}
}
}
6.12 同步技术的原理
- 图示:
6.13 解决线程安全问题_同步方法
- 同步方法 :使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外 等着。
- 同步方法的对象:就是实现类对象 new RunnableImpl,也就是this。
- 格式:定义方法的格式
修饰符 synchronized 返回值类型 方法名(参数列表){
可能会产生线程安全问题的代码(访问量共享数据的代码);
}
- 使用步骤:
- 把访问了共享数据的代码抽取出来,放到一个方法中;
- 在方法中添加synchronized修饰符。
- 示例:
package com.xww.demo05;
public class RunnableImpl implements Runnable{
//定义一个多个线程共享的资源
private int ticket = 100;
//设置线程任务
@Override
public void run() {
//使用死循环,让卖票操作重复执行
while(true){
payTicket();
}
}
//定义一个同步方法
public synchronized void payTicket(){
//先判断票是否存在
if(ticket>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+ticket);
ticket--;
}
}
}
6.14 解决线程安全问题_静态同步方法
- 静态的同步方法的锁对象:
- 不能是this,因为this是创建对象之后产生的,静态方法优先于对象;
- 是本类的Class属性–>Class文件对象(反射)。
- 示例:
package com.xww.demo05;
public class RunnableImpl implements Runnable{
//定义一个多个线程共享的资源
private static int ticket = 100;
//设置线程任务
@Override
public void run() {
//使用死循环,让卖票操作重复执行
while(true){
payTicket();
}
}
//定义一个静态同步方法
public static synchronized void payTicket(){
//先判断票是否存在
if(ticket>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+ticket);
ticket--;
}
}
}
6.15 解决线程安全问题_Lock 锁
- java.util.concurrent.locks.Lock 机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。
- Lock锁 也称同步锁,加锁与释放锁方法化了。
- Lock接口中的方法如下:
public void lock()
:加同步锁。public void unlock()
:释放同步锁。
- java.util.concurrent.locks.ReentrantLock implements Lock接口
- 使用步骤:
- 在成员位置创建一个ReentrantLock对象;
- 在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁;
- 在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁。
- 示例:
package com.xww.demo05;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class RunnableImpl implements Runnable{
//定义一个多个线程共享的资源
private int ticket = 100;
//1.在成员位置创建一个ReentrantLock对象
Lock l = new ReentrantLock();
//设置线程任务
@Override
public void run() {
//使用死循环,让卖票操作重复执行
while(true){
//2.在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁;
//先判断票是否存在
if(ticket>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+ticket);
ticket--;
}
//3.在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁;
l.unlock();
}
}
}