Java学习day47-线程的同步与死锁

一、线程的同步

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.解决方法:

  ①专门的算法、原则,比如加锁顺序一致。

  ②尽量减少同步资源的定义,尽量避免锁未释放的场景。

猜你喜欢

转载自www.cnblogs.com/su-peng/p/12632660.html
今日推荐