多线程的安全问题(代码同步问题)

通过分析,发现,打印出0,-1,-2的错票。
多线程出现安全问题!!!!

  • tick只有一份,多个线程共用这个tick;
  • 当多条语句在操作一个线程共享数据时,一个线程对多条语句只执行了一部分,还没执行完另一个线程已经开始执行共享代码块!!
  • 解决办法:将共享代码块加上一个锁,也叫同步代码块;——— cynchronized
  • 格式:synchronized(对象){ 代码块 };对象:只要是对象就可以,可以用上帝(Object obj)
  • 锁原理:就是在同步代码块前设置一个标志位,代码块被线程执行时,标志位置0,代码块未被执行时,标志位置1.
  • 持有锁的线程可以在代码块中执行,没有锁的线程及时获取到了CPU资源也不能在同步中中执行!
  • 锁的弊端:未执行代码块的线程要不断重复的判断标志位,这会消耗资源,拖慢进程速度。

同步的前提:

    • 两个或两个以上的线程。
    • 必须时多个线程同时使用一个锁。
    • 必须保证同步代码只有一个线程在运行。

什么代码需要同步?

    • 方法:明确哪些代码块是多线程运行。
    • 明确共享数据。
    • 明确多线程运行代码中哪些语句是操作共享数据的。
  • 函数也可以封装代码,格式:public synchronized void add( **** ){ } ; 作为修饰符,放在前面。
    注意:在任何时候,你都可以用Thread.currentThread().getName()来获取当前线程的名字

发现存在代码安全问题:

/*
需求:
银行有一个金库。
有两个储户分别存300元,每次存100,存3次
*/
class Bank{
    private int sum;
    public void add(int n){
        sum = sum + n;
        try{Thread.sleep(10);}catch(Exception e){}
        System.out.println("sum = "+sum);
    }
}
class Cus implements Runnable{
    private Bank b = new Bank();
    public void run(){
        for(int x=0; x<3; x++){
            b.add(100);
        }
    }
}
class BankDemo{
    public static void main(String[] args){
        Cus c = new Cus();
        Thread t1 = new Thread(c);
        Thread t2 = new Thread(c);
        t1.start();
        t2.start();
    }
}
  • 结果:
    多线程的安全问题(代码同步问题)_第1张图片
    image.png
  • 解决问题:

/*
需求:
银行有一个金库。
有两个储户分别存300元,每次存100,存3次
*/
class Bank{
    private int sum;
    Object obj = new Object();
    public void add(int n){
        synchronized(obj){
            sum = sum + n;
            try{Thread.sleep(10);}catch(Exception e){}
            System.out.println("sum = "+sum);
        }
    }
}
class Cus implements Runnable{
    private Bank b = new Bank();
    public void run(){
        for(int x=0; x<3; x++){
            b.add(100);
        }
    }
}
class BankDemo{
    public static void main(String[] args){
        Cus c = new Cus();
        Thread t1 = new Thread(c);
        Thread t2 = new Thread(c);
        t1.start();
        t2.start();
    }
}
  • 结果:

    • 多线程的安全问题(代码同步问题)_第2张图片
      image.png

同步函数:

    • 同步函数用的是哪一个锁?
    • 函数需要被对象调用,那么函数就都有一个所属对引用。就是this。
    • 所以同步函数使用的锁事this。
    /*
需求:
银行有一个金库。
有两个储户分别存300元,每次存100,存3次
*/
class Bank{
    private int sum;
    //Object obj = new Object();
    public synchronized void add(int n){   //同步函数!!
        //synchronized(obj){
            sum = sum + n;
            try{Thread.sleep(10);}catch(Exception e){}
            System.out.println("sum = "+sum);
        //}
    }
}
class Cus implements Runnable{
    private Bank b = new Bank();
    public void run(){
        for(int x=0; x<3; x++){
            b.add(100);
        }
    }
}
class BankDemo{
    public static void main(String[] args){
        Cus c = new Cus();
        Thread t1 = new Thread(c);
        Thread t2 = new Thread(c);
        t1.start();
        t2.start();
    }
}

一个小练习:

* /*
* 需求:现有两个线程:
* 一个线程在同步代码块中运行;
* 一个线程在同步函数中运行;
* 两个线程都在执行买票程序。
* */
* class Ticket implements Runnable{
*     private int tick = 1000;
*     Object obj = new Object();
*     boolean flag = true ;
*     public void run(){
*         if(flag){
*             while(true){
*                 synchronized(obj){  //synchronized( this ),解决0号票问题。
*                     if(tick >0){
*                         try{Thread.sleep(10);}catch(Exception e){}
*                         System.out.println(Thread.currentThread().getName()+"code.....sale "+ tick--);
*                     }
*                 }
*             }
*         }
*         else{
*             while(true){
*                 this.show();
*             }
*         }
*     }
*     public synchronized void show(){
*         if(tick >0){
*             try{Thread.sleep(10);}catch(Exception e){}
*             System.out.println(Thread.currentThread().getName()+"show.....sale "+ tick--);
*         }
*     }
* }
* class TickDemo{
*     public static void main(String[] args){
*         Ticket t = new Ticket();
*         Thread t1 = new Thread(t);
*         Thread t2 = new Thread(t);
*         t1.start();
*         try{Thread.sleep(10);}catch(Exception e){}  //解决办法
*         t.flag = false;  //问题的渊源!原因:主线程执行太快,这三条语句瞬间就执行完成;flag的作用完全没有体现出来,flag=true时同步代码块还没来的及执行,flag就等于false了,紧接着执行show( )函数;之后就一直为false了。
*         t2.start();
*     }
* }
  • 代码看似没问题,其实问题可大!所有线程都是由show( )同步函数执行完成的。如下:
  • 多线程的安全问题(代码同步问题)_第3张图片
    image.png
  • 解决办法在代码中!!!
  • 之后有出现0号票!!!可怕!!
    • 多线程的安全问题(代码同步问题)_第4张图片
      image.png
    • 解决:把同步代码块中obj对象换成this就OK了!
  • 如果同步函数被静态修饰时,使用的锁是什么呢?
    • 通过验证,发现不再是this,因为静态方法中也不可以定义this。
      • 原因:静态进内存时没有对象,只有类,也就是该类对应的字节码文件对象
      • 类名.class 该对象的类型是class。
    • 静态的同步方法,使用的锁事该方法所在类的字节码文件对象。类名.class

你可能感兴趣的:(多线程的安全问题(代码同步问题))