博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
day11_多线程(多线程安全问题)
阅读量:5322 次
发布时间:2019-06-14

本文共 10957 字,大约阅读时间需要 36 分钟。

 

同步代码块:

/*通过分析,发现,打印出0,-1,-2等错票多线程的运行出现了安全问题.问题原因: 当多条语句在操作多个线程的共享数据时,其中一个线程对多条语句只 执行了一部分,还没有执行完,另一个线程参与进来执行.导致共享数据 的错误.解决办法: 对多条操作共享数据的语句,在同一时间间隔内只让其中一个线程都执行完,在执行过程中,其它 线程不可以参与执行.Java对于多线程的安全问题提供了专业的解决方式就是同步代码块:
synchronized(对象)//该锁或叫监视器{    同步代码块}对象如同锁,持有锁的线程可以在同步中执行没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有锁同步前提:1.必须要有两个或者两个以上的线程2.多个线程必须使用同一把锁(使用的不同的锁不叫同步)        必须保证同步中只能有一个线程在运行好处:解决了多线程的安全问题弊端:多个线程每个线程都需要判断锁,较为消耗资源.(开门上的锁)*/class Ticket implements Runnable{    private int ticket=3000;    Object obj=new Object();    public void run()//不能抛出interruptedExcetption,因为Runnable中的run方法没有抛出任何异常    {      while(true)         {           synchronized(obj)            {               if(ticket>0)                {                   try{Thread.sleep(10);}catch(Exception e){ }//模拟出错,让线程休眠(暂停)10秒,让cpu执行其它线程,未加同步代码块前                                  System.out.println(Thread.currentThread().getName()+" "+ticket--);                //运行结果可能是全是一个线程在执行                //这是因为其它线程还没有抢到cpu,当抢到时,ticket<=0                //可以通过把ticket赋值大一点,让其它线程有执行到的机会                }            }        }    }}class TicketDemo2{    public static void main(String[] args)    {      Ticket t=new Ticket();       new Thread(t).start();       new Thread(t).start();       new Thread(t).start();       new Thread(t).start();    }}/*分析:(出现0,-1,-2)while(true)       if(ticket>0)        {         //可能出现的一种情况(安全隐患),当ticket=1时         //Thread-0 执行到该位置(0线程变成就绪状态)->cpu切换到Thread-1执行到该位置->Thread-2同理->Thread-3同理         //->cpu又切换到Thread-0(10ms时间已到),继续向下执行->打印出1         //->切换到Thread-1->0         //->切换到Thread-2->-1         //->切换到Thread-3->-2         try{Thread.sleep(10);}catch(Exception e){ }//模拟出错,让线程休眠(暂停)10ms,让cpu执行其它线程         System.out.println(Thread.currentThread().getName()+" "+ticket--);        }*//*     //在加上同步代码块后
//以下为可能出现的一种情况:假设ticket已经为1         Thread-0 Thead-1 Thread-2 Thread-3                              //③此时cpu切换到2线程,由于对象标志位为0,2线程无法执行            如果cpu切换到1,3线程,依然无法执行同步代码块          synchronized(obj)            {                              //①当cpu切换到0线程执行,判断对象标志位,为1,可以进来执行              //②0线程进来以后,把对象标志位置为0             //④cpu切换到0线程,ticket>0               if(ticket>0)                {                //⑤执行到sleep方法,0线程暂停10ms,cpu去执行其他线程->其它线程依然无法执行同步代码块                     try{Thread.sleep(10);}catch(Exception e){ }                //⑥10ms时间到时,当cpu再次切换到0线程时,打印1,ticket->0                  System.out.println(Thread.currentThread().getName()+" "+ticket--);                                }            }            //⑦0线程执行完同步代码块对象标志位置1    *//*经典同步问题:  火车上 上厕所*/

银行存钱小例子:

/*需求:银行有一个金库.有两个储户分别存300元,每次存100,存3次.思路:两个储户->两个线程一个金库->对同一个金库进行操作目的:该程序是否有安全问题?找问题:1.首先 明确哪些代码是多线程运行代码2.其次 明确共享数据3.再次 明确多线程运行代码中哪些语句是操作共享数据的*/class Bank{  private int sum=0;  Object obj=new Object();  public void add(int n)//这个位置可以抛出异常,注意Bank  {                     //没有继承任何父类(当然Object除外)        synchronized(obj)//这里犯得一个错误,采用匿名对象(new Object())->每个线程用的不是一把锁      {       sum += n;       try{Thread.sleep(10);}catch(Exception e){}//依然模拟出错       System.out.println(Thread.currentThread().getName()+" sum="+sum);      }  }}class Depositor implements Runnable{  //储户里面有存钱动作,被多个线程执行  private Bank b=new Bank();  public void run()//此位置不可抛出异常,因为Runnable中的run方法没有抛出任何异常    {
//
也可以把同步代码块加在这里//但是出现一个问题:张三必须把所有钱存完,李四才能去存     for(int i=0;i<3;++i)      {        //我也可以把同步代码块加在这里,问题是如果我在add中调用        //一些其它方法,或定义一些其它变量,是没有必要同步的         b.add(100);           }    }}class BankDemo{    public static void main(String[] args)    {        Depositor d=new Depositor();       new Thread(d).start();       new Thread(d).start();          }}

以上运行结果有点巧合,也可能出现线程交替(我的是双核cpu)

稍微改进一下:

/*需求:银行有一个金库.有两个储户分别存300元,每次存100,存3次.*/class Bank{    private int sumMoney=0;    public void add(int storeMoney){      synchronized(Bank.class){        sumMoney+=storeMoney;        System.out.println(Thread.currentThread().getName()+"..."+sumMoney);       }    }}class StoreUser{   private int storeMoney;   public StoreUser(int storeMoney){
//用户存入多少钱 this.storeMoney=storeMoney; } public void storeMoney(Bank bank){
//用户拿着钱,那么由用户发出存钱动作,存入哪个银行? bank.add(storeMoney); } public int getMoney(){ return storeMoney; } public void setMoney(int storeMoney){ this.storeMoney=storeMoney; }}class MainClass{ public static void main(String[] args){ final Bank bank=new Bank(); final StoreUser[] su={
new StoreUser(100),new StoreUser(100)}; for( int i=0;i

同步函数:

/*同步函数用的是哪一个锁呢?  函数需要被对象调用.那么函数都有一个所属对象引用.就是this所以同步函数使用的锁是this通过该程序验证.  使用两个线程来买票  一个线程在同步代码块中.  一个线程在同步函数中.  都在执行卖票动作.*/class Ticket implements Runnable{    private int tick=100;    Object obj=new Object();    boolean flag=true;    /*    public void run()    {                       while(tick>0)//卖完1000张"不卖了"         synchronized(obj)         {            if (tick>0)            {             try{Thread.sleep(10);}catch(Exception e){}             System.out.println(Thread.currentThread().getName()+" "+tick--);            }                     }    }        */  //方法二:利用同步函数加锁    public void run()    {       if(flag)        {                  while(tick>0)            {                 synchronized(this)                 {                    if (tick>0)                    {                     try{Thread.sleep(10);}catch(Exception e){}                     System.out.println(Thread.currentThread().getName()+" --code()-- "+tick--);                                         }                                      }            }                }        else         while(tick>0)            show();             }    private synchronized void show()    {             if (tick>0)             {              try{Thread.sleep(10);}catch(Exception e){}              System.out.println(Thread.currentThread().getName()+" --show()-- "+tick--);             }                }}class ThisLockDemo{  public static void main(String[] args)  {   Ticket t=new Ticket();   new Thread(t).start();   try{Thread.sleep(10);}catch(Exception e){}   t.flag=false;   new Thread(t).start();   //以上运行结果极有可能都为show()(当不使主线程暂停)   //这是因为主线程代码开启0线程后->0线程处于就绪状态,cpu不一定切换到   //该线程执行run()->cpu接着执行主线程,此时flag已经为false   //->cpu切换到0线程执行else内容   //->cpu切换到主线程开启1线程->依然执行else内容   //这时加上sleep让主线程短暂暂停,让其他线程有执行机会  }}/*以上打印结果中有0号票原因:两个线程用的不是一把锁(obj和this)     没有实现同步这时,把线程①的锁换成this此时消除0号票验证:同步函数的锁为this,并且this指向new Ticket()*/

同步函数被静态修饰:

/*如果同步函数被静态修饰后,使用的锁是什么?通过验证,不是this.因为静态方法中不可以定义this静态进内存时,没有本类对象,但是一定有该类对应的 字节码文件对象(类名.class)该对象的类型(所述的类)是Class(Class 类的实例表示正在运行的 Java 应用程序中的类和接口)静态的同步方法,使用的锁是该方法所在类的字节码文件对象.类名.class*/class Ticket implements Runnable{    private static int tick=100;    boolean flag=true;    Class c=Ticket.class;    public void run()    {       if(flag)        {                  while(tick>0)            {                 synchronized(Ticket.class)//或者用c                 {                    if (tick>0)                    {                     try{Thread.sleep(10);}catch(Exception e){}                     System.out.println(Thread.currentThread().getName()+"--code()-- "+tick--);                                         }                                      }            }                }        else         while(tick>0)            show();             }    private static synchronized void show()    {           if (tick>0)            {             try{Thread.sleep(10);}catch(Exception e){}             System.out.println(Thread.currentThread().getName()+" --show()-- "+tick--);            }                }}class StaticLockDemo{  public static void main(String[] args)  {   Ticket t=new Ticket();   new Thread(t).start();   try{Thread.sleep(10);}catch(Exception e){}   t.flag=false;   new Thread(t).start();    }}

又见单例设计模式:

/*单例设计模式*///饿汉式/*class Single{    private Single(){}    private static final Single s=new Single();//加上final更严谨,不能改变s的引用     public static Single getInstance()    {      return s;    }}*///懒汉式class Single{    private Single(){}    private static Single s=null;    /*    public static synchronized Single getInstance()    {     if(s==null)      //A执行到该位置,CPU切换B,B执行到该位置     //cpu又切换到A执行,创建对到象     //切换到B,又创建对象,因此使用同步函数      s=new Single();     return s;        }    */    //使用同步函数,每个线程都需要判断对象标志位,效率很低    public static Single getInstance()    {     //⑥当C线程执行,s已不为null,返回     if(s==null)        {
//②CPU切换到B,s依然为null synchronized(Single.class)//该类所属的字节码文件 { //⑤CPU切换到B,s不为null,返回 if(s==null) //①A线程执行到该位置 s=new Single(); //③A创建完对象 } //④对象标志位置1 } return s; } //利用同步代码块,多加一个判断,减少线程判断锁的次数 //相对提高了效率 }

死锁:

/*死锁:通俗点说:两个人一个人一根筷子,两人相互索要,谁也不给谁->都别想吃 同步中嵌套同步*/class Ticket implements Runnable{    private static int tick=1000;    boolean flag=true;    Object obj=new Object();    public void run()    {       if(flag)        {                     while(tick>0)            {                synchronized(obj)               {                 //(可能出现)0线程执行到此持有obj锁,要想执行show需要this锁                 show();                               }            }                }        else         while(tick>0)            show();             }    private synchronized void show()    {     //1线程执行到此,持有this锁,需要obj锁继续执行             synchronized(obj)              {                    if (tick>0)                    {                     try{Thread.sleep(10);}catch(Exception e){}                     System.out.println(Thread.currentThread().getName()+" --code()-- "+tick--);                                         }                                    }    }}class DeadLockDemo{  public static void main(String[] args)  {   Ticket t=new Ticket();   new Thread(t).start();   try{Thread.sleep(10);}catch(Exception e){}   t.flag=false;   new Thread(t).start();    }}/*可能不出现死锁:当CPU把一个线程执行完,再去执行另一个线程(把ticket赋值大点使其发生死锁)*/

死锁小程序:

class Test implements Runnable{    private boolean flag;    Test(boolean flag)    {     this.flag=flag;        }    public void run()    {         if(flag)         {             synchronized(MyLock.locka)             {               System.out.println("if locka");               synchronized(MyLock.lockb)               {               System.out.println("if lockb");                              }             }          }        else         {            synchronized(MyLock.lockb)             {                System.out.println("else lockb");               synchronized(MyLock.locka)               {               System.out.println("else locka");                              }             }                                          }    }}
//把锁放在单独类里面class MyLock{    static MyLock locka=new MyLock();    static MyLock lockb=new MyLock();}class DeadLockDemo2{  public static void main(String[] args)  {     new Thread(new Test(true)).start();     new Thread(new Test(false)).start();  }}

 

鉴于以上我在想,能否略有改动解决死锁,可以让0线程暂停一会,让cpu执行1线程.在if(flag)下加上try{Thread.sleep(10);}catch(Exception e){},但是这只是减小死锁发生概率.

转载于:https://www.cnblogs.com/yiqiu2324/archive/2013/03/16/2963499.html

你可能感兴趣的文章
面向对象:反射,双下方法
查看>>
鼠标悬停提示文本消息最简单的做法
查看>>
课后作业-阅读任务-阅读提问-2
查看>>
面向对象设计中private,public,protected的访问控制原则及静态代码块的初始化顺序...
查看>>
fat32转ntfs ,Win7系统提示对于目标文件系统文件过大解决教程
查看>>
Awesome Adb——一份超全超详细的 ADB 用法大全
查看>>
shell cat 合并文件,合并数据库sql文件
查看>>
Android 将drawable下的图片转换成bitmap、Drawable
查看>>
介绍Win7 win8 上Java环境的配置
查看>>
移动、联通和电信,哪家的宽带好,看完你就知道该怎么选了!
查看>>
Linux设置环境变量的方法
查看>>
Atitit.进程管理常用api
查看>>
构建自己的项目管理方案
查看>>
利用pca分析fmri的生理噪声
查看>>
div水平居中且垂直居中
查看>>
epoll使用具体解释(精髓)
查看>>
AndroidArchitecture
查看>>
安装Endnote X6,但Word插件显示的总是Endnote Web"解决办法
查看>>
python全栈 计算机硬件管理 —— 硬件
查看>>
大数据学习
查看>>