Java多线程(二)

来源:互联网 发布:进入带网络的安全模式 编辑:程序博客网 时间:2024/05/16 23:42

Java多线程

前言:

在上一节中,我们已经对Java的多线程进行了初步的了解;

在这一节中,我们将继续深入地探讨Java多线程的更多知识点!

线程堵塞,守护线程,线程组,经典线程同步问题:消费者与生产者问题

还有是很难的线程池,那么请读者跟随笔者的脚步,进一步剖析多线程

的更多知识!


线程堵塞:


Thread类中关于控制线程堵塞的相关方法:




守护线程:




线程组的使用

介绍:


在Java中,我们可以通过java.lang.ThreadGroup对线程进行组操作;每一个线程都归属于某个线程组管理的一员,如在main()工作流程

产生的一个都线程,则产生的线程属于main这个线程组管理的一员;在创建Thread实例时,如果没有指定线程组参数,则默认隶属于创建者

线程所隶属的线程组;这种隶属关系在创建新线程时指定,在线程的整个生命周期里都不能够改变


作用:

简化对多个线程的管理,对若干线程同时操作,比如:调用线程组的方法设置所有线程的优先级,调用线程组的方法启动或堵塞组中所有的

线程等;其实线程组最重要的意义是安全,Java默认创建的线程都是属于系统线程组,而处于同一线程组的线程时可以相互修改对方数据的;

当如果在不同的线程组中,那么就不能"跨线程组"修改数据,从一定程度上保证了数据的安全


线程组提供的操作:

①集合管理方法,用于管理包含在线程组中的线程与子线程组

②组操作方法:设置或者获取线程对象的属性

③组中所有线程的操作方法,针对组中所有线程与子线程执行某一操作,eg:线程启动,恢复

④访问限制方法基于线程组的成员关系


代码使用实例:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. package com.jay.example;  
  2.   
  3. /* 
  4.  * 该代码演示的是线程组的集合管理,一些对线程组中线程与子线程组的 
  5.  * 一些基本操作,这里故意弄了一个自定义线程增加效果; 
  6.  * 输出结果每次都不一样,这个是因为多线程执行的随机性哦! 
  7.  *  
  8.  * */  
  9.   
  10. class MyThread extends Thread  
  11. {  
  12.     MyThread()  
  13.     {  
  14.         super("呵呵");  
  15.     }  
  16.       
  17.     @Override  
  18.     public void run() {  
  19.         for(int i = 0;i < 10;i++)  
  20.         {  
  21.             System.out.println(i);  
  22.         }  
  23.     }  
  24. }  
  25.   
  26. public class ThreadGroupDemo {  
  27.     public static void main(String[] args) {  
  28.         //故意添加一个线程演示效果  
  29.         MyThread mt = new MyThread();  
  30.         mt.start();  
  31.           
  32.         //①获得当前线程的线程组对象  
  33.         ThreadGroup tg = Thread.currentThread().getThreadGroup();  
  34.         //②打印线程组的名称  
  35.         System.out.println(tg.getName());  
  36.         //③获得线程组中活动线程的数目  
  37.         int count = tg.activeCount();  
  38.         System.out.println("当前线程组中有"+count+"个活动线程!");  
  39.         //④将当前所有活动线程放到一个线程数组中  
  40.         Thread[] th = new Thread[count];  
  41.         tg.enumerate(th);  
  42.         //⑤输出线程数组中所有线程的名字:  
  43.         for(int i = 0;i < count;i++)  
  44.         {  
  45.             System.out.print("线程#"+(i + 1) + "=");  
  46.             if(th[i] != null)  
  47.                 System.out.println(th[i].getName());  
  48.             else System.out.println("线程组中木有线程!");  
  49.         }  
  50.     }  
  51. }  

运行截图:




