一:什么是线程同步?
1.线程同步是解决线程安全问题的方案。
2.线程同步是让多个线程实现先后依次访问共享资源,这样就解决了安全问题。
3.线程同步的常见方案
加锁:每次只允许一个线程加锁,加锁后才能进入访问,访问完毕后自动解锁,然后其他线程才能再加锁进来
二:加锁的方式
1.方式一:同步代码块
作用:把访问共享资源的核心代码给上锁,以保证线程安全。
原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行。
同步锁注意事项:
对于当前同时执行的线程来说,同步锁必须是同一把(同一个对象),否则会出bug。
锁对象的使用规范:
①建议使用共享资源作为锁对象,对于实例方法建议使用this作为锁对象。
②对于静态方法建议使用字节码(类名.class)对象作为锁对象。
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+"取钱失败,余额不足~~");
}
}
}
2.方式二:同步方法
作用:把访问共享资源的核心方法给上锁,以保证线程安全。
原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行。
同步方法的底层原理:
①同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码。
②如果方法是实例方法:同步方法默认用this作为的锁对象。
③如果方法是静态方法:同步方法默认用类名.class作为锁对象。
public synchronized 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+"取钱失败,余额不足~~");
}
}
--------------------------------------------------------------------------------------
同步方法与同步代码块相比较:
范围上:同步代码块锁的范围更小,同步方法锁的范围更大。
可读性:同步方法更好。
3.方式三:Lock锁
Lock锁是JDK5开始提供的一个新的操作锁定,通过它可以创建出锁对象进行加锁和解锁,更灵活、更方便、更强大。
Lock锁是接口不能直接实例化,可以采用它的实现类ReentrantLock来构建Lock锁对象
构造器:
ReentrantLock() 获取Lock锁的实现类对象
方法:
lock() 获得锁
unlock() 释放锁
public Lock lk = new ReentrantLock();
public void DrawMoney(double money) {
String name = Thread.currentThread().getName();
try {
lk.lock();
if (this.money >= money){
System.out.println(name+"来取钱成功"+"取钱"+money+"元!");
this.money -= money;
System.out.println(name+"取钱后余额为"+this.money);
}else {
System.out.println(name+"取钱失败,余额不足~~");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lk.unlock();
}
}
三:银行取钱案例(Lock锁方式)
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Account {
private String cardId;
private double money;
public Lock lk = new ReentrantLock();
public Account() {
}
public void DrawMoney(double money) {
String name = Thread.currentThread().getName();
try {
lk.lock();
if (this.money >= money){
System.out.println(name+"来取钱成功"+"取钱"+money+"元!");
this.money -= money;
System.out.println(name+"取钱后余额为"+this.money);
}else {
System.out.println(name+"取钱失败,余额不足~~");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lk.unlock();
}
}
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 class DrawThread extends Thread{
public Account acc;
public DrawThread(Account acc, String name){
super(name);
this.acc = acc;
}
@Override
public void run() {
acc.DrawMoney(100000);
}
}
public class ThreadTest5 {
public static void main(String[] args) {
Account acc1 = new Account("IBI-1",100000);
new DrawThread(acc1,"小红").start();
new DrawThread(acc1,"小明").start();
}
}