黑马程序员——java基础---多线程(二)

    线程间的通信:简单来说,就是多个线程在操作同一资源,但操作的动作不同。

  试想一下,对于同一个资源做不同的操作,这势必会在操作的过程中产生矛盾。为了避免这种情况的发生,就需要用的synchronized来保证,每次对共享资源的操作,只能是一条线程在进行。在用到同步的时候,就会因需求问题用到wait()、notify()、notifyAll()这三个方法。

  wait()方法:作用是使调用线程自动释放CPU使用权,从而使线程本身进入线程池继续等待。

  notify()方法:由运行的线程唤醒线程池中排在最前面的等待线程,使该线程具有使用CPU的资格。

  notifyAll()方法:由运行的线程唤醒线程池中的所有等待线程。

  因为三个方法都需以对持有(锁)监视器的对象操作,也就使他们只能在同步中使用,因为只有同步中才会出现锁这个概念。而通过查阅API,我们很容易就发现这三个方法都被定义在Object类当中,那么这些操作锁的方法为什么会被定义在Object类中呢?我们可以这样理解,同步中对于锁的标志位可以是任意的特有对象,而这三个方法都是操作锁的,也就是说他们可以操作任何对象,Java中能操作任何对象的类也只有Object类了。

  虽然有这些方法来使同步机制更加完善,但是对于某些情况同步机制并不能完美的完成相应的功能。

  针对以下代码:

 1 *
 2  * 生产者消费者问题
 3  */
 4 public class ProducerConsumerDemo{
 5     public static void main(String[] args){
 6         Resource r = new Resource();
 7         Producer pro = new Producer(r);
 8         Consumer con = new Consumer(r);
 9 
10         Thread t1 = new Thread(pro);
11         Thread t3 = new Thread(pro);
12         Thread t2 = new Thread(con);
13         Thread t4 = new Thread(con);
14 
15         t1.start();
16         t2.start();
17         t3.start();
18         t4.start();
19     }
20 }
21 //定义共享的资源类
22 class Resource{
23     private String name;
24     private int count = 1;
25     private boolean flag = false;
26 
27     public synchronized void set(String name){
28         while(flag)
29             try{
30                 this.wait();
31             }catch(Exception e){
32 
33             }
34         this.name = name+"--"+count++;
35         System.out.println(Thread.currentThread().getName()+"---生产者---"+this.name);
36         flag = true;
37         this.notifyAll();
38     }
39     public synchronized void out(){
40         while(!flag)
41             try{
42                 wait();
43             }catch(Exception e){
44             }
45         System.out.println(Thread.currentThread().getName()+"-----消费者-----"+this.name);
46         flag = false;
47         this.notifyAll();
48     }
49 }
50 //定义生产者类
51 class Producer implements Runnable{
52     private Resource res;
53     Producer(Resource res){
54         this.res = res;
55     }
56     public void run(){
57         while(true){
58             res.set("+商品+");
59         }
60     }
61 }
62 //定义消费者类
63 class Consumer implements Runnable{
64     private Resource res;
65     Consumer(Resource res){
66         this.res = res;
67     }
68     public void run(){
69         while(true){
70             res.out();
71         }
72     }
73 }

  在这个类中虽然可以初步满足生产者消费者这一功能需求,但我们可以发现在代码中我们用了notifyAll()这个方法,这个方法每运行一次将会把所有的等待状态的线程全部唤醒,这会给计算机带来很大的耗费,这种情况显然并不是我们所期望的。

   针对上述问题,官方在JDK1.5版本中进行了升级:

  * 引进了Lock接口从而替代了synchronized 
  * 用Condition 替代了 Object监视器方法
  这样做的好处就是:一定程度上简化了同步代码的编写;一个Condition对象可以对不同锁进行操作,而且可以根据需求对特定的线程进行唤醒。而同时也要注意:一定要在对应的程序代码尾部执行释放锁的操作。
