一、线程的同步
1.问题的提出:
①多个线程执行的不确定性引起执行结果的不稳定
②多个线程对账本的共享,会造成操作的不完整性,会破坏数据
2.问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。
3.解决办法:
对多条语句操作共享数据的语句,只能让一个线程都执行完,再执行过程中,其他线程不可以参与执行。
二、同步锁:Synchronized的使用方法
1.Java对于多线程的安全问题提供了专业的解决方式:同步机制
①synchronized还可以放在方法中声明,表示整个方法为同步方法。例如:
public synchronized void show(String name){...}
②synchronized(对象){ //需要被同步的代码; }
package day20; public class Test2 { public static void main(String[] args){ //定义账户对象 Acount a = new Acount(); //多线程对象 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 Acount{ public static int money = 3000;//全局变量,所有操作共享这个变量 /** * 提款,先判断账户钱够不够 * 多线程调用这个方法,就有问题,线程共享资源时,一个线程在执行这个方法没有完毕时,另一个线程又开始执行这个方法 * 解决思路:先让一个线程整体执行完这个方法,另一个线程再执行 * 通过synchronized同步锁来完成(1.直接在方法上加上synchronized关键字) * 在普通方法上加同步锁synchronized,锁的是整个对象,不是某一方法。 * */ 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); System.out.println(name +"操作,取款操作:原金额" + money + "-" +"取款金额" + m); money = money - m; System.out.println(name +"操作,账户取款后余额:" + money); } } } class User implements Runnable{ Acount acount; int money; public User(Acount acount,int money){ this.acount = acount; this.money = money; } public void run() { // TODO Auto-generated method stub acount.drawing(money); } }
打印结果:
总结补充:
1.普通方法加同步锁,锁的是当前方法对应的对象,当前的对象的所有加了同步锁的方法是共用一个同步锁
2.静态的方法加上synchronized,对于所有的对象都是使用同一个锁
3.对代码块加入同步锁:
①代码块synchronized(this),所有当前的对象的同步的代码都是使用同一个锁
②代码块synchronized(xxx),这个小括号中传入不同的对象,就有不同的锁
4.如果是针对对象要加同步锁,那就加在方法上,如果针对某一段代码需要加同步锁,那就直接在代码块上加同步锁。
package day20; public class Test2 { public static void main(String[] args){ //定义账户对象 Acount a = new Acount(); Acount a1 = new Acount(); //多线程对象 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 Acount{ public static int money = 3000;//全局变量,所有操作共享这个变量 /** * 提款,先判断账户钱够不够 * 多线程调用这个方法,就有问题,线程共享资源时,一个线程在执行这个方法没有完毕时,另一个线程又开始执行这个方法 * 解决思路:先让一个线程整体执行完这个方法,另一个线程再执行 * 通过synchronized同步锁来完成(1.直接在方法上加上synchronized关键字) * 在普通方法上加同步锁synchronized,锁的是整个对象,不是某一方法。 * 不同的对象就是不同的锁,普通方法加synchronized,线程使用不同的此方法的对象,还是有共享资源的问题 * */ 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); System.out.println(name +"操作,取款操作:原金额" + money + "-" +"取款金额" + 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); System.out.println(name +"操作,取款操作:原金额" + money + "-" +"取款金额" + m); money = money - m; System.out.println(name +"操作,账户取款后余额:" + money); } } /** * 静态的方法加上synchronized,对于所有的对象都是使用同一个锁 * */ 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); System.out.println(name +"操作,取款操作:原金额" + money + "-" +"取款金额" + m); money = money - m; System.out.println(name +"操作,账户取款后余额:" + money); } } /** * 对代码块加入同步锁 * 代码块synchronized(this),所有当前的对象的同步的代码都是使用同一个锁 * */ public synchronized void drawing3(int m){ synchronized(this){//表示当前的对象的代码块,被加了同步锁 //用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); System.out.println(name +"操作,取款操作:原金额" + money + "-" +"取款金额" + m); money = money - m; System.out.println(name +"操作,账户取款后余额:" + money); } } } public synchronized void drawing4(int m){ synchronized(this){//表示当前的对象的代码块,被加了同步锁 //用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); System.out.println(name +"操作,取款操作:原金额" + money + "-" +"取款金额" + m); money = money - m; System.out.println(name +"操作,账户取款后余额:" + money); } } } /** * synchronized修饰代码块,想要根据不同的对象有不同的锁,需要加当前对象的参数 * synchronized(a)这个小括号中传入不同的对象,就有不同的锁 * */ public synchronized void drawing5(int m,Acount 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); System.out.println(name +"操作,取款操作:原金额" + money + "-" +"取款金额" + m); money = money - m; System.out.println(name +"操作,账户取款后余额:" + money); } } } } class User implements Runnable{ Acount acount; int money; public User(Acount acount,int money){ this.acount = acount; this.money = money; } public void run() { // TODO Auto-generated method stub // acount.drawing(money); // if( Thread.currentThread().getName().equals("微信")){ //// acount.drawing(money); // acount.drawing3(money); // }else{ //// acount.drawing1(money); // acount.drawing4(money); // } // acount.drawing2(money);//调用类的静态方法 // acount.drawing3(money); acount.drawing5(money,acount); } }
三、线程的死锁问题
1.死锁:不同的线程分别占用对方需要的同步资源不放弃,都在等对方放弃自己需要的同步资源,就形成了线程的死锁。
2.解决方法:
①专门的算法、原则,比如加锁顺序一致。
②尽量减少同步资源的定义,尽量避免锁未释放的场景。