9. 多线程 Part 3 同步与死锁 --- 学习笔记

来源:互联网 发布:淘宝的伞黑胶都在外面 编辑:程序博客网 时间:2024/06/02 05:31


9.6 同步与死锁

         一个多线程的程序如果是通过Runnabl接口实现的,则意味着类中的属性将被多个线程共享,那么这样一来就会造成一种问题,如果这多个线程操作同一资源时就有可能出现资源的同步问题。例如, 卖票程序,如果多个线程同时操作时就有可能出现卖出票为负数的问题。

      9.6.1 问题的产生

范例:通过Runnable接口实现多线程,并产生3个线程对象,同时卖5张票。

class MyThread implements Runnable{    private int ticket = 5;    public void run(){        for (int i = 0; i < 7; i++){            if (ticket > 0){            try{            Thread.sleep(3000);        }catch(InterruptedException e){            e.printStackTrace();        }        System.out.println("卖票: ticket = " + ticket--);            }        }    }}public class SyncDemo01{    public static void main(String args[]){        MyThread my = new MyThread();        new Thread(my, "Thread-A").start();        new Thread(my, "Thread-B").start();        new Thread(my, "Thread-C").start();    }}

运行结果截图

卖票: ticket = 2卖票: ticket = 1卖票: ticket = 0<span style="color:#ff0000;">卖票: ticket = -1</span>-------------------------------------------------

     从程序的运行结果中可以发现,程序中加入了延迟操作,所以在运行的最后出现了负数的情况,那么为什么现在会产生这样的问题呢?

       上面程序对于票数的操作步骤如下:

  1. 判断票数是否大于0,大于0则表示还有票可以卖
  2. 如果票数大于0,则将票卖出。

      但是,代码中,在步骤1和步骤2之间加入了延迟操作,那么一个线程就有可能在还没有对票数进行减法操作之前,其他线程就已经将票数减少了,这样一来就会出现票数为负的情况。

             

     如果想解决这样的问题,就必须使用同步。

        所谓同步就是指多个操作在同一个时间段内只能有一个线程进行,其他线程要等待此线程完成之后才可以继续执行,如下图所示:

   


      9.6.2 使用同步解决问题

         解决资源共享的同步操作,可以使用同步代码块和同步方法两种方式完成。

  •  同步代码块

所谓的代码块就是指使用“{}”括起来的一段代码,根据其位置和声明的不同,可以分为普通代码块、构造块、静态块3种,如果在代码块前面加上synchronized关键字,则此代码块就称为同步代码块。  其格式如下

synchronized(同步对象){      需要同步的代码;}

*******从上面的格式可以看出,在使用同步代码块时必须制定一个同步对象,但一般都将当前对象(this)设置成同步对象!!!*******

范例:使用同步代码块解决的同步问题

class MyThread implements Runnable{    private int ticket = 5;    public void run(){        for (int i = 0; i < 7; i++){        synchronized(this){            if (ticket > 0){                try{                    Thread.sleep(500);                }catch(InterruptedException e){                    e.printStackTrace();                }                    System.out.println("卖票: ticket = " + ticket--);            }        }        }    }}public class SyncDemo01{    public static void main(String args[]){        MyThread my = new MyThread();        new Thread(my, "Thread-A").start();        new Thread(my, "Thread-B").start();        new Thread(my, "Thread-C").start();    }}

运行结果:

-------------------------------------------------卖票: ticket = 5卖票: ticket = 4卖票: ticket = 3卖票: ticket = 2卖票: ticket = 1-------------------------------------------------

          将上面代码的取值和修改值的操作代码进行了同步,所以不会再出现卖出票为负数的情况了。

  •  同步方法

               除了可以将需要的代码设置成同步代码块外,也可以使用synchronized关键字将一个方法声明成同步方法。其格式如下:

synchronized 方法返回值  方法名称(参数列表){    方法体;}

范例: 使用同步方法解决卖票出负数的情况

class MyThread implements Runnable{    private int ticket = 5;    public void run(){        for (int i = 0; i < 7; i++){        this.sale();        }    }    private synchronized void sale(){        if (ticket > 0){            try{                Thread.sleep(500);            }catch(InterruptedException e){                e.printStackTrace();            }            System.out.println("卖票:ticket = " + ticket--);        }    }}public class SyncDemo02{    public static void main(String args[]){        MyThread my = new MyThread();        new Thread(my, "Thread-A").start();        new Thread(my, "Thread-B").start();        new Thread(my, "Thread-C").start();    }}

运行结果如下,与上面同步代码块实现了完全相同的功能。

-------------------------------------------------卖票:ticket = 5卖票:ticket = 4卖票:ticket = 3卖票:ticket = 2卖票:ticket = 1-------------------------------------------------

  • 方法定义的完整格式

访问权限  {public|default|protected|private} [final] [static] [synchronized] 返回值类型|void  方法名称(参数类型 参数名称,…) [throws Exception1, Exception2] {      [return [返回值|返回调用处]]}

      9.6.3 死锁

           同步可以保证资源共享操作的正确性,但是过多同步也会产生问题。例如, 现在张三想要李四的画,李四想要张三的书;张三对李四说“把你的画给我,我就给你书”,李四也对张三说了:“把你的书给我,我就给你画”,此时,张三在等着李四的答复,而李四也在等着张三的答复;那么这样下去最终结果就是,张三得不到李四的画,李四也得不到张三的书。这就是死锁的概念!!

          所谓死锁就是指两个线程都在等待彼此先完成,造成了程序的停滞,一般程序的死锁都是在程序运行时出现的。

范例:死锁

class Zhangsan{    public void say(){        System.out.println(" 张三对李四说:“你给我画,我就把书给你。”");    }    public void get(){        System.out.println("张三得到画了!!");    }}class Lisi{    public void say(){        System.out.println("李四对张三说:“你给我书,我就把画给你。”");    }    public void get(){        System.out.println("李四得到书了!!");    }}
public class ThreadDeadLock implements Runnable{    private static Zhangsan zs = new Zhangsan();    private static Lisi ls = new Lisi();    private boolean flag = false;   //标记,用于判断哪个对象先执行        public void run(){        if(flag){            synchronized (zs){                zs.say();                try{                    Thread.sleep(500);                }catch(InterruptedException e){                    e.printStackTrace();                }                synchronized(ls){                    zs.get();                }            }        }else{            synchronized(ls){                ls.say();                try{                    Thread.sleep(500);                }catch(InterruptedException e){                    e.printStackTrace();                }                synchronized(zs){                    ls.get();                }            }        }    }    public static void main(String args[]){        ThreadDeadLock t1 = new ThreadDeadLock();        ThreadDeadLock t2 = new ThreadDeadLock();        t1.flag = true;        t2.flag = false;        new Thread(t1, "Thread-A").start();        new Thread(t2, "Thread-B").start();    }}


程序运行结果:

------------------------------------------------- 张三对李四说:“你给我画,我就把书给你。”李四对张三说:“你给我书,我就把画给你。”

[以下代码不再执行,程序进入死锁状态]
    从程序的运行结果中可以发现,两个线程都在彼此等着对方的执行完成,这样,程序就无法向下继续执行,从而造成了死锁的现象。

  • 关于同步与死锁, 多个线程共享同一资源时需要进行同步,以保证资源操作的完整性;但是过多的同步就有可能产生死锁。。。


关于线程的死锁、互锁、互斥锁, 以及它们产生的原因、排错方法等等要自己查资料看看!!!!!!!!!!!

0 0
原创粉丝点击