下面是通过运用Lock和Condition改进后的代码:
 1 import java.util.concurrent.locks.Condition;
 2 import java.util.concurrent.locks.ReentrantLock;
 3 
 4 /*
 5  * 生产者消费者问题
 6  */
 7 public class ProducerConsumerDemo2{
 8     public static void main(String[] args){
 9         Resource2 r = new Resource2();
10         Producer2 pro = new Producer2(r);
11         Consumer2 con = new Consumer2(r);
12 
13         Thread t1 = new Thread(pro);
14         Thread t3 = new Thread(pro);
15         Thread t2 = new Thread(con);
16         Thread t4 = new Thread(con);
17 
18         t1.start();
19         t2.start();
20         t3.start();
21         t4.start();
22     }
23 }
24 //定义共有的资源
25 class Resource2{
26      private String name;
27      private int count = 1;
28      private boolean flag = false;
29     
30      private ReentrantLock lock = new ReentrantLock();
31      Condition c_pro = lock.newCondition();
32      Condition c_con = lock.newCondition();
33 
34     public void set(String name) throws InterruptedException{
35         lock.lock();
36         try{
37             while(flag)
38                     c_pro.await();
39             this.name = name+"--"+count++;
40             System.out.println(Thread.currentThread().getName()+"---生产者---"+this.name);
41             flag = true;
42             c_con.signal();
43         }finally{
44             lock.unlock();
45         }
46     }
47     public void out() throws InterruptedException{
48         lock.lock();
49         try{
50             while(!flag)
51                 c_con.await();
52             System.out.println(Thread.currentThread().getName()+"-----消费者-----"+this.name);
53             flag = false;
54             c_pro.signal();
55         }finally{
56             lock.unlock();
57         }
58     }
59 }
60 //定义生产者
61 class Producer2 implements Runnable{
62     private Resource2 res;
63     Producer2(Resource2 r){
64         this.res = r;
65     }
66     public void run(){
67         while(true){
68             try {
69                 res.set("+商品+");
70             } catch (InterruptedException e) {
71                 // TODO Auto-generated catch block
72                 e.printStackTrace();
73             }
74         }
75     }
76 }
77 //定义消费者
78 class Consumer2 implements Runnable{
79     private Resource2 res;
80     Consumer2(Resource2 r){
81         this.res = r;
82     }
83     public void run(){
84         while(true){
85             try {
86                 res.out();
87             } catch (InterruptedException e) {
88                 e.printStackTrace();
89             }
90         }
91     }
92 }

 

 接下来是关于线程的停止:
   1、正常的情况是,等待线程运行完,自动释放CPU占用权。这种方式也是最为安全的。
   2、多线程中常用循环控制语句,我们可以通过设置标志位,来使线程在不满足条件的情况下自动释放CPU资源,停止运行。
   3、对于占有资源但却因为某种原因进入冻结的线程,我们可以使用interrupt()方法来打断其状态,进而使之可以正常运行释放资源。

 设置守护进程的方法:通过调用
SetDaemon()方法,可以把对应进程设置成守护进程或者是用户进程。

  join()方法:作用是长期占有CPU使用权。原理是:当A线程执行到了B线程的join方法时,A就会等待,等B线程执行完,A才会执行。Join可以用来临时加入线程执行。
代码示例:
 1 class Demo implements Runnable{
 2     public void run(){
 3         for(int x=0; x<20; x++){
 4             System.out.println(Thread.currentThread().getName()+"---"+x);
 5         }
 6     }
 7 }
 8 public class JoinTest {
 9     public static void main(String[] args) throws InterruptedException {
10         Demo d = new Demo();
11         Thread t1 = new Thread(d);
12         Thread t2 = new Thread(d);
13         
14         t1.start();
15         t1.join();
16         t2.start();
17         
18         for(int x=0; x<30; x++){
19             System.out.println("main--"+x);
20         }
21         System.out.println("over");
22     }
23 }

 

  执行结果:
 1 Thread-0---0
 2 Thread-0---1
 3 Thread-0---2
 4 Thread-0---3
 5 Thread-0---4
 6 Thread-0---5
 7 Thread-0---6
 8 Thread-0---7
 9 Thread-0---8
10 Thread-0---9
11 Thread-0---10
12 Thread-0---11
13 Thread-0---12
14 Thread-0---13
15 Thread-0---14
16 Thread-0---15
17 Thread-0---16
18 Thread-0---17
19 Thread-0---18
20 Thread-0---19
21 main--0
22 Thread-1---0
23 main--1
24 Thread-1---1
25 Thread-1---2
26 Thread-1---3
27 Thread-1---4
28 Thread-1---5
29 Thread-1---6
30 Thread-1---7
31 main--2
32 Thread-1---8
33 Thread-1---9
34 main--3
35 Thread-1---10
36 main--4
37 Thread-1---11
38 Thread-1---12
39 Thread-1---13
40 Thread-1---14
41 Thread-1---15
42 Thread-1---16
43 main--5
44 main--6
45 main--7
46 main--8
47 main--9
48 main--10
49 main--11
50 main--12
51 main--13
52 main--14
53 main--15
54 main--16
55 main--17
56 Thread-1---17
57 main--18
58 Thread-1---18
59 main--19
60 Thread-1---19
61 main--20
62 main--21
63 main--22
64 main--23
65 main--24
66 main--25
67 main--26
68 main--27
69 main--28
70 main--29
71 over

  我们可以发现只有当线程t1执行完以后,main线程才会和t2线程继续强度CPU的使用权,进而相继输出。所以可以理解为join()方法是线程具有了长期占有CPU的资格。

    关于线程优先级的知识:

     1、线程的优先级设置范围为(1-10);

       2、线程默认的优先级是5;

           3、我们可以通过SetPriority(1-10)来对线程的优先级进行设置。

     4、因为相邻的优先级效果差别很小,说以一般提倡对优先级设置一下几个值:Thread.MAX_PRIORITY 10 ;Thread.MIN_PRIORITY 1;Thread.NORM_PRIORITY 5
   yield()方法:暂停当前正在执行的线程,转而去执行其它线程。 
开发中应用:保证以下三个代码同时运行。
案例代码:
 1 new Thread(){
 2       for(int x=0;x<100;x++){
 3          sop(Thread.currentThread().getName())
 4       }
 5 }.start();
 6 
 7       for(int x=0;x<100;x++){
 8          sop(Thread.currentThread().getName())
 9       }
10 
11 Runnable r=new Runnable(){
12    public voud run(){
13       for(int x=0;x<100;x++){
14          sop(Thread.currentThread().getName())
15       }
16    }
17 };
18 
19 new Thread(r).start();

 


 

posted @ 2015-07-15 01:31  shadowW_W  阅读(142)  评论(0编辑  收藏  举报