您的位置:首页 > 编程语言 > Java开发

多线程的等待唤醒机制和Lock锁

2014-10-21 07:19 330 查看
多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。通过一定的手段使各个线程能有效的利用资源。而这种手段即—— 等待唤醒机制。

等待唤醒机制所涉及到的方法:

wait() :等待,将正在执行的线程释放其执行资格 和 执行权,并存储到线程池中。

notify():唤醒,唤醒线程池中被wait()的线程,一次唤醒一个,而且是任意的。

notifyAll(): 唤醒全部:可以将线程池中的所有wait() 线程都唤醒。

 

其实,所谓唤醒的意思就是让 线程池中的线程具备执行资格。必须注意的是,这些方法都是在 同步中才有效。同时这些方法在使用时必须标明所属锁,这样才可以明确出这些方法操作的到底是哪个锁上的线程。

仔细查看JavaAPI之后,发现这些方法 并不定义在 Thread中,也没定义在Runnable接口中,却被定义在了Object类中,为什么这些操作线程的方法定义在Object类中?

因为这些方法在使用时,必须要标明所属的锁,而锁又可以是任意对象。能被任意对象调用的方法一定定义在Object类中。

01.public class Resource {
02.    private String name;
03.    private int count = 1;
04.    private boolean flag ;
05.
06.    public synchronized void set(String name){
07.        if(flag){
08.            try {
09.                this.wait();
10.            } catch (InterruptedException e) {
11.                // TODO Auto-generated catch block
12.                e.printStackTrace();
13.            }
14.        }
15.            this.name = name + count;
16.            count++;
17.
18.            System.out.println(Thread.currentThread().getName()
19.                        + "The producer name is :++++++++++++++ " + this.name);
20.
21.            flag = true;
22.
23.            this.notify();
24.
25.    }
26.
27.    public synchronized void out(){
28.        if(!flag){
29.            try {
30.                this.wait();
31.            } catch (InterruptedException e) {
32.                // TODO Auto-generated catch block
33.                e.printStackTrace();
34.            }
35.        }
36.        System.out.println(Thread.currentThread().getName() +
37.                 "The consumer name is : ---------------------------" + this.name);
38.        flag = false;
39.
40.        this.notify();
41.
42.    }
43.
44.}
45.
46.public class Producer implements Runnable {
47.
48.    private Resource r;
49.    public Producer(Resource r){
50.        this.r = r;
51.    }
52.    @Override
53.    public void run() {
54.
55.        while(true){
56.            r.set("馒头");
57.        }
58.
59.    }
60.}
61.
62.public class Consumer implements Runnable {
63.
64.    private Resource r;
65.    public Consumer(Resource r ){
66.        this.r = r;
67.    }
68.    @Override
69.    public void run() {
70.
71.        while(true){
72.            r.out();
73.        }
74.    }
75.}
76.
77.public class ProConDemo {
78.
79.    public static void main(String[] args) {
80.
81.        //资源对象
82.        Resource r = new Resource();
83.        //任务对象
84.        Producer pro = new Producer(r);
85.        Consumer con = new Consumer(r);
86.        //线程对象,两个生产者,两个消费者
87.        Thread t1 = new Thread(pro);
88.        Thread t2 = new Thread(pro);
89.
90.        Thread t3 = new Thread(con);
91.        Thread t4 = new Thread(con);
92.
93.        //开启线程
94.        t1.start();
95.        t2.start();
96.        t3.start();
97.        t4.start();
98.    }
99.
100.}


经过分析,发现,产生这种状况的根源是:

1,本方唤醒了本方。

2,被唤醒的本方没有判断标记。

为此,我们要做的改进是——将if 判断改为 while 标记,保证,每次被wait的线程在醒了之后,都得再次判断标记。进过修改,再次运行之后,会发现新的问题又来了,产生了死锁。经过分析,这个问题的根源在于,本方唤醒了本方,被唤醒的本方判断标记后,发现,标记不成立,就继续等待,因此,再也没有活着的线程。为此,我们需要唤醒,所有的线程,就用到了 notifyAll() 。将线程池中,所有等待的线程都叫醒。

经过修改后的代码成功的解决了上述的问题。

01.public class Resource {
02.    private String name;
03.    private int count = 1;
04.    private boolean flag ;
05.
06.    public synchronized void set(String name){
07.        //用while 确保线程醒了后再次,判断标记。
08.        while(flag){
09.            try {
10.                this.wait();
11.            } catch (InterruptedException e) {
12.                // TODO Auto-generated catch block
13.                e.printStackTrace();
14.            }
15.        }
16.            this.name = name + count;
17.            count++;
18.
19.            System.out.println(Thread.currentThread().getName()
20.                        + "The producer name is :++++++++++++++ " + this.name);
21.
22.            flag = true;
23.            //唤醒所有被等待的线程
24.            this.notifyAll();
25.
26.    }
27.
28.    public synchronized void out(){
29.        //用while 确保线程醒了后再次,判断标记。
30.        while(!flag){
31.            try {
32.                this.wait();
33.            } catch (InterruptedException e) {
34.                // TODO Auto-generated catch block
35.                e.printStackTrace();
36.            }
37.        }
38.        System.out.println(Thread.currentThread().getName() +
39.                 "The consumer name is : ---------------------------" + this.name);
40.        flag = false;
41.        //唤醒所有被等待的线程
42.        this.notifyAll();
43.
44.    }
45.}


Lock:比同步函数和同步代码块要好一些。同步函数还是同步代码块所做的都是隐式的锁操作。并且,同步函数或者同步代码块使用的锁和监视器是同一个。

Lock接口:是将锁进行单独对象的封装。而且提供了对锁对象很多功能。  比如:lock()获取锁,unlock()释放锁。  Lock对锁的操作都是显示操作。所以它的出现要比同步函数或者同步代码块明确的多,更符合面向对象思想。

简单一句话:Lock接口的出现替代同步。

 

01.class Resource
02.{
03.
04.    private String name;
05.    private int count = 1;
06.    private boolean flag;
07.    //创建一个锁对象。
08.    private final Lock lock = new ReentrantLock();
09.    //创建一个生产者的监视器。
10.    private Condition producer_con = lock.newCondition();
11.    //创建一个消费者监视器。
12.    private Condition consumer_con = lock.newCondition();
13.
14.    public  void set(String name)//
15.    {
16.        //获取锁。
17.        lock.lock();
18.        try
19.        {
20.
21.            while(flag)
22.                try{producer_con.await();}catch(InterruptedException e){}
23.            this.name = name + count;
24.
25.            count++;
26.
27.            System.out.println(Thread.currentThread().getName()+".....生产者......"+this.name);
28.            flag = true;
29.            consumer_con.signal();
30.        }
31.        finally
32.        {
33.            //释放锁。
34.            lock.unlock();
35.        }
36.    }
37.
38.    public  void get()//
39.    {
40.        lock.lock();
41.        try
42.        {
43.
44.            while(!flag)
45.                try{consumer_con.await();}catch(InterruptedException e){}
46.
47.            System.out.println(Thread.currentThread().getName()+".........消费者......"+this.name);
48.            flag = false;
49.            producer_con.signal();
50.        }
51.        finally
52.        {
53.            lock.unlock();
54.        }
55.    }
56.}

------- android培训java培训IOS培训.Net培训期待与您交流!
----------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 多线程
相关文章推荐