1.线程安全问题的概述
2.线程安全问题的代码实现和分析
package com.study_06.ThreadSafe;
public class RunnableImpl implements Runnable {
// 定义一个多个线程共享的票源
private int ticket = 100;
// 卖票
@Override
public void run() {
// 使用死循环 让买票重复执行
while (true) {
// 先判断票是否存在
if (ticket > 0) {
// 提高安全问题出现的概率,让程序睡眠
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票存在 ticket---
System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");
ticket--;
}
}
}
}
package com.study_06.ThreadSafe;
public class Demo01Ticket {
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();
}
}
3.线程同步
- 同步代码块
- 同步方法
- 锁机制
4.同步代码块
同步代码块:
synchronized
关键字可以用与方法中的某个区块中,表示只对这个区域的资源实行互斥访问格式:
synchronized(同步锁){
需要同步操作的代码
}
同步锁
对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁
- 锁对象 可以是任意类型
- 多个线程对象 要使用同一把锁
注意: 在任何时候吗,最多允许一个线程拥有同步锁,谁拿到锁谁就进入代码块中,其它的线程只能在外面等着(BLOCKED)
代码改进:
package com.study_07.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();
}
//票存在 ticket---
System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");
ticket--;
}
}
}
}
}
5.同步技术的原理
6.同步方法
同步方法:使用
synchronized
修饰的方法,就叫做同步方法,保证A线程 执行该方法的时候,其它线程只能在方法外等着格式:
public synchronized void method(){
可能会产生线程安全问题的代码
}
同步锁是谁? 对于非static方法,同步锁就是this 对于static方法,我们使用当前方法所在类的字节码对象(类名.class)
package com.study_08.Synchronized;
public class RunnableImpl implements Runnable {
// 定义一个多个线程共享的票源
private int ticket = 100;
// 卖票
@Override
public void run() {
// 使用死循环 让买票重复执行
System.out.println("this:"+this);
while (true) {
payTicket();
}
}
/*
定义一个同步方法
同步方法也会把方法内部的代码锁住
只让一个线程执行
同步方法的对象是谁?
就是实现类对象
*/
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--;
}
}
}
7.静态同步方法
package com.study_08.Synchronized;
public class RunnableImpl implements Runnable {
// 定义一个多个线程共享的票源
private static int ticket = 100;
// 卖票
@Override
public void run() {
// 使用死循环 让买票重复执行
System.out.println("this:"+this);
while (true) {
payTicket();
}
}
/*
静态同步方法
锁对象是谁?
不能是this
this是创建对象之后产生的,静态方法优先于对象
静态的锁对象是本类的class属性-->class文件对象(反射)
*/
public static /*synchronized*/ void payTicket() {
// 先判断票是否存在
synchronized (RunnableImpl.class) {
if (ticket > 0) {
// 提高安全问题出现的概率,让程序睡眠
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票存在 ticket---
System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");
ticket--;
}
}
}
}
8.Lock锁
package com.study_09.Lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
使用Lock锁
Lock实现提供了比使用synchronized方法和语句课获得的跟广泛的锁定操作
Lock接口中的方法:
void lock() 获取锁
void unlock() 释放锁
*/
public class RunnableImpl implements Runnable {
// 定义一个多个线程共享的票源
private int ticket = 100;
// 1. 在成员位置创建一个ReentrantLock对象
Lock l = new ReentrantLock();
// 卖票
@Override
public void run() {
// 使用死循环 让买票重复执行
while (true) {
l.lock();
try {
// 先判断票是否存在
if (ticket > 0) {
// 提高安全问题出现的概率,让程序睡眠
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票存在 ticket---
System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");
ticket--;
}
} finally {
l.unlock(); // 无论程序是否异常,都会将锁对象释放
}
}
}
}