码迷,mamicode.com
首页 > 编程语言 > 详细

黑马程序员-学习日记(多线程安全问题和死锁认识)

时间:2015-07-13 23:52:28      阅读:166      评论:0      收藏:0      [点我收藏+]

标签:

------Java培训、Android培训、iOS培训、期待与您交流! -------

安全问题产生的原因:

当多条代码语句在操作同一个共享数据时,一个线程对多条语句只执行了一部分,还没有执行完, 另一个线程参与进来执行。导致共享数据的错误。

class Mlpc implements Runnable
{
    private int ticket = 50;

    public void run() 
    {
        while(true)
        {
            if(ticket>0)
            {
                try
                {
                    Thread.sleep(20); //让线程进入冻结状态
                }
                catch (Exception e)
                {
                }
                System.out.println(Thread.currentThread()+"  "+ticket--);
            }
        }
    }
}
class mlpcDemo 
{
    public static void main(String[] args) 
    {
        Mlpc m = new Mlpc();
        Thread th0 = new Thread(m);
        Thread th1 = new Thread(m);
        Thread th2 = new Thread(m);
        Thread th3 = new Thread(m);

        th0.start();
        th1.start();
        th2.start();
        th3.start();
    }
}

技术分享

出现了票数为-1和-2这样的结果。

 解决办法:

针对多条线程将会操作共享数据的那条语句,每次只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。

方式:

同步代码块:

class Mlpc implements Runnable
{
    private int ticket = 50;
    Object obj = new Object();
    public void run() 
    {
        while(true)
        {
            synchronized (obj)
            {
                if(ticket>0)
                {
                    try
                    {
                        Thread.sleep(20);
                    }
                    catch (Exception e)
                    {
                    }
                    System.out.println(Thread.currentThread()+"  "+ticket--);
                }
            }
        }
    }
}

对象如同锁,持有锁的线程可以在同步中执行。非持有锁的线程,即使获取cpu的执行权,也进不去,因为没有获取锁。

同步的前提:

1.必须要有两个或者两个以上的线程

2.必须多个线程必须使用同一个锁。 必须保证同步中只能有一个线程在运行。

好处:解决了线程的安全问题

弊端:消耗了资源,多个线程需要判断锁。

两个储户分别向银行存入300元,每次100,存3次。

找出改程序的安全问题。

1,明确哪些代码是多线程运行代码。

2,明确共享数据

3,明确多线程运行代码中哪些语句是操作共享数据的。

我们创建4个线程,与之前不同的是,同时使用了同步函数和同步代码快。看看结果如何。

class Mlpc implements Runnable
{
    private int ticket = 550;
    public boolean flag = true;
    Object obj = new Object();
    public void run() 
    {
        if(flag == true)
        {
            while(true)
            {
                synchronized (obj)
                {
                   if(ticket>0)
                   {    
                      try
                      {
                         Thread.sleep(20);
                      }
                      catch (Exception e)
                      {
                      }
                      System.out.println(Thread.currentThread()+" show "+ticket--);
                    } 
                 }
            }
        }
        else
        {
            while(true)
            {
                show();
            }
        }

    }
    public synchronized void show()
    {
            if(ticket>0)
            {    
                try
                {
                    Thread.sleep(20);
                }
                catch (Exception e)
                {
                }
                System.out.println(Thread.currentThread()+" show "+ticket--);
            } 
    }
}

 class mlpcDemo 
{
    public static void main(String[] args) 
    {     
        Mlpc m = new Mlpc();
        Thread th0 = new Thread(m);
        Thread th1 = new Thread(m);
        Thread th2 = new Thread(m);
        Thread th3 = new Thread(m);

        th0.start();
        try
        {
            Thread.sleep(20);    
        }
        catch (Exception e)
        {
        }
        m.flag = false;
        th1.start();
        th2.start();
        th3.start();
    }
}

 结果出现了票数为0。

技术分享

 public void run() 
    {
        if(flag == true)
        {
            while(true)
            {
               synchronized (this) //从原本的任意对象改为函数自己所属的对象this
{ if(ticket>0) { try { Thread.sleep(20); } catch (Exception e) { } System.out.println(Thread.currentThread()+" show "+ticket--); } } } } else { while(true) { show(); } } }

技术分享

现在,无论如何都显示正常结果。由此可以验证出函数都有自己所属的对象this,所以同步函数所使用的是this锁。

死锁:

当类中存在多个不同的锁时,而这些锁存在交叉,那么就可能会出现死锁

如:

1,锁A--11

      {锁B--22}

2,锁B--33

      {锁A--44} 

