引入:
在多线程程序中往往会出现这么一个情况:多个线程同时访问某个线程间的共享变量。
案例:
假设银行存款业务写了两个方法,
一个是存钱 store() 方法 ,一个是查询余额 get() 方法。
假设初始客户小明的账户余额为 0 元。
public void store(int money){int newAccount=account+money;account=newAccount;}public void get(){System.out.print("小明的银行账户余额:");System.out.print(account);}
如果小明为自己存款 1 元,我们期望的线程调用情况如下:
- 首先会启动一个线程调用
store()方法,为客户账户余额增加 1;- 再启动一个线程调用
get()方法,输出客户的新余额为 1。
但实际情况可能由于线程执行的先后顺序,出现如图所示的错误:
这就是一个典型的由共享数据引发的并发数据冲突问题。
解决方式也很简单,让并发执行会产生问题的代码段不并发行了。
如果 store() 方法 执行完,才能执行 get() 方法,而不是像上图一样并发执行,自然不会出现这个问题。那如何才能做到呢?
答案就是使用 synchronized 关键字。
我们可以设置某个锁,锁会有两种状态,分别是上锁和解锁。在 store() 方法执行之前,先观察这个锁的状态,如果是上锁状态,就进入阻塞,代码不运行;
对于 get() 方法也是如此。
synchronized 使用方法
synchronized 一共有三种使用方法:
1、直接修饰某个实例方法
**
在这种情况下多线程并发访问实例方法时,如果其他线程调用同一个对象的被 synchronized 修饰的方法,就会被阻塞。相当于把锁记录在这个方法对应的对象上。
public synchronized void store(int money){
int newAccount=account+money;
account=newAccount;
}
public synchronized void get(){
System.out.print("小明的银行账户余额:");
System.out.print(account);
}
2、直接修饰某个静态方法
在这种情况下进行多线程并发访问时,如果其他线程也是调用属于同一个类的被 synchronized 修饰的静态方法,就会被阻塞。相当于把锁信息记录在这个方法对应的类上。
public synchronized static void get(){
···
}
3、修饰代码块
如果此时有别的线程也想访问某个被synchronized(对象0)修饰的同步代码块时,也会被阻塞。
public static void get(){
synchronized(对象0){
···
}
}
