线程同步
(线程同步来解决线程安全问题)有三种解决方式
1 同步代码块
2 同步方法 (还有静态方法)
3 Lock锁
同步技术原理
1 解决同步线程安全的第一种方式(同步代码块)
package lwq.day06.xiancheng.Sychronized;
/**
* @author: LWQ
* @description
* @date: 2021/9/5 21:39
*/
/*
目的:实现买票案例
卖票案例出现了线程安全问题
卖出了不存在的票以及重复的票
解决线程安全问题的一种方法:使用同步代码块
格式:
Sychronized(锁对象){
可能会出现的线程安全问题的代码(访问了共享数据的代码)
}
注意:
2 通过代码块的锁对象 可以使用任意的对象
2 但是必须保证多个线程使用的锁对象是同一个
3 锁对象的作用:
把同步代码块锁住 只让一个线程在同步代码块中执行
*/
public class RunnableImpl implements Runnable{
//定义一个多个线程共享的票源
private int ticket = 100;
//创建一个锁对象
Object object = new Object();
//设置线程任务 :卖票
@Override
public void run() {
//使用死循环 让卖票操作 重复执行
while(true){
//同步代码块
synchronized(object){
//先判断票是否存在
if(ticket>0) {
//提高安全问题出现的概率 可以让程序睡眠一下
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票存在 再卖票ticket--
System.out.println(Thread.currentThread().getName() + "--->" + "正在卖第" + ticket + "张票");
ticket--;
}
}
}
}
}
2 解决同步线程安全的第二种方式(同步方法)
- 同步方法:使用synchronized修饰方法 就叫做同步方法 保证A线程执行该方法的时候 其他线程就只能在方法外等着
public synchronized void method(){
可能会产生线程安全问题的代码
}
电影票卖票案例的实现
package lwq.day06.xiancheng.Demo02synchronize;
/**
* @author: LWQ
* @description
* @date: 2021/9/5 21:39
*/
/*
目的:实现买票案例
卖票案例出现了线程安全问题
卖出了不存在的票以及重复的票
解决线程安全问题的一种方法:使用同步方法
使用步骤::
1 把访问了共享数据的代码抽取出来 放到一个方法中
2 在方法上添加synchronized的修饰符
格式:定义方法的格式
修饰符 synchronized 返回值类型 方法名(参数列表){
可能会产生线程安全问题的代码(访问了共享数据的代码)
}
*/
public class RunnableImpl implements Runnable {
//定义一个多个线程共享的票源
private int ticket = 10;
//创建一个锁对象
Object object = new Object();
//设置线程任务 :卖票
@Override
public void run() {
System.out.println("this:"+this);
//使用死循环 让卖票操作 重复执行
while (true) {
payTicket();
}
}
/*
定义一个同步方法
同步方法也会把方法内部的代码锁住
同步方法的锁对象:也就是线程的实现类对象 new RunnableImpl()
也就是this
*/
public synchronized void payTicket() {
//先判断票是否存在
if (ticket > 0) {
//提高安全问题出现的概率 可以让程序睡眠一下
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票存在 再卖票ticket--
System.out.println(Thread.currentThread().getName() + "--->" + "正在卖第" + ticket + "张票");
ticket--;
}
}
}
package lwq.day06.xiancheng.Demo02synchronize;
/**
* @author: LWQ
* @description 测试类
* @date: 2021/9/5 22:05
*/
/*
目的:模拟卖票案例
创建3个线程 同时开启 对共享的票出售
*/
public class Ticket {
public static void main(String[] args) {
//创建Runnable接口的实现类对象(创建一个实现类)
RunnableImpl run = new RunnableImpl();
System.out.println("run:"+run);
//创建Thread类对象 构造方法中传递Runnable接口的实现类对象
Thread t0 = new Thread(run);
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
//调用start方法 开启多线程
t0.start();
t1.start();
t2.start();
}
}
运行效果:
run:lwq.day06.xiancheng.Demo02synchronize.RunnableImpl@404b9385
this:lwq.day06.xiancheng.Demo02synchronize.RunnableImpl@404b9385
this:lwq.day06.xiancheng.Demo02synchronize.RunnableImpl@404b9385
this:lwq.day06.xiancheng.Demo02synchronize.RunnableImpl@404b9385
Thread-0--->正在卖第10张票
Thread-0--->正在卖第9张票
Thread-0--->正在卖第8张票
Thread-0--->正在卖第7张票
Thread-0--->正在卖第6张票
Thread-0--->正在卖第5张票
Thread-0--->正在卖第4张票
Thread-0--->正在卖第3张票
Thread-0--->正在卖第2张票
Thread-0--->正在卖第1张票
(静态同步方法)
package lwq.day06.xiancheng.Demo02synchronize;
/**
* @author: LWQ
* @description
* @date: 2021/9/5 21:39
*/
/*
目的:实现买票案例
卖票案例出现了线程安全问题
卖出了不存在的票以及重复的票
解决线程安全问题的一种方法:使用同步方法
使用步骤::
1 把访问了共享数据的代码抽取出来 放到一个方法中
2 在方法上添加synchronized的修饰符
格式:定义方法的格式
修饰符 synchronized 返回值类型 方法名(参数列表){
可能会产生线程安全问题的代码(访问了共享数据的代码)
}
*/
public class RunnableImpl implements Runnable {
//定义一个多个线程共享的票源
private static int ticket = 10;
//创建一个锁对象
Object object = new Object();
//设置线程任务 :卖票
@Override
public void run() {
System.out.println("this:"+this);
//使用死循环 让卖票操作 重复执行
while (true) {
payTicketStatic();
}
}
/*
定义一个静态的同步方法
锁对象是谁?
不能是this
this是创建对象之后产生的 静态方法优先于对象
静态方法的锁对象是本类的class属性----->class文件对象(反射)
*/
public static synchronized void payTicketStatic() {
//先判断票是否存在
if (ticket > 0) {
//提高安全问题出现的概率 可以让程序睡眠一下
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票存在 再卖票ticket--
System.out.println(Thread.currentThread().getName() + "--->" + "正在卖第" + ticket + "张票");
ticket--;
}
}
}
解决同步线程安全的第三种方式(Lock锁)
java.util.concurrent.locks 接口 Lock
public interface LockLock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。
package lwq.day06.xiancheng.Lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author: LWQ
* @description
* @date: 2021/9/5 21:39
*/
/*
目的:实现买票案例
卖票案例出现了线程安全问题
卖出了不存在的票以及重复的票
解决线程安全问题的一种方法:使用Lock锁
java.util.concurrent.locks.Lock接口
所有已知实现类: ReentrantLock implement Lock接口
Lock接口的方法:
void lock() 获取锁
void unlock() 释放锁
使用步骤:
1 在成员位置创建一个ReentrantLock类
2 在可能出现安全问题的代码前调用Lock接口中的方法lock获取锁
3 在可能出现安全问题的代码后调用Lock接口中的方法unlock获取锁
*/
public class RunnableImpl implements Runnable {
//定义一个多个线程共享的票源
private int ticket = 100;
//1 在成员位置创建一个ReentrantReadWriteLock类
Lock l = new ReentrantLock();
//设置线程任务 :卖票
@Override
public void run() {
//使用死循环 让卖票操作 重复执行
while(true){
//2 在可能出现安全问题的代码前调用Lock接口中的方法lock获取锁
l.lock();
//先判断票是否存在
if (ticket > 0) {
//提高安全问题出现的概率 可以让程序睡眠一下
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票存在 再卖票ticket--
System.out.println(Thread.currentThread().getName() + "--->" + "正在卖第" + ticket + "张票");
ticket--;
}
//3 在可能出现安全问题的代码后调用Lock接口中的方法unlock获取锁
l.unlock();
}
}
}
改进写法
package lwq.day06.xiancheng.Lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author: LWQ
* @description
* @date: 2021/9/5 21:39
*/
/*
目的:实现买票案例
卖票案例出现了线程安全问题
卖出了不存在的票以及重复的票
解决线程安全问题的一种方法:使用Lock锁
java.util.concurrent.locks.Lock接口
所有已知实现类: ReentrantLock implement Lock接口
Lock接口的方法:
void lock() 获取锁
void unlock() 释放锁
使用步骤:
1 在成员位置创建一个ReentrantLock类
2 在可能出现安全问题的代码前调用Lock接口中的方法lock获取锁
3 在可能出现安全问题的代码后调用Lock接口中的方法unlock获取锁
*/
public class RunnableImpl implements Runnable {
//定义一个多个线程共享的票源
private int ticket = 100;
//1 在成员位置创建一个ReentrantReadWriteLock类
Lock l = new ReentrantLock();
//设置线程任务 :卖票
@Override
public void run() {
//使用死循环 让卖票操作 重复执行
while(true){
//2 在可能出现安全问题的代码前调用Lock接口中的方法lock获取锁
l.lock();
//先判断票是否存在
if (ticket > 0) {
//提高安全问题出现的概率 可以让程序睡眠一下
try {
Thread.sleep(10);
//票存在 再卖票ticket--
System.out.println(Thread.currentThread().getName() + "--->" + "正在卖第" + ticket + "张票");
ticket--;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//3 在可能出现安全问题的代码后调用Lock接口中的方法unlock获取锁
l.unlock();
}
}
}
}
}