线程组操作示例:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. package com.jay.example;  
  2.   
  3. /* 
  4.  * 该代码演示的是通过线程组来管理线程组的优先级 
  5.  * 出了下面的方法还有: 
  6.  * getParent():返回本线程组的父线程组 
  7.  * parentOf(ThreadGroup g):判断本线程组是否指定线程组的上层线程组 
  8.  * isDaemon():设置或判断线程组是否为一个守护线程组 
  9.  * */  
  10.   
  11. public class ThreadGroupDemo2 {  
  12.     public static void main(String[] args) {  
  13.         //①新建一个线程组对象  
  14.         ThreadGroup tg = new ThreadGroup("自定义线程组");  
  15.         //②定义两个线程对象放到线程组中  
  16.         Thread t1 = new Thread(tg,"线程一");  
  17.         Thread t2 = new Thread(tg,"线程二");  
  18.         //③获得线程中初始最大优先级,所有线程不能高过这个级别  
  19.         //除了设置之前已经存在的线程以外  
  20.         System.out.println("线程组的出事最大优先级:" + tg.getMaxPriority());  
  21.         //获得两个线程的优先级  
  22.         System.out.println(t1.getName()+"的初始优先级"+t1.getPriority());  
  23.         System.out.println(t2.getName()+"的初始优先级"+t1.getPriority());  
  24.         //将一号线程优先级设置为9  
  25.         t1.setPriority(9);  
  26.         //④设置线程组的最高优先级为8  
  27.         tg.setMaxPriority(8);  
  28.         //输出设置后的线程组最高优先级,同时输出一号线程的优先级  
  29.         System.out.println("线程组的新优先级:" + tg.getMaxPriority());  
  30.         System.out.println("一号线程的新优先级:" + t1.getPriority());  
  31.         //⑤我们将线程组优先级该成了8,如果我们此时为2号线程优先级设置为10,结果如何?  
  32.         t2.setPriority(10);  
  33.         System.out.println("二号线程的新优先级:" + t2.getPriority());  
  34.         //返回本线程组的字符串描述  
  35.         System.out.println(tg.toString());  
  36.           
  37.     }  
  38. }  

运行截图:




生产者与消费者的问题:


问题解析:






代码解决生产者消费者问题的流程:




代码示例:

产品类:Producer.java

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. package com.jay.example;  
  2.   
  3. public class Products {  
  4.       
  5.     private int productID = 0;  
  6.   
  7.     public Products(int productID) {  
  8.         super();  
  9.         this.productID = productID;  
  10.     }  
  11.   
  12.     public int getProductID() {  
  13.         return productID;  
  14.     }  
  15.   
  16.     public void setProductID(int productID) {  
  17.         this.productID = productID;  
  18.     }  
  19.       
  20.     @Override  
  21.     public String toString() {  
  22.         //通过""连接将其转化为字符串  
  23.         return "" + productID;  
  24.     }  
  25. }  


仓库类:WareHouse.java

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. package com.jay.example;  
  2.   
  3. public class WareHouse {  
  4.       
  5.     private int base = 0;  
  6.     private int top = 0;  
  7.       
  8.     //设置仓库的容量为10  
  9.     private Products[] products = new Products[10];  
  10.       
  11.       
  12.     //定义生产产品的方法,因为线程同步的问题,需要将方法设置为同步方法,即  
  13.     //为其设置同步锁     
  14.     public synchronized void produce(Products product)  
  15.     {  
  16.         //判断仓库是否已经满了,如果满了的话,先让生产者歇一歇  
  17.         //之所以把notify写到外面是为了唤醒消费线程;  
  18.         //因为开始生产了,那么消费者也可以开始消费了  
  19.         notify();  
  20.         while(top == products.length)  
  21.         {  
  22.               
  23.             try  
  24.             {  
  25.                 System.out.println("仓库已经满了,等待消费者消费...");  
  26.                 wait();  
  27.             }catch(InterruptedException e){e.printStackTrace();}  
  28.         }  
  29.         //如果仓库没满的话,那么继续将生产好的商品添加到仓库中,仓库产品数量+1  
  30.         products[top] = product;  
  31.         top++;  
  32.     }  
  33.       
  34.     //定义消费产品的方法,同理,需要为其设置同步锁  
  35.     public synchronized Products consume()  
  36.     {  
  37.         Products product = null;  
  38.         //判断仓库是否为空,空的话,消费者等一等,把生产者叫醒  
  39.         while(top == base)  
  40.         {  
  41.             notify();  
  42.             try{  
  43.                 System.out.println("仓库中木有商品了,等待生产者生产...");  
  44.                 wait();  
  45.             }catch(InterruptedException ex){ex.printStackTrace();}  
  46.         }  
  47.         //仓库没空,继续消费,消费一个产品,仓库产品数量-1  
  48.         top--;  
  49.         product = products[top];  
  50.         products[top] = null;  
  51.         return product;  
  52.     }  
  53.       
  54. }  


