什么是线性安全问题
多个线程同时操作同一个共享资源的时候可以会出现业务安全问题,称为线程安全问题
出现原因:1.存在多线程并发 2.同时访问共享资源 3.存在修改共享资源
共同账户取钱问题(存在线程安全问题)
public class ATMTest {
public static void main(String[] args)throws Exception {
//共同账户
Account account = new Account();
FutureTask f1 = new FutureTask(new Get(account));
Thread thread1 = new Thread(f1);
FutureTask f2 = new FutureTask(new Get(account));
Thread thread2 = new Thread(f2);
thread1.start();
thread2.start();
System.out.println((Integer) f1.get());
System.out.println((Integer) f2.get());
System.out.println(account.getAnInt());
}
}
@Getter
@Setter
class Account{
private Integer anInt = 10000;
public Integer getMoney(int i){
if (anInt>i){
anInt = anInt - i;
return i;
}else {
System.out.println("余额不足");
return null;
}
}
}
class Get implements Callable<Integer>{
private Account account;
public Get(Account account) {
this.account = account;
}
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"输入您要取的钱数:");
int i = new Scanner(System.in).nextInt();
return account.getMoney(i);
}
}
线程同步
为了解决线程安全问题
让多个线程依次访问共享资源,解决安全问题
线程同步的核心思想:
加锁,把共享资源上锁,每次只能一个进程进入访问完毕以后解锁,然后其他线程才能进来
方式一:同步代码块
public class ATMTest {
public static void main(String[] args)throws Exception {
//共同账户
Account account = new Account();
FutureTask f1 = new FutureTask(new Get(account));
Thread thread1 = new Thread(f1);
FutureTask f2 = new FutureTask(new Get(account));
Thread thread2 = new Thread(f2);
thread1.start();
thread2.start();
System.out.println((Integer) f1.get());
System.out.println((Integer) f2.get());
System.out.println(account.getAnInt());
}
}
@Getter
@Setter
class Account{
private Integer anInt = 10000;
public Integer getMoney(int i){
synchronized ("ccc") {
if (anInt>i){
anInt = anInt - i;
return i;
}else {
System.out.println("余额不足");
return null;
}
}
}
}
class Get implements Callable<Integer>{
private Account account;
public Get(Account account) {
this.account = account;
}
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"输入您要取的钱数:");
int i = new Scanner(System.in).nextInt();
return account.getMoney(i);
}
}
锁对象不能用任意的无关对象,不然会影响其他无关进程的执行
锁对象的规范要求:
规范上:建议使用共享资源作为锁对象
对于实例方法建议使用this作为锁对象
对于静态方法建议使用字节码(类名.class)对象作为锁对象(因为静态方法在内存中是唯一的,所以直接使用类名上锁)
方式二:同步方法
方式三:Lock锁
public class ATMTest {
public static void main(String[] args)throws Exception {
//共同账户
Account account = new Account();
FutureTask f1 = new FutureTask(new Get(account));
Thread thread1 = new Thread(f1);
FutureTask f2 = new FutureTask(new Get(account));
Thread thread2 = new Thread(f2);
thread1.start();
thread2.start();
System.out.println((Integer) f1.get());
System.out.println((Integer) f2.get());
System.out.println(account.getAnInt());
}
}
@Getter
@Setter
class Account{
private Integer anInt = 10000;
private final Lock lock =new ReentrantLock(); //锁对象,唯一,不可修改
public Integer getMoney(int i){
lock.lock();
try {
if (anInt>i){
anInt = anInt - i;
return i;
}else {
System.out.println("余额不足");
return null;
}
} finally {
lock.unlock();
}
}
}
class Get implements Callable<Integer>{
private Account account;
public Get(Account account) {
this.account = account;
}
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"输入您要取的钱数:");
int i = new Scanner(System.in).nextInt();
return account.getMoney(i);
}
}