线程同步、死锁和通信——Java多线程(二)

一、多线程同步

上一篇随笔中,我曾遇到对多线程程序的多次运行结果不一致的情况,这主要是因为没有对这些线程在访问临界资源做必要的控制,而接下来就用线程的同步来解决这个问题。

1.同步代码块

 1 class RunnableDemo implements Runnable 
 2 {
 3     private int tickets=5;
 4     public void run()
 5     {
 6         while(true)
 7         {
 8             synchronized(this)//同步代码块语法定义如下
 9             {
10                 if(tickets<=0) break;
11                 try{
12                     Thread.sleep(100);
13                 }
14                 catch(Exception e){
15                     e.printStackTrace();
16                 }
17                 System.out.println(Thread.currentThread().getName()+"出售票:"+tickets);
18                 tickets -= 1;
19             }
20         }
21     }
22 }
23 
24 public class ThreadTest
25 {
26     public static void main(String[] args)
27     {
28         RunnableDemo r = new RunnableDemo();
29         new Thread(r).start();
30         new Thread(r).start();
31         new Thread(r).start();
32         new Thread(r).start();
33     }
34 }

在同一时刻只能由一个线程进入同步代码块内运行,只有当该线程离开同步代码块后,其他线程才能进入同步代码块内运行。

2.同步方法

即:把上述例子中同步代码块的内容,专门封装在一个方法里,通过在run()方法中调用创建的方法实现相应的功能。

 1 class RunnableDemo implements Runnable 
 2 {
 3     private int tickets=5;
 4     public void run()
 5     {
 6         while(tickets>0)
 7         {
 8             sale();
 9         }
10     }
11     public synchronized void sale()//同步方法
12     {
13         if(tickets>0){
14         try{
15             Thread.sleep(100);
16         }
17         catch(Exception e){
18             e.printStackTrace();
19         }
20         System.out.println(Thread.currentThread().getName()+"出售票:"+tickets);
21         tickets -= 1;
22         }
23     }
24 }
25 
26 public class ThreadTest
27 {
28     public static void main(String[] args)
29     {
30         RunnableDemo r = new RunnableDemo();
31         new Thread(r).start();
32         new Thread(r).start();
33         new Thread(r).start();
34         new Thread(r).start();
35     }
36 }

 二、死锁

如果有一组进程(或线程),线程1 已经占据资源R1,并持有资源R1上的锁,而且正在等待资源R2开锁;线程2已经占据资源R2,并拥有资源R2上的锁,却正在等待R1开锁。那么这两个线程都不释放自己占据的资源,同时申请不到对方资源上的锁,它们只能永远等待下去。这种现象就叫做死锁。

预防死锁的一种方法:利用有序资源分配策略——要求线程申请资源必须按照以编号上升的次序依次申请。

三、线程间通信

同属于一个进程的多个线程,是共享地址空间的,它们可以相互通信,共同协作来完成指定任务。

Java是通过Object类的wait()notify()notifyAll()这几个方法来实现线程间的通信。

wait():线程进入睡眠状态,直到其他线程进入并调用notify()notifyAll()为止。

notify():唤醒在该同步代码块中第1个调用wait()的线程。

notifyAll():唤醒在该同步代码块中所有调用wait()的线程,高优先级最先被唤醒。

 1 class Producer implements Runnable
 2 {
 3     Person q = null;
 4     public Producer(Person q)
 5     {
 6         this.q=q;
 7     }
 8     @Override
 9     public void run()
10     {
11         for(int i=0;i<10;i++)
12         {
13             if(i%2==0)
14             {
15                 q.set("张三","男");
16             }
17             else{
18                 q.set("李四","女");
19             }
20         }
21     }
22 }
23 class Consumer implements Runnable
24 {
25     Person q = null;
26     public Consumer(Person q)
27     {
28         this.q=q;
29     }
30     @Override
31     public void run() 
32     {
33         for(int i=0;i<10;++i)
34         {
35             q.get();
36         }
37     }
38 }
39 class Person
40 {
41     private String name = "李四";
42     private String sex = "女";
43     private boolean bFull = false;//当Consumer线程取走数据后,false
44     public synchronized void set(String name,String sex)
45     {
46         if(bFull)
47         {
48             try
49             {
50                 wait();//后来的线程要等待
51             }catch(InterruptedException e){
52                 e.printStackTrace();
53             }
54         }
55         this.name = name;
56         this.sex = sex;
57         bFull = true;//当Producer线程放入数据后,true
58         notify();//唤醒最先到达的线程
59     }
60     public synchronized void get()
61     {
62         if(!bFull)
63         {
64             try{
65                 wait();
66             }catch(InterruptedException e){
67                 e.printStackTrace();
68             }
69         }
70         System.out.println(name+"——>"+sex);
71         bFull = false;
72         notify();
73     }
74 }
75 public class ThreadCommunation
76 {
77     public static void main(String[] args)
78     {
79         Person q = new Person();
80         new Thread(new Producer(q)).start();
81         new Thread(new Consumer(q)).start();
82     }
83 }

***注意:wait()notify()notifyAll()这三个方法必须在synchronized方法中调用,该线程必须得到该对象的所有权。

四、线程的生命周期

控制线程生命周期的方法:suspend()resume()stop()方法,但是这三个方法都不推荐使用。

若想控制线程的生命周期,推荐使用在run()方法中添加循环条件的方法来实现对线程生命周期的控制。

posted @ 2017-04-29 21:45  王醒燕  阅读(289)  评论(0编辑  收藏  举报