本章节介绍Java多线程安全与锁
当多个线程同时操作同一个数据时,可能会出现数据不一样的情况,这就是线程安全问题。线程安全机制用于保证多个线程访问数据时的一致性.
(多线程并发操作同一个数据可能会引发线程安全问题)
锁就是把多个线程对数据的并发操作转换为串行操作。
**代码 使用synchronized **
package com.xxxx.reids.thread;
import lombok.AllArgsConstructor;
import lombok.Data;
/***
* @title Account
* @desctption 账户
* @author Kelvin
* @create 2023/5/29 11:43
**/
@Data
@AllArgsConstructor
public class Account {
/**
* 余额
*/
private Integer balance;
}
package com.xxxx.reids.thread;
import lombok.AllArgsConstructor;
import lombok.Data;
/***
* @title Person
* @desctption 人员信息
* @author Kelvin
* @create 2023/5/29 11:44
**/
@Data
@AllArgsConstructor
public class Person {
/**
* 姓名
*/
private String name;
/**
* 取款金额
*/
private Integer drawAccount;
}
package com.hqyj.reids.thread;
import java.util.concurrent.locks.Lock;
/***
* @title Draw
* @desctption 取款
* @author Kelvin
* @create 2023/5/29 11:49
**/
public class Draw implements Runnable{
private Account account;
private Person person;
public Draw(Account account,Person person){
this.account = account;
this.person = person;
}
@Override
public void run() {
synchronized (this.account){
while (true){
//余额
Integer balance = this.account.getBalance();
if(balance < this.person.getDrawAccount()){
//余额不足
System.out.println(this.person.getName() + ",账户余额不足");
break;
}else{
/* try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
//取款,减余额
this.account.setBalance(balance - this.person.getDrawAccount());
System.out.println(this.person.getName() + "取了"+this.person.getDrawAccount()+"钱,账户余额:" + this.account.getBalance());
}
}
}
}
}
测试
package com.xxxx.redis;
import com.xxxx.reids.thread.Account;
import com.xxxx.reids.thread.Draw;
import com.xxxx.reids.thread.Person;
import org.junit.Test;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/***
* @title ThreadLockTest
* @desctption 测试类
* @author Kelvin
* @create 2023/5/29 11:50
**/
public class ThreadLockTest {
@Test
public void lock() {
Lock lock = new ReentrantLock();
Account account = new Account(100);
Person zhangSan = new Person("张三", 20);
Person liSi = new Person("李四", 15);
new Thread(new Draw(account,zhangSan)).start();
new Thread(new Draw(account,liSi)).start();
}
}
**代码2 使用Lock **
package com.hqyj.reids.thread;
import java.util.concurrent.locks.Lock;
/***
* @title Draw
* @desctption 取款
* @author Kelvin
* @create 2023/5/29 11:49
**/
public class Draw implements Runnable{
private Account account;
private Person person;
private Lock lock ;
public Draw(Account account,Person person,Lock lock){
this.account = account;
this.person = person;
this.lock = lock;
}
@Override
public void run() {
lock.lock();
//取款操作
Integer drawAccmount = this.person.getDrawAccount();
Integer balanceInit = this.account.getBalance();
//System.out.print("原始值:" + balanceInit + " | ");
if(balanceInit < drawAccmount){
//余额不足,不给取款
System.out.println("取款余额不足");
//break;
}else{
Integer balance = balanceInit - drawAccmount;
this.account.setBalance(balance);
System.out.println(this.person.getName() + "成功取款:" + drawAccmount + ",账户余额:" + balance);
}
lock.unlock();
}
}
import com.xxxx.reids.thread.Account;
import com.xxxx.reids.thread.Draw;
import com.xxxx.reids.thread.Person;
import org.junit.Test;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/***
* @title ThreadLockTest
* @desctption 测试类
* @author Kelvin
* @create 2023/5/29 11:50
**/
public class ThreadLockTest {
@Test
public void lock() {
Lock lock = new ReentrantLock();
Account account = new Account(110);
Person zhangSan = new Person("张三", 17);
Person liSi = new Person("李四", 12);
new Thread(new Draw(account,zhangSan,lock)).start();
new Thread(new Draw(account,liSi,lock)).start();
new Thread(new Draw(account,zhangSan,lock)).start();
new Thread(new Draw(account,liSi,lock)).start();
new Thread(new Draw(account,zhangSan,lock)).start();
new Thread(new Draw(account,liSi,lock)).start();
new Thread(new Draw(account,zhangSan,lock)).start();
new Thread(new Draw(account,liSi,lock)).start();
}
}
死锁的例子
Java 死锁产生的四个必要条件:
/***
* @title DieLock
* @desctption 死锁案例
* @author Kelvin
* @create 2023/5/29 15:36
**/
import java.util.concurrent.TimeUnit;
public class DieLock {
private static Object object1 = new Object();
private static Object object2 = new Object();
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
synchronized (object1){
System.out.println("Thread1 get object1");
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object2){
System.out.println("thread1 get object2");
}
}
}
}.start();
new Thread(){
@Override
public void run() {
synchronized (object2){
System.out.println("Thread2 get object2");
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object1){
System.out.println("thread2 get object1");
}
}
}
}.start();
}
}
volatile是java虚拟机提供的轻量级的同步机制
什么时候去使⽤Volatile?
public class SingletonObject {
private static volatile SingletonObject singletonObject = new SingletonObject();
private SingletonObject(){
}
public static synchronized SingletonObject getInstance(){
if(singletonObject == null){
singletonObject = new SingletonObject();
}
return singletonObject;
}
}