Java学习 线程安全与线程同步

来源:互联网 发布:uc监控软件下载 编辑:程序博客网 时间:2024/04/29 06:36

转载自:

http://blog.csdn.net/ie800/article/details/17139715


线程安全问题

线程安全问题产生的原因:多条线程代码操作共享数据,一条线程还未执行完,并对结果进行反馈;其他的线程通过条件判断参与进运算,对数据重复操作

多次启动一个线程是非法的,尤其是线程已经结束后,不能重新启动

买票示例

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. /*卖票示例 
  2.  
  3. */  
  4. class Ticket implements Runnable  
  5. {  
  6.     private int num = 100;  
  7.   
  8.     public void sale()  
  9.     {  
  10.         while (true)//  
  11.         {  
  12.             if (num>0)  
  13.             {  
  14.                 try//使用sleep方法时会出现异常,继承的接口没有继承Exception 异常,所以run方法不能throws,只能try catch  
  15.                 {  
  16.                     Thread.sleep(10);//线程休眠10毫秒  
  17.                 }  
  18.                 catch (InterruptedException e)  
  19.                 {  
  20.                 }  
  21.                 System.out.println("name: "+Thread.currentThread().getName()+"....."+num--);  
  22.             }  
  23.         }  
  24.     }  
  25.     public void run()  
  26.     {  
  27.         sale();  
  28.     }  
  29. }  
  30.   
  31. class  TicketDemo  
  32. {  
  33.     public static void main(String[] args)   
  34.     {  
  35.         Ticket t = new Ticket();  
  36.           
  37.         Thread t1= new Thread(t);  
  38.         Thread t2= new Thread(t);     
  39.         Thread t3= new Thread(t);     
  40.         Thread t4= new Thread(t);     
  41.         t1.start();  
  42.         t2.start();  
  43.         t3.start();  
  44.         t4.start();  
  45.     }  
  46. }  

运行结果出现负数票



解决方法:

当前线程执行完毕后,其它线程才能参与运算。

将共享数据的代码封装起来,当有线程在执行这些代码的时候。其它线程不可以参与运算,使用


线程同步

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. class Ticket implements Runnable  
  2. {  
  3.     private int num = 100;  
  4.     Object obj =new Object();  
  5.   
  6.     public synchronized void sale()//同步函数,不能直接调用,因为一个线程进入后全部执行完才出来,其它线程没有锁所以执行不到  
  7.     {  
  8.         while (true)  
  9.         {  
  10.         //  synchronized(obj)//同步代码块,同步锁;需要传入对象,可以是任意对象,作为自定义锁  
  11.         //  {  
  12.                 if (num>0)  
  13.                 {  
  14.                     try  
  15.                     {  
  16.                         Thread.sleep(10);//线程休眠10毫秒  
  17.                     }  
  18.                     catch (InterruptedException e)  
  19.                     {  
  20.                     }  
  21.                     System.out.println("name: "+Thread.currentThread().getName()+"....."+num--);  
  22.                 }  
  23.         //  }     
  24.         }  
  25.     }  
  26.     public void run()//所有线程都可以调用run方法,在run方法中间接调用同步函数,从而间接执行到sale();  
  27.     {  
  28.         sale();  
  29.     }  
  30. }  


对象如同同步就相当于一个保险箱,持有锁的线程可以进入同步中执行,没有锁的线程即使获取cpu执行权,也无法进去执行

经典实例----火车上的卫生间

同步的前提多个线程使用同一个锁,必须保证同步中只有一个线程在执行

同步的弊端多个线程需要判断锁,较为消耗资源

同步的原则明确哪些代码是多线程运行代码;明确共享数据;明确多线程运行代码哪些是操作共享数据的,只能有一个共同的


同步的方式

同步代码块

synchronized(对象){需要被同步的代码}//只需要把需要同步的代码封装在同步中,同步代码块的锁是传入的对象,可以是任意对象,自定义锁。

同步函数

同步函数的锁:函数需要被对象调用,那么函数都有一个所属对象的引用,就是this。即当前任务对象

(考点)静态同步函数使用的锁是该方法所在类的字节码文件对象,类名.class

静态成员进内存的过程:内存中没有本类对象,但是一定有该类对应的字节码文件对象,类名.class,该对象的类型是class

总结:同步函数中,那个对象调用了同步,同步锁就是该对象的引用


单例模式的线程安全问题

懒汉式特点:对象延迟加载,多线程访问时存在安全隐患;可以加同步来解决。同步过程中判断锁会降低效率,而同步函数每一条线程访问都会有判断锁的过程;使用同步代码块二次判断,用判断变量来替代判断锁,只需判断一次锁,稍微提高了执行效率

由于是静态同步,所以懒汉式中的同步锁是本类对象的字节码文件


[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. //饿汉式  
  2. class Single  
  3. {  
  4.     private static final Single s=new Single();//有final修饰  
  5.     private Single(){}  
  6.     public static Single getInstance()  
  7.     {  
  8.         return s;  
  9.     }  
  10. }  
  11.   
  12. //懒汉式  
  13. class Single  
  14. {  
  15.     private static Single s =null;//无final修饰  
  16.     private Single(){}  
  17.   
  18. //  public static Single getInstance()//延迟加载,多个线程并发访问时可能引发安全问题  
  19. //  public static synchronized Single getInstance()使用同步函数,多了一步判断锁的过程,多线程时较为低效  
  20.     public static Single getInstance()  //使用同步代码块  
  21.     {  
  22.         if (s==null)//减少判断锁的次数,稍微提高效率  
  23.         {  
  24.             synchronized(Single.class)//静态同步的锁  
  25.             {  
  26.                 if(s==null)  
  27.                     s=new Single();   
  28.             }  
  29.                 return s;  
  30.         }  
  31.     }  
  32. }  

线程死锁

死锁的条件:同步中嵌套同步,同步之间的调用,由于每个同步只有一个对应的锁,相互嵌套时同时持有两个锁才会执行,一旦两个同步都持有一个对方的锁无法执行也无法释放,就会导致线程死锁。这种情况应避免发生

死锁示例:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. class Test implements Runnable  
  2. {  
  3.     private boolean flag;  
  4.     Test(boolean flag)  
  5.     {  
  6.         this.flag = flag;  
  7.     }  
  8.   
  9.     public void run()  
  10.     {  
  11.         if (flag)  
  12.         {  
  13.             synchronized(MyLock.a)  
  14.             {  
  15.                 System.out.println("if.....a");  
  16.                 synchronized(MyLock.b)  
  17.                 {  
  18.                     System.out.println("if....b");  
  19.                 }  
  20.             }  
  21.         }  
  22.         else  
  23.         {  
  24.             synchronized(MyLock.b)  
  25.             {  
  26.                 System.out.println("else....b");  
  27.                 synchronized(MyLock.a)  
  28.                 {  
  29.                     System.out.println("else......a");  
  30.                 }  
  31.             }  
  32.         }  
  33.     }  
  34. }  
  35.   
  36. class MyLock  
  37. {  
  38.     static Object a= new Object();  
  39.     static Object b= new Object();  
  40. }  
  41.   
  42. class DeadLock   
  43. {  
  44.     public static void main(String[] args)   
  45.     {  
  46.         Thread t1=new Thread(new Test(true));  
  47.         Thread t2=new Thread(new Test(false));  
  48.         t1.start();  
  49.         t2.start();  
  50.     }  
0 0
原创粉丝点击