生产者线程:Producer.java


[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. package com.jay.example;  
  2.   
  3. public class Producer implements Runnable{  
  4.   
  5.     private String produceName;  
  6.     private WareHouse wareHouse;  
  7.           
  8.     public Producer(String produceName, WareHouse wareHouse) {  
  9.         super();  
  10.         this.produceName = produceName;  
  11.         this.wareHouse = wareHouse;  
  12.     }  
  13.       
  14.   
  15.     public String getProduceName() {  
  16.         return produceName;  
  17.     }  
  18.   
  19.   
  20.     public void setProduceName(String produceName) {  
  21.         this.produceName = produceName;  
  22.     }  
  23.       
  24.       
  25.     @Override  
  26.     public void run() {  
  27.         int i = 0;  
  28.         int j = 0;  
  29.         while(j <= 100)  
  30.         {  
  31.             j++;  
  32.             i++;  
  33.             Products products = new Products(i);  
  34.             wareHouse.produce(products);  
  35.             System.out.println(getProduceName() + " 生产了  " + products);  
  36.             try  
  37.             {  
  38.                 Thread.sleep(200);  
  39.             }catch(InterruptedException ex){ex.printStackTrace();}  
  40.               
  41.         }  
  42.     }  
  43.       
  44. }  


消费者进程:Consumer.java


[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. package com.jay.example;  
  2.   
  3. public class Consumer implements Runnable{  
  4.   
  5.     private String consumerName  = null;  
  6.     private WareHouse wareHouse = null;  
  7.       
  8.       
  9.       
  10.     public Consumer(String consumerName, WareHouse wareHouse) {  
  11.         super();  
  12.         this.consumerName = consumerName;  
  13.         this.wareHouse = wareHouse;  
  14.     }  
  15.   
  16.   
  17.     public String getConsumerName() {  
  18.         return consumerName;  
  19.     }  
  20.   
  21.     public void setConsumerName(String consumerName) {  
  22.         this.consumerName = consumerName;  
  23.     }  
  24.   
  25.   
  26.   
  27.     @Override  
  28.     public void run() {  
  29.         int j = 0;  
  30.         while(j < 100)  
  31.         {  
  32.             j++;  
  33.             System.out.println(getConsumerName() + " 消费了 " + wareHouse.consume());  
  34.             try{  
  35.                 Thread.sleep(300);  
  36.             }catch(InterruptedException ex){ex.printStackTrace();}  
  37.               
  38.         }  
  39.           
  40.     }  
  41.           
  42. }  


测试类:ThreadTest.java

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. package com.jay.example;  
  2.   
  3. public class ThreadTest {  
  4.     public static void main(String[] args) {  
  5.         WareHouse wh = new WareHouse();  
  6.         Producer pd = new Producer("生产者", wh);  
  7.         Consumer cs = new Consumer("消费者", wh);  
  8.         Thread t1 = new Thread(pd);  
  9.         Thread t2 = new Thread(cs);  
  10.         t1.start();  
  11.         t2.start();  
  12.           
  13.     }  
  14. }  



部分运行截图:因为太长,读者可以自己把代码运行一次,就知道了



ps:如果是多个生产者和消费者就稍微复杂点,这个时候可能就需要用notifyAll方法了!



线程池:


线程池的相关概念:



使用Executors生成简单的线程池




代码实例:生成四中线程池的简单例子:


自定义的一个线程类:MyThread.java


[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. package com.jay.example;  
  2.   
  3. public class MyThread extends Thread  
  4. {  
  5.     private String name;  
  6.     public MyThread(String name) {  
  7.         super(name);  
  8.         this.name = name;  
  9.     }  
  10.       
  11.       
  12.       
  13.     @Override  
  14.     public void run() {  
  15.         System.out.println(Thread.currentThread().getName() + "\t" + name +"开始执行...");  
  16.         System.out.println(name + "结束运行!");  
  17.     }  
  18.       
  19. }  


实现前面的三个生成线程池方法:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. package com.jay.example;  
  2.   
  3. import java.util.concurrent.ExecutorService;  
  4. import java.util.concurrent.Executors;  
  5.   
  6. /* 
  7.  * 本程序演示的是使用Executors生成简单的线程池 
  8.  * 调用它的三个方法进行演示 
  9.  * newSingleThreadExecutor:生成单线程池 
  10.  * newFixedThreadPool:生成固定大小的线程池 
  11.  * newCachedThreadPool:生成可缓存的线程池 
  12.  *  
  13.  * 只需要把注释依次撤销就可以看到效果了! 
  14.  * */  
  15.   
  16.   
  17.   
  18. public class SingleThreadExecutorTest {  
  19.     public static void main(String[] args) {  
  20.         ExecutorService pool = Executors.newSingleThreadExecutor();  
  21. //      ExecutorService pool = Executors.newFixedThreadPool(3);  
  22. //      ExecutorService pool = Executors.newCachedThreadPool();  
  23.         MyThread mt1 = new MyThread("线程一");  
  24.         MyThread mt2 = new MyThread("线程二");  
  25.         MyThread mt3 = new MyThread("线程三");  
  26.         MyThread mt4 = new MyThread("线程四");  
  27.         MyThread mt5 = new MyThread("线程五");  
  28.         //将线程放到线程池中  
  29.         pool.execute(mt1);  
  30.         pool.execute(mt2);  
  31.         pool.execute(mt3);  
  32.         pool.execute(mt4);  
  33.         pool.execute(mt5);  
  34.         //关闭线程池  
  35.         pool.shutdown();  
  36.     }  
  37. }  

运行截图:




第四个生成线程池方法的演示:

ScheduledThreadThreadPool.java


[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. package com.jay.example;  
  2.   
  3. import java.util.concurrent.Executors;  
  4. import java.util.concurrent.ScheduledExecutorService;  
  5. import java.util.concurrent.TimeUnit;  
  6.   
  7. //该代码演示的是第四个静态方法:newScheduledThreadPool();生成大小无限  
  8. //可执行定时周期性操作的线程池,代码中设置了该线程池容量为3,一,四号线程  
  9. //延迟10ms再  
  10.   
  11. public class ScheduledThreadThreadPool {  
  12.     public static void main(String[] args) {  
  13.         ScheduledExecutorService pool = Executors.newScheduledThreadPool(3);  
  14.         MyThread mt1 = new MyThread("线程一");  
  15.         MyThread mt2 = new MyThread("线程二");  
  16.         MyThread mt3 = new MyThread("线程三");  
  17.         MyThread mt4 = new MyThread("线程四");  
  18.         MyThread mt5 = new MyThread("线程五");  
  19.   
  20.         pool.execute(mt3);  
  21.         pool.execute(mt2);  
  22.         pool.execute(mt5);  
  23.   
  24.         //使用延时执行的方法,让后面两线程10ms以后才执行  
  25.         pool.schedule(mt1, 10, TimeUnit.MILLISECONDS);  
  26.         pool.schedule(mt4, 10, TimeUnit.MILLISECONDS);  
  27.     }  
  28.       
  29.   
  30. }  

运行截图:





自定义一个简单的线程池流程





最简单的自定义线程池演示:

三个文件:线程池类 + 线程  + 测试类


MyThread.java

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. package com.jay.example;  
  2.   
  3. public class MyThread implements Runnable{  
  4.   
  5.     //设置哨岗值  
  6.     private boolean flag;  
  7.     private String str;  
  8.     private String index;  
  9.       
  10.     //在构造方法中完成初始化  
  11.     public MyThread(String index) {  
  12.         flag = false;  
  13.         this.index = index;  
  14.         System.out.println(index + "开始启动...");  
  15.     }  
  16.       
  17.     //因为线程池中所有的线程都是一个线程类,需要对run方法加锁  
  18.     @Override  
  19.     public synchronized void run() {  
  20.         while(true)  
  21.         {  
  22.             if(!isFlag())  
  23.             {  
  24.                 try{  
  25.                     wait();  
  26.                 }catch(InterruptedException ex){ex.printStackTrace();}  
  27.             }  
  28.             else  
  29.             {  
  30.                 System.out.println(index + "正在运行" + getStr());  
  31.                 try {  
  32.                     Thread.sleep(3000);  
  33.                 } catch (InterruptedException e) {e.printStackTrace();}  
  34.             }  
  35.         }  
  36.           
  37.     }  
  38.   
  39.     //通过改变flag的值,唤醒线程  
  40.     public synchronized void setFlag(boolean flag)  
  41.     {  
  42.         this.flag = flag;  
  43.         if(flag)this.notify();  
  44.     }  
  45.       
  46.     public boolean isFlag()  
  47.     {  
  48.         return flag;  
  49.     }  
  50.       
  51.     public String getStr() {  
  52.         return str;  
  53.     }  
  54.   
  55.     public void setStr(String str) {  
  56.         this.str = str;  
  57.     }  
  58.           
  59.       
  60. }  


线程池类: ThreadPoolManager,java

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. package com.jay.example;  
  2.   
  3. import java.util.ArrayList;  
  4.   
  5. public class ThreadPoolManager {  
  6.     private int num;  
  7.     //定义一个集合用于存放线程池中的线程对象  
  8.     private ArrayList<MyThread> alist;  
  9.       
  10.     //初始化线程池集合,同时启动线程  
  11.       
  12.     public ThreadPoolManager(int num) {  
  13.         this.num = num;  
  14.         alist = new ArrayList<MyThread>(num);  
  15.         //通过for循环初始化集合中的线程  
  16.         for(int i = 0; i < num;i++)  
  17.         {  
  18.             MyThread mt = new MyThread("线程" + (i+1));  
  19.             alist.add(mt);  
  20.             Thread t = new Thread(mt);  
  21.             try  
  22.             {  
  23.                 Thread.sleep(1000);  
  24.             }catch(InterruptedException ex){ex.printStackTrace();}  
  25.               
  26.             t.start();  
  27.         }  
  28.     }  
  29.       
  30.     //如果改变了条件则唤醒线程  
  31.     public void notifyThread(String str)  
  32.     {  
  33.         int i;  
  34.         for(i = 1; i < alist.size() + 1;i++)  
  35.         {  
  36.             MyThread mt = alist.get(i);  
  37.             if(!mt.isFlag())  
  38.             {  
  39.                 System.out.println("线程" + (i + 1) + "正在唤醒" + str);  
  40.                 mt.setFlag(true);  
  41.                 mt.setStr(str);  
  42.                 return;  
  43.             }  
  44.         }  
  45.         if(i == alist.size() + 1)System.out.println("线程池中的所有线程已经启动...");  
  46.     }  
  47.       
  48.       
  49. }  


测试类:ThreadPoolTest.java

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. package com.jay.example;  
  2.   
  3. import java.io.BufferedReader;  
  4. import java.io.InputStreamReader;  
  5.   
  6. /* 
  7.  * 因为线程池已经启动了所有线程,我故意通过一个输入事件 
  8.  * 将所有的线程处于堵塞状态,然后没输入一次按回车 
  9.  * 将会随机的激活线程池中的一个线程 
  10.  *  
  11.  * */  
  12.   
  13. public class ThreadPoolTest {  
  14.     public static void main(String[] args) {  
  15.         ThreadPoolManager tpm = new ThreadPoolManager(10);  
  16.         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));  
  17.         try  
  18.         {  
  19.             String str = "";  
  20.             while((str = br.readLine())!= null)  
  21.             {  
  22.                 System.out.println("===========================");  
  23.                 tpm.notifyThread(str);  
  24.             }  
  25.         }catch(Exception e){e.printStackTrace();}  
  26.     }  
  27. }  


运行截图:

具体的,读者把代码复制下运行就可以知道大概效果了!





总结:

多线程第二节足足花了我一天多的时间去深入理解,这一节的有点难理解,

进程的堵塞倒没什么,是一些方法的了解而已,守护线程也是;

线程组是线程池的雏形;

经典线程同步问题:消费者与生产者问题,这是多线程的一个难点,需要花比较多的时间理解

还有线程池,这个真心纠结,查阅大量资料,才可以说基本掌握(ps:网上的都很乱..),线程池在web方面用处比较大;

这个在这里能够大概能掌握基本的使用就可以了,在后面的web博文中会再深入讲解线程池这个东东!



ps:这两天看线程看到相死的心都有了,最纠结的莫过于线程池,不过现在已经有个大概的轮廓了;

等到深入web部分的时候在深入研究这个东东吧,线程池对性能的优化很明显的说!

好了,这一节就到这里,如果有什么错误,纰漏,疑问,或者好的建议,欢迎读者指出

由衷感激!O(∩_∩)O谢谢~



0 0