像上面这样出现交叉的锁。如11 33 同时执行后 要运行22 44就锁住了。22被下面B锁了,44又被上面11锁住了。那么就执行不下去了。 

class Demo implements Runnable
{
    private boolean flag;
    Demo(boolean flag)
    {
        this.flag = flag;
    }
    public void run() 
    {
        if(flag)
        {
            while(true)
            synchronized(MyLock.locka)
            {
                System.out.println("if locka");
                synchronized(MyLock.lockb)
                {
                    System.out.println("if lockb");
                }
            }
        }
        else
        {
            while (true)
            
            synchronized(MyLock.lockb)
            {
                System.out.println("else lockb");
                synchronized(MyLock.locka)
                {
                    System.out.println("else locka");
                }
            }
        }
    }
}
class MyLock
{
    static Object locka = new Object();
    static Object lockb = new Object();
}
 class ThreadTest 
{
    public static void main(String[] args) 
    {
       Thread t1 = new Thread(new Demo(true));
       Thread t2 = new Thread(new Demo(false));
        t1.start();
        try
        {
            Thread.sleep(100);
        }
        catch (Exception e)
        {

        }
        t2.start();
    }
}

 技术分享

生产者与消费者问题:

 1 class PCDemo 
 2 {
 3     public static void main(String[] args) 
 4     {
 5         Resource r = new Resource();
 6         Producer prd = new Producer(r);
 7         Consumer csu = new Consumer(r);
 8 
 9         Thread th0 = new Thread(prd);
10         Thread th1 = new Thread(prd);        
11         Thread th2 = new Thread(prd);
12         Thread th3 = new Thread(prd);
13 
14         Thread th4 = new Thread(csu);
15         Thread th5 = new Thread(csu);
16         Thread th6 = new Thread(csu);
17         Thread th7 = new Thread(csu);
18         th0.start();
19         th1.start();
20         th2.start();
21         th3.start();
22         th4.start();
23         th5.start();
24         th6.start();
25         th7.start();
26     }
27 }
28 
29 
30 class Resource
31 {
32     private String name;
33     private int count =1;
34     private boolean flag = false;
35 
36     public synchronized void set(String name)
37     {
38         if(flag)
39             try
40             {
41                 wait();
42             }
43             catch (Exception e)
44             {
45             }
46         this.name = name+"---"+count++;
47         System.out.println(Thread.currentThread().getName()+"生產"+this.name);
48         flag = true;
49         this.notify();
50     }
51     public synchronized void out()
52     {
53         if(!flag)
54             try
55             {
56                 wait();
57             }
58             catch (Exception e)
59             {
60             }
61         System.out.println(Thread.currentThread().getName()+"________消費"+this.name);
62         flag = false;
63         this.notify();
64     }
65 
66 }
67 
68 class Producer implements Runnable
69 {
70     private Resource res;
71     Producer(Resource res)
72     {
73         this.res = res;
74     }
75     public void run()
76     {
77         while(true)
78         {
79             res.set("物件");
80         }
81     }
82 }
83 
84 class Consumer implements Runnable
85 {
86     private Resource res;
87     Consumer(Resource res)
88     {
89         this.res = res;
90     }
91     public void run()
92     {
93         while(true)
94         {
95             res.out();
96         }
97     }
98 }

技术分享

 如果只是开启了两个线程,运行时会轮流“消费--生产”很和谐。但如果两者各开启两条线程,就会出现这样的情况。

 t1取得资格、执行权->生产商品1、将flag之位true后t1进入阻塞状态、t2取得资格和执行权->进入阻塞状态,t3取得资格和执行权->消费商品1->唤醒第一个进入线程池的t1、t3而后阻塞、t1取得资格,若同样取得资格t4抢到执行权->t4阻塞->t1取得执行权->生产商品2->唤醒t2、t2取得执行权后不再判断flag真假直接生产t3。

于是将if判断改为while循环,这样的确弥补了错误。但会导致t2"醒来"->判断flag真假->t2进入阻塞状态。造成了所有线程全部等待。于是使用notifyAll();总是唤醒全部线程。又一次解决了问题。

JDK1.5提供了多线程升级解决方案,将同步synchronized替换成现实lock操作,要为特定 Lock 实例获得 Condition 实例,使用其 newCondition() 方法。

Condition 实例实质上被绑定到一个锁上。Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,

以便通过将这些对象与任意 Lock 实现组合使用。

技术分享

黑马程序员-学习日记(多线程安全问题和死锁认识)

标签:

原文地址:http://www.cnblogs.com/tozr/p/4150570.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!