zoukankan      html  css  js  c++  java
  • JAVA 多线程

    摘自:http://blog.csdn.net/seto2/article/details/42494245

    一、进程与线程


    个人所理解进程与线程的关系,如图:


            进程是资源的拥有者,所以切换中系统要付出较大的时空开销,如图中A-->B所占用的时间片段。因此导致系统中的进程数和切换频率不宜过高,限制了并发程度的提高,而线程不属于资源被分配的单位,只是共享所属进程的资源,因此可以轻装上阵,线程间的切换开销要比进程少得多,由于资源是共享的所以进程间的通信也比进程间通信容易得多。


    二、两种多线程的方式


    1.继承Thread类复写run方法

    [java] view plaincopy
    1. public class ThreadDemo  {  
    2.     public static void main(String[] args) {  
    3.         MyThread thread1=new MyThread();  
    4.         //thread1.setName("这里定义线程名");  
    5.         thread1.start();//启动该线程 注:此时共有main和thread1两个线程  
    6.           
    7.           
    8.     }  
    9. }  
    10. class MyThread extends Thread{  
    11.     @Override  
    12.     public void run() {  
    13.         // TODO Auto-generated method stub  
    14.         //super.run();  
    15.           
    16.         System.out.println("这里写线程所要执行的代码");  
    17.         System.out.println(Thread.currentThread().getName()); //Thread.currentThread() 获取当前线程对向  
    18.     }  
    19. //  public MyThread(String name){  
    20. //      super.setName(name);初始化同时命名线程  
    21. //  }  
    22. }  

    2.实现Runnable接口

    [java] view plaincopy
    1. public class ThreadDemo {  
    2.     public static void main(String[] args) {  
    3.         MyRunnable runnable = new MyRunnable();  
    4.         Thread thread = new Thread(runnable);  
    5.         thread.setName("这里定义线程名");  
    6.         thread.start();// 启动该线程 注:此时共有main和thread两个线程  
    7.   
    8.     }  
    9. }  
    10.   
    11. class MyRunnable implements Runnable {  
    12.   
    13.     @Override  
    14.     public void run() {  
    15.         // TODO Auto-generated method stub  
    16.         System.out.println("这里写线程所要执行的代码");  
    17.     }  
    18.   
    19. }  


    三、两种方式的比较(以售票为例,多个线程同时对Ticket类的实例对象进行操作)


    1.以继承Thread类方式

            如果以这种方式实现多线程操作共享数据,需要将共享资源作为继承于Thread的类成员变量,并提供set方法或在构造方法中予以赋值,而每一个Thread的实例set进同一个资源的载体。如代码中:MyThread继承Thread类,多个MyThread的实例即多个线程操作Ticket的一个实例,则将Ticket类作为MyThread的成员

    [java] view plaincopy
    1. class Ticket {  
    2.     private int num;//定义票数  
    3.   
    4.     public Ticket(int num) {  
    5.         this.num = num;  
    6.     }  
    7.   
    8.     /** 
    9.      * 提供售票方法,每次执行票数减一 
    10.      * @return  
    11.      */  
    12.     public synchronized void sell() {  
    13.         if (num > 0)  
    14.         System.out.println(Thread.currentThread().getName() + "售出一张,剩余" + --num);  
    15.         else  
    16.         System.out.println("对不起,票已售完");  
    17.     }  
    18.       
    19.   
    20.     public int getNum() {  
    21.         return num;  
    22.     }  
    23. }  
    24.   
    25. class MyThread extends Thread {  
    26.     private Ticket ticket;//将要操作的资源类作为成员变量,并提供set方法,将实例对象set进来  
    27.   
    28.     public void setTicket(Ticket ticket) {  
    29.         this.ticket = ticket;  
    30.     }  
    31.   
    32.     @Override  
    33.     public void run() {  
    34.         // TODO Auto-generated method stub  
    35.         while (true) {  
    36.             synchronized (ticket) {  
    37.                 if (ticket.getNum() == 0)  
    38.                     break;  
    39.                 ticket.sell();  
    40.             }  
    41.         }  
    42.     }  
    43. }  
    44.   
    45. public class ThreadDemo {  
    46.     public static void main(String[] args) throws InterruptedException {  
    47.   
    48.         Ticket ticket = new Ticket(10000);  
    49.         MyThread thread1 = new MyThread();  
    50.         MyThread thread2 = new MyThread();  
    51.         MyThread thread3 = new MyThread();  
    52.         thread1.setTicket(ticket);  
    53.         thread2.setTicket(ticket);  
    54.         thread3.setTicket(ticket);// 这里三个新创建的并发线程都要set进同一个资源(同一个实例对象)  
    55.         thread1.start();  
    56.         thread2.start();  
    57.         thread3.start();  
    58.           
    59.     }  
    60. }  

    2.以实现Runnable接口的方式(列举多种写法)

    • 在实现Runnable接口的同时继承要操作的共享资源类,如代码中新建MyRunnable extend Ticket implements,但Ticket中票数num要设为protect
    [java] view plaincopy
    1. class Ticket {  
    2.     protected int num;// 定义票数  
    3.   
    4.     public Ticket(int num) {  
    5.         this.num = num;  
    6.     }  
    7.   
    8.     /** 
    9.      * 提供售票方法,每次执行票数减一 
    10.      *  
    11.      * @return 
    12.      */  
    13.     public synchronized void sell() {  
    14.         if (num > 0)  
    15.             System.out.println(Thread.currentThread().getName() + "售出一张,剩余" + --num);  
    16.         else  
    17.             System.out.println("对不起,票已售完");  
    18.     }  
    19.   
    20.     public int getNum() {  
    21.         return num;  
    22.     }  
    23. }  
    24.   
    25. class MyRunnable extends Ticket implements Runnable{  
    26.   
    27.     public MyRunnable(int num) {  
    28.         super(num);  
    29.         // TODO Auto-generated constructor stub  
    30.     }  
    31.   
    32.     @Override  
    33.     public void run() {  
    34.         // TODO Auto-generated method stub  
    35.         while(true){  
    36.             synchronized (this) {  
    37.                 if(num==0)break;  
    38.                 sell();  
    39.             }  
    40.         }  
    41.     }  
    42. }  
    43. public class ThreadDemo{  
    44.     public static void main(String[] args) {  
    45.         Runnable myRunnable=new MyRunnable(10000);  
    46.         Thread thread1=new Thread(myRunnable);  
    47.         Thread thread2=new Thread(myRunnable);  
    48.         Thread thread3=new Thread(myRunnable);  
    49.         thread1.start();  
    50.         thread2.start();  
    51.         thread3.start();  
    52.           
    53.     }  
    54. }  

    •  仅实现Runnable接口不继承资源类,而是将资源类作为成员变量set或构造函数中赋值
    [java] view plaincopy
    1. class Ticket {  
    2.     privateint num;// 定义票数  
    3.   
    4.     public Ticket(int num) {  
    5.         this.num = num;  
    6.     }  
    7.   
    8.     /** 
    9.      * 提供售票方法,每次执行票数减一 
    10.      *  
    11.      * @return 
    12.      */  
    13.     public synchronized void sell() {  
    14.         if (num > 0)  
    15.             System.out.println(Thread.currentThread().getName() + "售出一张,剩余" + --num);  
    16.         else  
    17.             System.out.println("对不起,票已售完");  
    18.     }  
    19.   
    20.     public int getNum() {  
    21.         return num;  
    22.     }  
    23. }  
    24.   
    25. class MyRunnable implements Runnable{  
    26.     private Ticket ticket;  
    27.   
    28.     public void setTicket(Ticket ticket) {  
    29.         this.ticket = ticket;  
    30.     }  
    31.     public MyRunnable(Ticket ticket) {  
    32.         // TODO Auto-generated constructor stub  
    33.         this.ticket=ticket;  
    34.     }  
    35.     @Override  
    36.     public void run() {  
    37.         // TODO Auto-generated method stub  
    38.         while(true){  
    39.             synchronized (this) {  
    40.                 if(ticket.getNum()==0)break;  
    41.                 ticket.sell();  
    42.             }  
    43.         }  
    44.     }  
    45. }  
    46. public class ThreadDemo{  
    47.     public static void main(String[] args) {  
    48.         Runnable myRunnable = new MyRunnable(new Ticket(10000));  
    49.         Thread thread1 = new Thread(myRunnable);  
    50.         Thread thread2 = new Thread(myRunnable);  
    51.         Thread thread3 = new Thread(myRunnable);  
    52.         thread1.start();  
    53.         thread2.start();  
    54.         thread3.start();  
    55.   
    56.     }  
    57. }  

    • 直接用共享资源类去实现Runnable接口,这种方法还可以继续继承其他类
    [java] view plaincopy
    1. class Ticket implements Runnable {  
    2.     privateint num;// 定义票数  
    3.   
    4.     public Ticket(int num) {  
    5.         this.num = num;  
    6.     }  
    7.   
    8.     /** 
    9.      * 提供售票方法,每次执行票数减一 
    10.      *  
    11.      * @return 
    12.      */  
    13.     public synchronized void sell() {  
    14.         if (num > 0)  
    15.             System.out.println(Thread.currentThread().getName() + "售出一张,剩余" + --num);  
    16.         else  
    17.             System.out.println("对不起,票已售完");  
    18.     }  
    19.   
    20.     public int getNum() {  
    21.         return num;  
    22.     }  
    23.   
    24.     @Override  
    25.     public void run() {  
    26.         // TODO Auto-generated method stub  
    27.         while (true) {  
    28.             synchronized (this) {  
    29.                 if (num == 0break;  
    30.                 sell();  
    31.             }  
    32.         }  
    33.     }  
    34. }  
    35.   
    36. public class ThreadDemo {  
    37.     public static void main(String[] args) {  
    38.         Runnable ticket = new Ticket(10000);  
    39.         Thread thread1 = new Thread(ticket);  
    40.         Thread thread2 = new Thread(ticket);  
    41.         Thread thread3 = new Thread(ticket);  
    42.         thread1.start();  
    43.         thread2.start();  
    44.         thread3.start();  
    45.   
    46.     }  
    47. }  

            由此可见,实现Runnable这种方式除了可以避免java单继承的局限外,写法多变,更为灵活,可以根据实际情况采取相应的方法,在实际开发中也更为常用。

    四、线程的状态及转换



    • 初始化状态:调用 new方法产生一个线程对象后、调用 start 方法前所处的状态。
    • 就绪状态:当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态。当start()方法调用时,线程首先进入就绪状态。在线程运行之后或者从阻塞、等待或睡眠状态回来后,也返回到就绪状态。多个线程间对cpu的争夺,都是以该状态为前提,即只有就绪状态的线程才有资格争取cpu的执行。
    • 运行状态:线程调度程序从就绪线程池中选择一个线程执行后所处的状态。这也是线程进入运行状态的唯一一种方式。
    • 等待/阻塞/睡眠状态:这是线程有资格运行时它所处的状态但非就绪。实际上这个三状态组合为一种,其共同点是:线程仍旧是活的,但是当前没有条件运行。换句话说,它是可运行的,但是如果某件事件出现,他可能返回到可运行状态。
    • 消亡状态:当线程的run()方法完成时的状态。

    五、常用的进程控制语句

    1.Thread.sleep()
    [java] view plaincopy
    1. class MyRunnable implements Runnable{  
    2.     @Override  
    3.     public void run() {  
    4.         // TODO Auto-generated method stub  
    5.         try {  
    6.             Thread.sleep(1000);  
    7.             System.out.println("Thread.sleep(这里是毫秒)");  
    8.         } catch (InterruptedException e) {  
    9.             // TODO Auto-generated catch block  
    10.             e.printStackTrace();  
    11.         }  
    12.     }  
    13. }  
    2.设置优先级
    [java] view plaincopy
    1. public class ThreadDemo  {  
    2.     public static void main(String[] args) throws Exception{  
    3.         Runnable myrRunnable=new MyRunnable();  
    4.         Thread thread1=new Thread(myrRunnable);  
    5.         Thread thread2=new Thread(myrRunnable);  
    6.         thread1.setPriority(7);//这里可设优先级1-10代表由低到高   
    7.         thread1.start();  
    8.         thread2.start();  
    9.     }  
    10. }  
    线程默认优先级是5,Thread类中有三个常量,定义线程优先级范围:
    static int MAX_PRIORITY 线程可以具有的最高优先级。 
    static int MIN_PRIORITY 线程可以具有的最低优先级。 
    static int NORM_PRIORITY 分配给线程的默认优先级。
    3.线程让步yield()
        之前提到所有可争夺cpu执行权的线程都应该是就绪状态,但是如果正在执行的线程一只占用cpu不主动释放,会导致就绪线程吃内的线程在一段时间内得不到执行,而该时间内总是由运行着的线程霸占。结果是cpu在一个线程上处理一大批数据后,才切换到另一个线程上去。而yield()方法则是让当前线程主动放权,即有运行状态主动转为就绪状态,这样线程切换的频率大大提高,效果是cpu每在一个线程上处理几条数据就切换到另一个线程。


    4.join()

    Thread的非静态方法join()让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前,B不能工作。下面是一个两个线程向同一个数组中添加元素的例子,要求两线程向该数组添加结束后,主线程再打印该数组的元素。
    [java] view plaincopy
    1. public class JoinDemo{  
    2.     public static void main(String[] args) throws Exception {  
    3.         int[] nums = new int[6];  
    4.         Add add = new Add(nums);  
    5.         Thread thread1 = new Thread(add);  
    6.         Thread thread2 = new Thread(add);  
    7.         thread1.setName("线程1");  
    8.         thread2.setName("线程2");  
    9.         thread1.start();  
    10.         thread2.start();//此时共main、thread1、thread2三个线程  
    11.         thread1.join();//main读到这条与语句后,main只有在thread1结束后再运行(实际是main的线程栈追加到thread1的末尾)。此时只有thread1和thread2在运行  
    12.         thread2.join();//main方法读这条语句时刻,运行的语句有main、thread2,读完该条语句后,只有thread2运行结束后才执行main  
    13.         System.out.println(Arrays.toString(nums));//此时只有main线程  
    14.     }  
    15. }  
    16.   
    17. class Add implements Runnable {  
    18.   
    19.     private int[] nums;  
    20.       
    21.     @Override  
    22.     public  void run() {  
    23.         // TODO Auto-generated method stub  
    24.            
    25.         try {  
    26.             mainDo();  
    27.         } catch (InterruptedException e) {  
    28.             // TODO Auto-generated catch block  
    29.             e.printStackTrace();  
    30.         }  
    31.     }  
    32.   
    33.     private void mainDo() throws InterruptedException {  
    34.         for (int i = 0; i < 3; i++) {  
    35.             synchronized (this) {  
    36.             for (int j = 0; j < nums.length; j++) {  
    37.                   
    38.                     Thread.sleep(new Random().nextInt(500));  
    39.                     if (nums[j] == 0) {  
    40.                         nums[j] = new Random().nextInt(999) + 1;  
    41.                         System.out.println(Thread.currentThread().getName() + "向num[" + j + "]添加" + nums[j]);  
    42.                         break;  
    43.                     }  
    44.                 }  
    45.             }  
    46.         }  
    47.     }  
    48.   
    49.     public Add(int[] nums) {  
    50.   
    51.         this.nums = nums;  
    52.   
    53.     }  
    54.    
    55. }  
    通过以上11行和12行的语句使得打印结果如下:


    而不使用join方法结果是在添加数据前打印出空数组




    六、线程安全

    依然以常见的售票为例,这里设票数只有一张,两个线程出售,运行如下代码:
    [java] view plaincopy
    1. class Ticket {  
    2.     private int num;//定义票数  
    3.   
    4.     public Ticket(int num) {  
    5.         this.num = num;  
    6.     }  
    7.   
    8.     /** 
    9.      * 提供售票方法,每次执行票数减一 
    10.      * @return  
    11.      */  
    12.     public void sell() {  
    13.         if (num > 0)  
    14.         System.out.println(Thread.currentThread().getName() + "售出一张,剩余" + --num);  
    15.     }  
    16.       
    17.   
    18.     public int getNum() {  
    19.         return num;  
    20.     }  
    21. }  
    22.   
    23. class MyThread extends Thread {  
    24.     private Ticket ticket;//将要操作的资源类作为成员变量,并提供set方法,将实例对象set进来  
    25.   
    26.     public void setTicket(Ticket ticket) {  
    27.         this.ticket = ticket;  
    28.     }  
    29.   
    30.     @Override  
    31.     public void run() {  
    32.         // TODO Auto-generated method stub  
    33.         while (true) {        
    34.                 if (ticket.getNum() <= 0)  
    35.                     break;  
    36.                 ticket.sell();  
    37.                 Thread.yield();  
    38.         }  
    39.     }  
    40. }  
    运行结果:

    出现-1张余票的原因是两个线程同时在操作共享数据票数,然而在A线程执行一半,另一个进程抢占到cpu执行权,这是调度程序会对A发出中断,A响应中断,将当前全部状态进栈,接下来执行线程B,线程B又对票数进行修改,当A线程再次执行时,将之前栈内状态出栈,恢复被中断前的状态继续执行,结果就会出现这种情况。解决办法是在一个线程对共享数据读或写的过程中不被中断。

    线程同步的方法:


    1.同步代码块,代码块内的语句执行过程不会被中断
    synchronized(obj)
    { //obj表示同步监视器,是同一个同步对象,该例中可以将共享资源ticket作为监视器,个人理解要保证同步的线程监视器对象必须是同一个
    /**.....
    TODO SOMETHING
    */
    }
    2.方法的同步,加入修饰符synchronized ,监视器为this,格式如下,
    synchronized 返回值类型 方法名(参数列表){
    /**.....
    TODO SOMETHING
    */
    }
    注:由于在调用静态方法时,对象实例不一定被创建,因此,就不能使用this作为监视器,而是该静态方法所属类的字节码。
    常见的延迟单例模式中监视器就为Single.class

    [java] view plaincopy
    1. class Single {  
    2.     private Single() {  
    3.         // TODO Auto-generated constructor stub  
    4.     }  
    5.   
    6.     private static Single instance = null;  
    7.   
    8.     public static Single getInstance() {  
    9.         if (instance == null) {  
    10.             synchronized (Single.class) {  
    11.                 if (instance == null)  
    12.                     instance = new Single();  
    13.             }  
    14.   
    15.         }  
    16.         return instance;  
    17.     }  
    18.   
    19. }  
    3.ReentrantLock代替synchronized 

    [java] view plaincopy
    1. class MyRunnable extends Ticket implements Runnable{  
    2.     ReentrantLock lock=new ReentrantLock();  
    3.     public MyRunnable(int num) {  
    4.         super(num);  
    5.         // TODO Auto-generated constructor stub  
    6.     }  
    7.   
    8.     @Override  
    9.     public void run() {  
    10.         // TODO Auto-generated method stub  
    11.         while(true){  
    12.             lock.lock();  
    13.                 if(num==0)break;  
    14.                 sell();  
    15.             lock.unlock();  
    16.         }  
    17.     }  
    18. }  


    七、关于死锁

    关于汤子瀛版操作系统中描述的哲学家进餐问题,五位哲学家围坐在圆桌进餐,但每人仅右手边放置一只筷子,哲学家只有在同时获得两只筷子时才会进餐,进餐完毕释放两只筷子,不进餐时进行思考(等待),若有一位哲学家获得一只筷子,不会主动释放而是等待另一只筷子(请求保持),哲学家间不可抢夺筷子。如此循环,就有可能出现五个人手中各有一只筷子,在等待另一只筷子,造成再也无人进餐。这种情况就是死锁。

    进程和线程发生死锁的必要条件:

    对资源的互斥使用、不可强占、请求和保持、循环等待,当满足这四个条件时就有可能发生死锁,但是只要破坏其中任意一个条件就能避免思索。

    Java多线程中出现死锁的例子(这里的资源指的是同步时的监视器对象,同步代码块使用监视器对象就是占有该资源):

    [java] view plaincopy
    1. class Resource {  
    2.   
    3.     public static Object object1 = new Object();  
    4.     public static Object object2 = new Object();  
    5.     public static Object object3 = new Object();  
    6.     public static Object object4 = new Object();  
    7.   
    8. }  
    9.   
    10. class MyRunnable1 implements Runnable {  
    11.     @Override  
    12.     public void run() {  
    13.         // TODO Auto-generated method stub  
    14.         while (true) {  
    15.             synchronized (Resource.object1) {  
    16.                 try {  
    17.                     Thread.sleep(1000);  
    18.                 } catch (InterruptedException e) {  
    19.                     // TODO Auto-generated catch block  
    20.                     e.printStackTrace();  
    21.                 }  
    22.                 synchronized (Resource.object2) {  
    23.                     System.out.println("执行MyRunnable1.fun()");  
    24.                 }  
    25.             }  
    26.         }  
    27.     }  
    28. }  
    29.   
    30. class MyRunnable2 implements Runnable {  
    31.     @Override  
    32.     public void run() {  
    33.         // TODO Auto-generated method stub  
    34.         while (true) {  
    35.             synchronized (Resource.object2) {  
    36.                 try {  
    37.                     Thread.sleep(1000);  
    38.                 } catch (InterruptedException e) {  
    39.                     // TODO Auto-generated catch block  
    40.                     e.printStackTrace();  
    41.                 }  
    42.                 synchronized (Resource.object1) {  
    43.                     System.out.println("执行MyRunnable2.fun()");  
    44.                 }  
    45.             }  
    46.         }  
    47.     }  
    48. }  
    49.   
    50. public class ThreadDemo {  
    51.     public static void main(String[] args) {  
    52.         Runnable runnable1 = new MyRunnable1();  
    53.         Runnable runnable2 = new MyRunnable2();  
    54.         Thread thread1 = new Thread(runnable1);  
    55.         Thread thread2 = new Thread(runnable2);  
    56.         thread1.start();  
    57.         thread2.start();  
    58.     }  
    59. }  
    关于避免死锁,个人理解:对于上述代码中出现了thread1和thread2互斥使用资源object1和object2,并且相互等待(循环等待)对方线程占有的资源(锁),同时同步代码块也满足了请求保持和不可抢占。解决办法是可以将object1和object2两个资源(锁)封装成一个对象(整体)(锁)。这样可以避免循环等待。对应哲学家进餐问题中,将两只筷子封装成一套餐具,只有得到一套餐具时才可以进餐,在多线程中也应尽量避免锁的嵌套使用。以免造成循环等待的情况出现。


    八、线程通讯

    wait():让当前线程放弃监视器进入等待,直到其他线程调用同一个监视器并调用notify()或notifyAll()为止。
    notify():唤醒在同一对象监听器中调用wait方法的第一个线程。
    notifyAll():唤醒在同一对象监听器中调用wait方法的所有线程。

    下面的例子是通过两个线程向一个数组内添加元素,通过线程间的通讯达到一个线程添加一个元素后,另一个线程再添加一个元素如此往复的效果。

    [java] view plaincopy
    1. import java.util.Arrays;  
    2. import java.util.Random;  
    3.   
    4. public class ThreadTest {  
    5.     public static void main(String[] args) throws Exception {  
    6.         int[] arrary = new int[1000];  
    7.         Res res = new Res(arrary, false);  
    8.         Runnable r1 = new add1(res);  
    9.         Runnable r2 = new add2(res);  
    10.         Thread t1 = new Thread(r1);  
    11.         Thread t2 = new Thread(r2);  
    12.         t1.start();  
    13.         t2.start();  
    14.         t1.join();  
    15.         t2.join();  
    16.         //main线程最后打印结果  
    17.         System.out.println(Arrays.toString(arrary));  
    18.   
    19.     }  
    20.   
    21. }  
    22.   
    23. /** 
    24.  * @author Mr 
    25.  * 资源类,封装有数组和线程间通讯的标志位 
    26.  */  
    27. class Res {  
    28.     private int[] array;  
    29.     private boolean sign;  
    30.   
    31.     public int[] getArray() {  
    32.         return array;  
    33.     }  
    34.   
    35.     public void setArray(int[] array) {  
    36.         this.array = array;  
    37.     }  
    38.   
    39.     public boolean isSign() {  
    40.         return sign;  
    41.     }  
    42.   
    43.     public void setSign(boolean sign) {  
    44.         this.sign = sign;  
    45.     }  
    46.   
    47.     public Res(int[] array, boolean sign) {  
    48.         // TODO Auto-generated constructor stub  
    49.         this.array = array;  
    50.         this.sign = sign;  
    51.     }  
    52. }  
    53.   
    54. /** 
    55.  * @author Mr 
    56.  *  线程1负责向数组的前半部分添加元素 
    57.  */  
    58. class add1 implements Runnable {  
    59.     private Res res;  
    60.   
    61.     public add1(Res res) {  
    62.         // TODO Auto-generated constructor stub  
    63.         this.res = res;  
    64.     }  
    65.   
    66.     @Override  
    67.     public void run() {  
    68.         // TODO Auto-generated method stub  
    69.         for (int i = 0; i < res.getArray().length / 2; i++) {  
    70.             synchronized (res) {  
    71.                 if (res.isSign())  
    72.                     try {  
    73.                         res.wait();  
    74.                     } catch (InterruptedException e) {  
    75.                         // TODO Auto-generated catch block  
    76.                         e.printStackTrace();  
    77.                     }  
    78.                 int num = new Random().nextInt(99) + 1;  
    79.                 res.getArray()[i] = num;  
    80.                 System.out.println(Thread.currentThread().getName() + "向array["  
    81.                         + i + "]添加" + num);  
    82.                 res.setSign(true);  
    83.                 res.notify();  
    84.             }  
    85.         }  
    86.     }  
    87. }  
    88.   
    89. /** 
    90.  * @author Mr 
    91.  *  线程2负责向数组的后半部分添加元素 
    92.  */  
    93. class add2 implements Runnable {  
    94.   
    95.     private Res res;  
    96.   
    97.     public add2(Res res) {  
    98.         // TODO Auto-generated constructor stub  
    99.         this.res = res;  
    100.     }  
    101.   
    102.     @Override  
    103.     public void run() {  
    104.         // TODO Auto-generated method stub  
    105.         for (int i = res.getArray().length / 2; i < res.getArray().length; i++) {  
    106.             synchronized (res) {  
    107.                 if (!res.isSign())  
    108.                     try {  
    109.                         res.wait();  
    110.                     } catch (InterruptedException e) {  
    111.                         // TODO Auto-generated catch block  
    112.                         e.printStackTrace();  
    113.                     }  
    114.                 int num = new Random().nextInt(99) + 1;  
    115.                 res.getArray()[i] = num;  
    116.                 System.out.println(Thread.currentThread().getName() + "向array["  
    117.                         + i + "]添加" + num);  
    118.                 res.setSign(false);  
    119.                 res.notify();  
    120.             }  
    121.         }  
    122.     }  
    123. }  

    效果如下:


    另外在jdk1.5后提供了新的方法以代替锁和wait、notify、notifyall方法,常见的生产者与消费者的例子,其中三个生产者线程,两个消费者线程:

    [java] view plaincopy
    1. import java.util.concurrent.locks.Condition;  
    2. import java.util.concurrent.locks.Lock;  
    3. import java.util.concurrent.locks.ReentrantLock;  
    4.   
    5. public class ThreadTest{  
    6.     public static void main(String[] args) {  
    7.   
    8.         Res res = new Res("豆浆"0);  
    9.         Runnable r1 = new producer(res);  
    10.         Runnable r2 = new customer(res);  
    11.         Thread t1 = new Thread(r1);  
    12.         Thread t2 = new Thread(r1);  
    13.         Thread t3 = new Thread(r1);  
    14.   
    15.         Thread t5 = new Thread(r2);  
    16.         Thread t6 = new Thread(r2);  
    17.   
    18.         t1.start();  
    19.         t2.start();  
    20.         t3.start();  
    21.   
    22.         t5.start();  
    23.         t6.start();  
    24.   
    25.     }  
    26. }  
    27.   
    28. class Res {  
    29.     private String name;// 商品(资源)名  
    30.     private int no;// 商品编号  
    31.     private boolean sign;  
    32.     private final Lock lock = new ReentrantLock();  
    33.     private Condition condition_con = lock.newCondition();  
    34.     private Condition condition_pro = lock.newCondition();  
    35.   
    36.     public Res(String name, int no) {  
    37.         // TODO Auto-generated method stub  
    38.         this.name = name;  
    39.         this.no = no;  
    40.     }  
    41.   
    42.     /** 
    43.      * 生产 
    44.      */  
    45.     public void pro() {  
    46.         lock.lock();  
    47.         while (sign) {  
    48.             try {  
    49.                 condition_pro.await();// 生产相关线程wait  
    50.             } catch (InterruptedException e) {  
    51.                 // TODO Auto-generated catch block  
    52.                 e.printStackTrace();  
    53.             }  
    54.         }  
    55.         System.out.println(Thread.currentThread().getName() + "生产了" + name  
    56.                 + ++no);  
    57.         sign = true;  
    58.         condition_con.signalAll();// 唤起消费相关线程  
    59.         lock.unlock();  
    60.     }  
    61.   
    62.     /** 
    63.      * 消费 
    64.      */  
    65.     public void cus() {  
    66.         lock.lock();  
    67.         while (!sign) {  
    68.             try {  
    69.                 condition_con.await();// 消费相关线程wait  
    70.             } catch (InterruptedException e) {  
    71.                 // TODO Auto-generated catch block  
    72.                 e.printStackTrace();  
    73.             }  
    74.         }  
    75.         System.out  
    76.                 .println(Thread.currentThread().getName() + "消费了" + name + no);  
    77.         sign = false;  
    78.         condition_pro.signalAll();// 唤起生产相关线程  
    79.         lock.unlock();  
    80.     }  
    81. }  
    82.   
    83. /** 
    84.  * @author Mr 生产者 
    85.  */  
    86. class producer implements Runnable {  
    87.     private Res res;  
    88.   
    89.     public producer(Res res) {  
    90.         // TODO Auto-generated constructor stub  
    91.         this.res = res;  
    92.     }  
    93.   
    94.     @Override  
    95.     public void run() {  
    96.         // TODO Auto-generated method stub  
    97.         while (true) {  
    98.             res.pro();  
    99.         }  
    100.     }  
    101. }  
    102.   
    103. /** 
    104.  * @author Mr 消费者 
    105.  */  
    106. class customer implements Runnable {  
    107.     private Res res;  
    108.   
    109.     public customer(Res res) {  
    110.         // TODO Auto-generated constructor stub  
    111.         this.res = res;  
    112.     }  
    113.   
    114.     @Override  
    115.     public void run() {  
    116.         // TODO Auto-generated method stub  
    117.         while (true) {  
    118.             res.cus();  
    119.         }  
    120.     }  
    121. }  
  • 相关阅读:
    IDEA的JDBC报错解决
    java注解
    Java反射
    javaIO
    工程师的认知
    前端的一些性能提升
    ES6函数的扩展
    2020新年愿望
    高维护性的javascript
    Chrome Dev tools的几点小技巧
  • 原文地址:https://www.cnblogs.com/dengshiwei/p/4258463.html
Copyright © 2011-2022 走看看