线程安全
线程安全:线程锁:当某线程取得该锁后,其他线程需要等待
线程的安全与否,主要取决于多线程同时访问同一个对象的其中某一个方法时,可能会触发同时运行其中的某一条代码,导致最后得出的运算结果与实际不符合
先来看看线程不安全的一个类
Account(不安全)
class Account{
int deposite=1000;
String name;
static Map<String,Integer>ids=new HashMap<>();
static int ID=1;
int id=(++ID);
public Account(String name) {
this.name=name;
ids.put(name,this.id);
}
void withDraw(int money){
//当多个线程同时访问同一个对象的withDarw方法,为了创造不安全的可能性,在方法中添加了休眠语句
if(deposite>=money){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
deposite-=money;
}else{
}
}
}
public class Demo04 {
public static void main(String[] args) {
Unsafe();
}
private static void Unsafe() {
Account a1=new Account("22");
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
a1.withDraw(900);
System.out.println(a1.deposite);
}
});
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
a1.withDraw(990);
System.out.println(a1.deposite);
}
});
t1.start();
t2.start();
}
}
输出结果:
10
-890
可以看到通过两个线程同时访问同一个对象的同一个方法,即使我们在进入增删存款前以及判断了当前存款是否够取,但是还是出现了负数,就是因为,这两条线程近乎同时进入了同一个方法,导致在判断过程中都能通过,最终导致结果为负数
所以,为了防止这种事情发生,我们需要为方法加上线程锁:synchronize
锁对象,同一时间,只能有一条线程访问这个对象的这个方法
void withDraw2(int money){
synchronized (this){
if(deposite>=money){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
deposite-=money;
}else{
}
}
}
锁方法:同一时间,只能有一个线程使用这个方法,等这条线程结束了这方法的运行,就会解锁
static synchronized void setID1(int id,Account account){
boolean flag=true;
Set<Map.Entry<String,Integer>>entrySet=ids.entrySet();
for (Map.Entry<String, Integer> entry:entrySet){
if(entry.getValue()==id){
flag=false;
}
}
if(flag){
account.id=id;
ids.put(account.name,id);
}
}
ReentrantLock 可重入锁,这个锁可以锁单一条代码
ReentrantLock lock=new ReentrantLock();//可重入锁
void m1(){
lock.lock();//锁
System.out.println("1");
lock.unlock();//解锁
System.out.println("2");
}
线程的死锁
有锁就会有死锁:多条线程持有其他线程所需要的锁,都不放手,造成死锁
package oldPackage.src.SE06;
public class Demo05 {
// 死锁:多条线程持有其他线程所需要的锁,都不放手,造成死锁
public static void main(String[] args) {
test1();
}
private static void test1() {
Accountx accountx1=new Accountx();
Accountx accountx2=new Accountx();
new Thread(new Runnable() {
@Override
public void run() {
accountx1.transferDL(accountx2,800);
//accountx1调用自己的转账方法,向accountx2转账
//此时,该线程就会持有转账锁,需要accountx2的转账锁
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
accountx2.transferDL(accountx1,800);
//accountx2调用自己的转账方法,向accountx1转账
//此时,该线程就会持有自己的转账锁,需要accountx2的转账锁
}
}).start();
//上述注释的两个线程就发生了死锁
}
}
class Accountx{
static int AUTO=0;
int id=(++AUTO);
int deposite=1000;
void transferDL(Accountx accountx,int money){
synchronized (this){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (accountx){
if(money<=deposite){
this.deposite-=money;
accountx.deposite+=money;
}
}
}
}
}
解决死锁问题
为了解决拿一把锁等一把锁的尴尬状况,我们可以对这个对象的同类的共有属性进行比较而改变访问同一个对象的方法的先后顺序
void transfer(Accountx account,int money){//同样还是传入对方账号和转账金额
Accountx max;//定义一个大账号
Accountx min;//定义一个小账号
if(account.id>this.id){//比较两个账号的ID值
max=account;//id大的就赋给max
min=this;//id小的就赋值给min
}else {
max=this;min=account;
}
synchronized (min){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (max){
if(money<=deposite){
System.out.println("进来了");
this.deposite-=money;
account.deposite+=money;
}
}
}
}