文章目录
- 1. 多线程产生的问题
- 2. Synchronized 的使用方法
- 3. 线程的死锁问题
1. 多线程产生的问题
问题:
同一个账户,支付宝转账,微信转账。两个手机,一个手机开支付宝,另一个手机开微信。假设账户上有3000元,支付宝和微信同时提款2000,这时你的账户余额会变成了-1000元吗?
答:
当然不会,出现就有问题了
多个线程执行的不确定性引起执行结果的不稳定
多个线程对账本的共享,会造成操作的不完整性,会破坏数据。
代码实现:
package day15;
public class Test2 {
public static void main(String[] args) {
//定义账户对象
Account a = new Account();
//多线程对象
User u_weixin = new User(a, 2000);
User u_zhifubao = new User(a, 2000);
Thread weixin = new Thread(u_weixin, "微信");
Thread zhifubao = new Thread(u_zhifubao, "支付宝");
weixin.start();
zhifubao.start();
}
}
class Account{
public static int money = 3000;//全局变量,所有操作共享这个变量
/**
* 提款先判断账户钱够不够
* 多线程调用这个方法就有问题,线程共享资源时,一个线程在执行这个方法没有完毕时,另一个线程又开始执行这个方法
* 解决思路:先让一个线程整体执行完这个方法,另一个线程再执行。
* @param m
*/
public void drawing(int m) {
String name = Thread.currentThread().getName();
if(money < m) {
System.out.println(name + "操作,账户金额不足:" + money);
}else {
System.out.println(name + "操作,账户原有金额:" + money);
System.out.println(name + "操作,取款金额:" + m);
money = money - m;
System.out.println(name + "操作,取款后的余额:" + money);
}
}
}
class User implements Runnable{
Account account;
int money;
public User(Account account, int money) {
this.account = account;
this.money = money;
}
@Override
public void run() {
account.drawing(money);
}
}
运行结果:
发现金额变成 -1000 了
多线程出现了安全问题
问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。
解决办法 :
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。
2. Synchronized 的使用方法
Java 对于多线程的安全问题提供了专业的解决方式:
同步机制
1 、 synchronized 还可以放在方法声明中,表示整个方法为同步方法。
如果针对对象要加同步锁,那就加在方法上,例如:
public synchronized void show (String name){
....
}
2 、 锁 代码块
如果针对某一段代码需要加同步锁,那就直接在代码块上加同步锁,例如:
synchronized ( 对象) {
// 需要被同步的代码;
}
解决方法:
直接在方法上加上synchronized关键字
package day15;
public class Test2 {
public static void main(String[] args) {
//定义账户对象
Account a = new Account();
//多线程对象
User u_weixin = new User(a, 2000);
User u_zhifubao = new User(a, 2000);
Thread weixin = new Thread(u_weixin, "微信");
Thread zhifubao = new Thread(u_zhifubao, "支付宝");
weixin.start();
zhifubao.start();
}
}
class Account{
public static int money = 3000;//全局变量,所有操作共享这个变量
/**
* 提款先判断账户钱够不够
* 多线程调用这个方法就有问题,线程共享资源时,一个线程在执行这个方法没有完毕时,另一个线程又开始执行这个方法
* 解决思路:先让一个线程整体执行完这个方法,另一个线程再执行。
* 通过 synchronized同步锁 来完成
* 可以直接在方法上加上synchronized关键字
* 在普通方法上加同步锁synchronized,锁的是整个对象,不是某一个方法
* @param m
*/
public synchronized void drawing(int m) {
String name = Thread.currentThread().getName();
if(money < m) {
System.out.println(name + "操作,账户金额不足:" + money);
}else {
System.out.println(name + "操作,账户原有金额:" + money);
System.out.println(name + "操作,取款金额:" + m);
money = money - m;
System.out.println(name + "操作,取款后的余额:" + money);
}
}
}
class User implements Runnable{
Account account;
int money;
public User(Account account, int money) {
this.account = account;
this.money = money;
}
@Override
public void run() {
account.drawing(money);
}
}
运行结果:
在普通方法上加同步锁synchronized,锁的是整个对象,不是某一个方法
举例:
package day15;
public class Test2 {
public static void main(String[] args) {
//定义账户对象
Account a = new Account();
//多线程对象
User u_weixin = new User(a, 2000);
User u_zhifubao = new User(a, 2000);
Thread weixin = new Thread(u_weixin, "微信");
Thread zhifubao = new Thread(u_zhifubao, "支付宝");
weixin.start();
zhifubao.start();
}
}
class Account{
public static int money = 3000;//全局变量,所有操作共享这个变量
/**
* 提款先判断账户钱够不够
* 多线程调用这个方法就有问题,线程共享资源时,一个线程在执行这个方法没有完毕时,另一个线程又开始执行这个方法
* 解决思路:先让一个线程整体执行完这个方法,另一个线程再执行。
* 通过 synchronized同步锁 来完成
* 可以直接在方法上加上synchronized关键字
* 在普通方法上加同步锁synchronized,锁的是整个对象,不是某一个方法
*
* 普通方法加同步锁,锁的是当前方法对应的对象,当前的对象的所有加了同步锁的方法是共用一个同步锁
* @param m
*/
public synchronized void drawing(int m) {
String name = Thread.currentThread().getName();
if(money < m) {
System.out.println(name + "操作,账户金额不足:" + money);
}else {
System.out.println(name + "操作,账户原有金额:" + money);
System.out.println(name + "操作,取款金额:" + m);
money = money - m;
System.out.println(name + "操作,取款后的余额:" + money);
}
}
public synchronized void drawing1(int m) {
String name = Thread.currentThread().getName();
if(money < m) {
System.out.println(name + "操作,账户金额不足:" + money);
}else {
System.out.println(name + "操作,账户原有金额:" + money);
System.out.println(name + "操作,取款金额:" + m);
money = money - m;
System.out.println(name + "操作,取款后的余额:" + money);
}
}
}
class User implements Runnable{
Account account;
int money;
public User(Account account, int money) {
this.account = account;
this.money = money;
}
@Override
public void run() {
if(Thread.currentThread().getName().equals("微信")) {
account.drawing(money);
}else {
account.drawing1(money);
}
}
}
运行结果:
运行结果和上一段相同
说明:锁死的是Account a
这个对象普通方法加同步锁,锁的是当前方法对应的对象,当前的对象的所有加了同步锁的方法是共用一个同步锁
不同的对象是不同的锁
举例:
package day15;
public class Test2 {
public static void main(String[] args) {
//定义账户对象
Account a = new Account();
Account a1 = new Account();
//多线程对象
User u_weixin = new User(a, 2000);
User u_zhifubao = new User(a1, 2000);
Thread weixin = new Thread(u_weixin, "微信");
Thread zhifubao = new Thread(u_zhifubao, "支付宝");
weixin.start();
zhifubao.start();
}
}
class Account{
public static int money = 3000;//全局变量,所有操作共享这个变量
/**
* 提款先判断账户钱够不够
* 多线程调用这个方法就有问题,线程共享资源时,一个线程在执行这个方法没有完毕时,另一个线程又开始执行这个方法
* 解决思路:先让一个线程整体执行完这个方法,另一个线程再执行。
* 通过 synchronized同步锁 来完成
* 可以直接在方法上加上synchronized关键字
* 在普通方法上加同步锁synchronized,锁的是整个对象,不是某一个方法,
* 不同的对象是不同的锁,普通方法中加synchronized,线程使用不同的此方法的对象,还是有资源共享的问题
* @param m
*/
public synchronized void drawing(int m) {
String name = Thread.currentThread().getName();
if(money < m) {
System.out.println(name + "操作,账户金额不足:" + money);
}else {
System.out.println(name + "操作,账户原有金额:" + money);
System.out.println(name + "操作,取款金额:" + m);
money = money - m;
System.out.println(name + "操作,取款后的余额:" + money);
}
}
public synchronized void drawing1(int m) {
String name = Thread.currentThread().getName();
if(money < m) {
System.out.println(name + "操作,账户金额不足:" + money);
}else {
System.out.println(name + "操作,账户原有金额:" + money);
System.out.println(name + "操作,取款金额:" + m);
money = money - m;
System.out.println(name + "操作,取款后的余额:" + money);
}
}
}
class User implements Runnable{
Account account;
int money;
public User(Account account, int money) {
this.account = account;
this.money = money;
}
@Override
public void run() {
if(Thread.currentThread().getName().equals("微信")) {
account.drawing(money);
}else {
account.drawing1(money);
}
}
}
运行结果:
如何解决上述问题:
举例:
package day15;
public class Test2 {
public static void main(String[] args) {
//定义账户对象
Account a = new Account();
Account a1 = new Account();
//多线程对象
User u_weixin = new User(a, 2000);
User u_zhifubao = new User(a1, 2000);
Thread weixin = new Thread(u_weixin, "微信");
Thread zhifubao = new Thread(u_zhifubao, "支付宝");
weixin.start();
zhifubao.start();
}
}
class Account{
public static int money = 3000;//全局变量,所有操作共享这个变量
/**
* 静态方法 + synchronized,对于所有的对象都是使用同一个锁
* @param m
*/
public static synchronized void drawing2(int m) {
String name = Thread.currentThread().getName();
if(money < m) {
System.out.println(name + "操作,账户金额不足:" + money);
}else {
System.out.println(name + "操作,账户原有金额:" + money);
System.out.println(name + "操作,取款金额:" + m);
money = money - m;
System.out.println(name + "操作,取款后的余额:" + money);
}
}
}
class User implements Runnable{
Account account;
int money;
public User(Account account, int money) {
this.account = account;
this.money = money;
}
@Override
public void run() {
account.drawing2(money);//调用类的静态方法
}
}
运行结果:
锁代码块
举例:
package day15;
public class Test2 {
public static void main(String[] args) {
//定义账户对象
Account a = new Account();
Account a1 = new Account();
//多线程对象
User u_weixin = new User(a, 2000);
User u_zhifubao = new User(a, 2000);
Thread weixin = new Thread(u_weixin, "微信");
Thread zhifubao = new Thread(u_zhifubao, "支付宝");
weixin.start();
zhifubao.start();
}
}
class Account{
public static int money = 3000;//全局变量,所有操作共享这个变量
/**
* 对代码块加入同步锁
* 代码块synchronized(this),所有当前的对象的synchronized(this)同步的代码都是使用同一个锁
* @param m
*/
public void drawing3(int m) {
synchronized(this) {//表示当前对象的代码块被加了synchronized同步锁
//用this锁代码块是代表当前的对象,如果在其他的方法中也有synchronized(this)的代码块,使用的都是同一个同步锁
String name = Thread.currentThread().getName();
if(money < m) {
System.out.println(name + "操作,账户金额不足:" + money);
}else {
System.out.println(name + "操作,账户原有金额:" + money);
System.out.println(name + "操作,取款金额:" + m);
money = money - m;
System.out.println(name + "操作,取款后的余额:" + money);
}
}
}
}
class User implements Runnable{
Account account;
int money;
public User(Account account, int money) {
this.account = account;
this.money = money;
}
@Override
public void run() {
account.drawing3(money);
}
}
运行结果:
如果在其他的方法中也有synchronized(this)的代码块,使用的都是同一个同步锁
举例:
package day15;
public class Test2 {
public static void main(String[] args) {
//定义账户对象
Account a = new Account();
//多线程对象
User u_weixin = new User(a, 2000);
User u_zhifubao = new User(a, 2000);
Thread weixin = new Thread(u_weixin, "微信");
Thread zhifubao = new Thread(u_zhifubao, "支付宝");
weixin.start();
zhifubao.start();
}
}
class Account{
public static int money = 3000;//全局变量,所有操作共享这个变量
/**
* 对代码块加入同步锁
* @param m
*/
public void drawing3(int m) {
synchronized(this) {//表示当前对象的代码块被加了synchronized同步锁
//用this锁代码块是代表当前的对象,如果在其他的方法中也有synchronized(this)的代码块,使用的都是同一个同步锁
String name = Thread.currentThread().getName();
if(money < m) {
System.out.println(name + "操作,账户金额不足:" + money);
}else {
System.out.println(name + "操作,账户原有金额:" + money);
System.out.println(name + "操作,取款金额:" + m);
money = money - m;
System.out.println(name + "操作,取款后的余额:" + money);
}
}
}
public void drawing4(int m) {
synchronized(this) {//表示当前对象的代码块被加了synchronized同步锁
//用this锁代码块是代表当前的对象,如果在其他的方法中也有synchronized(this)的代码块,使用的都是同一个同步锁
String name = Thread.currentThread().getName();
if(money < m) {
System.out.println(name + "操作,账户金额不足:" + money);
}else {
System.out.println(name + "操作,账户原有金额:" + money);
System.out.println(name + "操作,取款金额:" + m);
money = money - m;
System.out.println(name + "操作,取款后的余额:" + money);
}
}
}
}
class User implements Runnable{
Account account;
int money;
public User(Account account, int money) {
this.account = account;
this.money = money;
}
@Override
public void run() {
if(Thread.currentThread().getName().equals("微信")) {
account.drawing3(money);
}else {
account.drawing4(money);
}
}
}
运行结果:
怎么使用不同的同步锁
举例:
(1)锁同一个对象,设置a、a1两个账户
package day15;
public class Test2 {
public static void main(String[] args) {
//定义账户对象
Account a = new Account();
Account a1 = new Account();
//多线程对象
User u_weixin = new User(a, 2000);
User u_zhifubao = new User(a1, 2000);
Thread weixin = new Thread(u_weixin, "微信");
Thread zhifubao = new Thread(u_zhifubao, "支付宝");
weixin.start();
zhifubao.start();
}
}
class Account{
public static int money = 3000;//全局变量,所有操作共享这个变量
/**
* synchronized修饰代码块,想要根据不同的对象有不同的锁
* @param m
*/
public void drawing5(int m, Account a) {
synchronized(a) {//表示通过方法的参数传递进来的对象的代码块被加了synchronized同步锁
//不同的对象就有不同的同步锁
String name = Thread.currentThread().getName();
if(money < m) {
System.out.println(name + "操作,账户金额不足:" + money);
}else {
System.out.println(name + "操作,账户原有金额:" + money);
System.out.println(name + "操作,取款金额:" + m);
money = money - m;
System.out.println(name + "操作,取款后的余额:" + money);
}
}
}
}
class User implements Runnable{
Account account;
int money;
public User(Account account, int money) {
this.account = account;
this.money = money;
}
@Override
public void run() {
account.drawing5(money, account);
}
}
运行结果:
(2)锁同一个对象,只设置a一个账户
package day15;
public class Test2 {
public static void main(String[] args) {
//定义账户对象
Account a = new Account();
//多线程对象
User u_weixin = new User(a, 2000);
User u_zhifubao = new User(a, 2000);
Thread weixin = new Thread(u_weixin, "微信");
Thread zhifubao = new Thread(u_zhifubao, "支付宝");
weixin.start();
zhifubao.start();
}
}
class Account{
public static int money = 3000;//全局变量,所有操作共享这个变量
/**
* synchronized修饰代码块,想要根据不同的对象有不同的锁
* synchronized(a)这个小括号中传入不同的对象就是不同的锁
* @param m
*/
public void drawing5(int m, Account a) {
synchronized(a) {//表示通过方法的参数传递进来的对象的代码块被加了synchronized同步锁
//不同的对象就有不同的同步锁
String name = Thread.currentThread().getName();
if(money < m) {
System.out.println(name + "操作,账户金额不足:" + money);
}else {
System.out.println(name + "操作,账户原有金额:" + money);
System.out.println(name + "操作,取款金额:" + m);
money = money - m;
System.out.println(name + "操作,取款后的余额:" + money);
}
}
}
}
class User implements Runnable{
Account account;
int money;
public User(Account account, int money) {
this.account = account;
this.money = money;
}
@Override
public void run() {
account.drawing5(money, account);
}
}
运行结果:
3. 线程的死锁问题
问题情景:
线程a0,需要执行方法f0
线程a1,需要执行方法f1
f0和f1都是有同步锁的方法
a0调用f1方法并且一直没有执行完f1
a1调用f0方法并且一直没有执行完f0
导致a0和a1线程都在等待对方释放方法,对方都不释放,这样就形成了线程的死锁
死锁:
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
解决方法:
①专门的算法、原则,比如加锁顺序一致
②尽量减少同步资源的定义,尽量避免锁未释放的场景