个人简介
- 大家好,我是翰慧腾。一名正在努力学JAVA的大一小白,本文章为初学的笔记,希望各位多多指教。💙
- 欢迎点赞+收藏+留言💜
- 自己发光,而不是被谁照亮🧡
一、线程安全问题
概述:多个线程同时操作同一个共享资源(修改其资源数据)的时候可能会出现业务安全问题,称为线程安全问题。
案例:小明和小红是一对夫妻,他们有一个共同账户,余额是10万元,模拟2人同时去取钱10万。
/**
* @author hanhan
* date 2022/4/17 15:54
* 努力已经来不及了,你得拼命
*/
public class Thread_safety {
public static void main(String[] args) {
Account a1 = new Account("111222333", 1000000);
//创建两个线程对象,代表小明和小红同时进来
new DrawThread_1(a1,"小明").start();
new DrawThread_1(a1,"小红").start();
}
}
class Account{
private String cardId;
private double money;
public Account() {
}
public Account(String cardId, double money) {
this.cardId = cardId;
this.money = money;
}
public String getCardId() {
return cardId;
}
public void setCardId(String cardId) {
this.cardId = cardId;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public void drawMoney(double money) {
//获取取钱人
String name= Thread.currentThread().getName();
//判断余额是否够
if(this.money>=money){
System.out.println(name+"取钱成功,吐出"+money);
//更新余额
this.money-=money;
System.out.println(name+"取钱后剩余"+this.money);
}else{
System.out.println(name+"来取钱,余额不足了");
}
}
}
class DrawThread_1 extends Thread{
private Account a1;
public DrawThread_1(Account a1,String name){
super(name);
this.a1=a1;
}
@Override
public void run() {
//取钱
a1.drawMoney(1000000);
}
}
存在问题:账户1000000元,却两个人取了2000000元。
二、线程同步(为了解决线程安全问题)
就针对以上的取钱问题,解决办法:让多个线程实现先后依次访问共享资源
线程同步的核心思想:加锁,把共享资源进行上锁,每次只能一个线程进入访问完毕以后解锁,然后其他线程才可以进来。
方式一
同步代码块:把出现线程安全问题的核心代码给加上锁。
原理:每次只能一个线程进入,执行完毕后自动解锁,其它线程才可以进来执行
锁对象要求:锁对象只要对于当前同时执行的线程来说是同一个对象即可。
锁对象的规范要求:
1.建议使用共享资源作为锁对象
2.对于实例方法建议使用this作为锁对象
3.对于静态方法建议使用字节码(类名.class)对象作为锁对象
/**
* @author hanhan
* date 2022/4/17 15:54
* 努力已经来不及了,你得拼命
*/
public class Thread_safety {
public static void main(String[] args) {
Account a1 = new Account("111222333", 1000000);
//创建两个线程对象,代表小明和小红同时进来
new DrawThread_1(a1,"小明").start();
new DrawThread_1(a1,"小红").start();
}
}
class Account{
private String cardId;
private double money;
public Account() {
}
public Account(String cardId, double money) {
this.cardId = cardId;
this.money = money;
}
public String getCardId() {
return cardId;
}
public void setCardId(String cardId) {
this.cardId = cardId;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public void drawMoney(double money) {
//获取取钱人
String name= Thread.currentThread().getName();
//判断余额是否够
synchronized (this) {
if(this.money>=money){
System.out.println(name+"取钱成功,吐出"+money);
//更新余额
this.money-=money;
System.out.println(name+"取钱后剩余"+this.money);
}else{
System.out.println(name+"来取钱,余额不足了");
}
}
}
}
class DrawThread_1 extends Thread{
private Account a1;
public DrawThread_1(Account a1,String name){
super(name);
this.a1=a1;
}
@Override
public void run() {
//取钱
a1.drawMoney(1000000);
}
}
方式二
同步方法:把出现线程安全问题的核心方法给加上锁
原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以来执行。
同步方法底层原理:同步方法底层也是有隐式锁对象的,只是锁的范围是整个方法代码;如果方法是实例方法:同步方法默认用this作为锁的对象,但是代码要高度面向对象。如果是方法是静态方法:同步方法默认用类名.class作为锁的对象。
方式一和方式二的比较:同步代码块锁的范围更小,同步方法锁的范围更大
方式三 Lock锁
概述:Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作;Lock是接口,不能直接实例化,使用它的实现类ReentrantLock来构建Lock锁对象。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author hanhan
* date 2022/4/17 15:54
* 努力已经来不及了,你得拼命
*/
public class Thread_safety {
public static void main(String[] args) {
Account a1 = new Account("111222333", 10000000);
//创建两个线程对象,代表小明和小红同时进来
new DrawThread_1(a1,"小明").start();
new DrawThread_1(a1,"小红").start();
}
}
class Account{
private String cardId;
private double money;
//定义一个锁
private final Lock l=new ReentrantLock();
public Account() {
}
public Account(String cardId, double money) {
this.cardId = cardId;
this.money = money;
}
public String getCardId() {
return cardId;
}
public void setCardId(String cardId) {
this.cardId = cardId;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public void drawMoney(double money) {
//获取取钱人
String name= Thread.currentThread().getName();
//判断余额是否够
try {
l.lock();//上锁
if(this.money>=money){
System.out.println(name+"取钱成功吐出"+money);
//更新余额
this.money-=money;
System.out.println(name+"取钱后剩余"+this.money);
}else{
System.out.println(name+"来取钱,余额不足了");
}
} finally {
l.unlock();//解锁
}
}
}
class DrawThread_1 extends Thread{
private Account a1;
public DrawThread_1(Account a1,String name){
super(name);
this.a1=a1;
}
@Override
public void run() {
//取钱
a1.drawMoney(100000);
}
}