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

通过分析,发现,打印出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(); } }



  • 结果: 多线程的安全问题(代码同步问题)
    文章图片
    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(); } }

  • 【多线程的安全问题(代码同步问题)】结果:
    • 多线程的安全问题(代码同步问题)
      文章图片
      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( )同步函数执行完成的。如下:
  • 多线程的安全问题(代码同步问题)
    文章图片
    image.png
  • 解决办法在代码中!!!
  • 之后有出现0号票!!!可怕!!
    • 多线程的安全问题(代码同步问题)
      文章图片
      image.png
    • 解决:把同步代码块中obj对象换成this就OK了!
  • 如果同步函数被静态修饰时,使用的锁是什么呢?
    • 通过验证,发现不再是this,因为静态方法中也不可以定义this。
      • 原因:静态进内存时没有对象,只有类,也就是该类对应的字节码文件对象。
      • 类名.class 该对象的类型是class。
    • 静态的同步方法,使用的锁事该方法所在类的字节码文件对象。类名.class。

    推荐阅读