一、线程安全模拟
1、线程安全主要存在于多个线程修改共享资源时出现问题,如下面的取钱案例
/**
* 模拟取钱线程安全问题
* 1、共同的账户类
* 2、创建两个线程同时操作账户类
* 3、账户类中实现取钱方法
*/
public class Test {
public static void main(String[] args) {
Acccount acc = new Acccount(100000);
new DrawThread(acc,"小明").start();
new DrawThread(acc,"小红").start();
}
}
// 账户类
class Acccount {
private double money;
public Acccount(double money) {
this.money = money;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public void drawmoeny(double money) {
if(this.money >= 100000){
System.out.println(Thread.currentThread().getName() + "来取钱");
this.money -= money;
System.out.println("余额为"+this.money);
}else {
System.out.println("余额不足");
}
}
}
// 线程类
class DrawThread extends Thread {
private Acccount acc;
public DrawThread(Acccount acc,String name){
super(name);
this.acc = acc;
}
@Override
public void run() {
acc.drawmoeny(100000);
}
}
2、解决该类问题,在修改资源的节点加上锁,让线程依次修改资源后再释放给其他线程,其他非修改资源的操作是也属于多线程操作,但不影响安全。
二、 线程锁
2.1、方式一,同步代码块
// 账户类
class Acccount {
private double money;
public Acccount(double money) {
this.money = money;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
/**针对核心代码加锁且指定对线程来说是唯一的对象,该对象可以理解一把锁,先拿到这把锁的线程即可执行锁下面的代码块
实例方法通过使用对象本身,能保证同个对象使用自己的锁,从而不影响其他线程,例如如果该锁为常量(即字符串),
那表示任意线程进来都会受这锁的影响,比如创建两个account对象,但实际上不同对象不应该相互影响,同个对象才会影响线程安全,
因此规范写法如下实例方法,
对于静态代码块,则需要使用类似常量的锁对象,因为该代码块是针对所有的线程,规范书写如下
*/
public static void run(){
synchronized(Acccount.class){
System.out.println("静态代码块");
}
}
public void drawmoeny(double money) {
synchronized (this) {
if (this.money >= 100000) {
System.out.println(Thread.currentThread().getName() + "来取钱");
this.money -= money;
System.out.println("余额为" + this.money);
} else {
System.out.println("余额不足");
}
}
}
}
2.2、方式二,同步方法
// 账户类
class Acccount {
private double money;
public Acccount(double money) {
this.money = money;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public synchronized static void run(){
System.out.println("静态方法");
}
// 同步方法锁,synchronized装饰方法即可,静态代码同理
public synchronized void drawmoeny(double money) {
if (this.money >= 100000) {
System.out.println(Thread.currentThread().getName() + "来取钱");
this.money -= money;
System.out.println("余额为" + this.money);
} else {
System.out.println("余额不足");
}
}
}
3、方式三,同步锁
class Acccount {
private double money;
private final Lock lock = new ReentrantLock();// 定义一个同步锁,在相应的需要代码加锁并且解锁即可
public Acccount(double money) {
this.money = money;
}
public void drawmoeny(double money) {
lock.lock();
try{
if (this.money >= 100000) {
System.out.println(Thread.currentThread().getName() + "来取钱");
this.money -= money;
System.out.println("余额为" + this.money);
} else {
System.out.println("余额不足");
}
}finally {
lock.unlock();
}
}
}