日期:2014-05-20 浏览次数:20698 次
在上一节中,我们已经对Java的多线程进行了初步的了解;
在这一节中,我们将继续深入地探讨Java多线程的更多知识点!
线程堵塞,守护线程,线程组,经典线程同步问题:消费者与生产者问题
还有是很难的线程池,那么请读者跟随笔者的脚步,进一步剖析多线程
的更多知识!
在Java中,我们可以通过java.lang.ThreadGroup对线程进行组操作;每一个线程都归属于某个线程组管理的一员,如在main()工作流程
产生的一个都线程,则产生的线程属于main这个线程组管理的一员;在创建Thread实例时,如果没有指定线程组参数,则默认隶属于创建者
线程所隶属的线程组;这种隶属关系在创建新线程时指定,在线程的整个生命周期里都不能够改变
简化对多个线程的管理,对若干线程同时操作,比如:调用线程组的方法设置所有线程的优先级,调用线程组的方法启动或堵塞组中所有的
线程等;其实线程组最重要的意义是安全,Java默认创建的线程都是属于系统线程组,而处于同一线程组的线程时可以相互修改对方数据的;
当如果在不同的线程组中,那么就不能"跨线程组"修改数据,从一定程度上保证了数据的安全
①集合管理方法,用于管理包含在线程组中的线程与子线程组
②组操作方法:设置或者获取线程对象的属性
③组中所有线程的操作方法,针对组中所有线程与子线程执行某一操作,eg:线程启动,恢复
④访问限制方法基于线程组的成员关系
package com.jay.example; /* * 该代码演示的是线程组的集合管理,一些对线程组中线程与子线程组的 * 一些基本操作,这里故意弄了一个自定义线程增加效果; * 输出结果每次都不一样,这个是因为多线程执行的随机性哦! * * */ class MyThread extends Thread { MyThread() { super("呵呵"); } @Override public void run() { for(int i = 0;i < 10;i++) { System.out.println(i); } } } public class ThreadGroupDemo { public static void main(String[] args) { //故意添加一个线程演示效果 MyThread mt = new MyThread(); mt.start(); //①获得当前线程的线程组对象 ThreadGroup tg = Thread.currentThread().getThreadGroup(); //②打印线程组的名称 System.out.println(tg.getName()); //③获得线程组中活动线程的数目 int count = tg.activeCount(); System.out.println("当前线程组中有"+count+"个活动线程!"); //④将当前所有活动线程放到一个线程数组中 Thread[] th = new Thread[count]; tg.enumerate(th); //⑤输出线程数组中所有线程的名字: for(int i = 0;i < count;i++) { System.out.print("线程#"+(i + 1) + "="); if(th[i] != null) System.out.println(th[i].getName()); else System.out.println("线程组中木有线程!"); } } }
线程组操作示例:
package com.jay.example; /* * 该代码演示的是通过线程组来管理线程组的优先级 * 出了下面的方法还有: * getParent():返回本线程组的父线程组 * parentOf(ThreadGroup g):判断本线程组是否指定线程组的上层线程组 * isDaemon():设置或判断线程组是否为一个守护线程组 * */ public class ThreadGroupDemo2 { public static void main(String[] args) { //①新建一个线程组对象 ThreadGroup tg = new ThreadGroup("自定义线程组"); //②定义两个线程对象放到线程组中 Thread t1 = new Thread(tg,"线程一"); Thread t2 = new Thread(tg,"线程二"); //③获得线程中初始最大优先级,所有线程不能高过这个级别 //除了设置之前已经存在的线程以外 System.out.println("线程组的出事最大优先级:" + tg.getMaxPriority()); //获得两个线程的优先级 System.out.println(t1.getName()+"的初始优先级"+t1.getPriority()); System.out.println(t2.getName()+"的初始优先级"+t1.getPriority()); //将一号线程优先级设置为9 t1.setPriority(9); //④设置线程组的最高优先级为8 tg.setMaxPriority(8); //输出设置后的线程组最高优先级,同时输出一号线程的优先级 System.out.println("线程组的新优先级:" + tg.getMaxPriority()); System.out.println("一号线程的新优先级:" + t1.getPriority()); //⑤我们将线程组优先级该成了8,如果我们此时为2号线程优先级设置为10,结果如何? t2.setPriority(10); System.out.println("二号线程的新优先级:" + t2.getPriority()); //返回本线程组的字符串描述 System.out.println(tg.toString()); } }
代码示例:
产品类:Producer.java
package com.jay.example; public class Products { private int productID = 0; public Products(int productID) { super(); this.productID = productID; } public int getProductID() { return productID; } public void setProductID(int productID) { this.productID = productID; } @Override public String toString() { //通过""连接将其转化为字符串 return "" + productID; } }
仓库类:WareHouse.java
package com.jay.example; public class WareHouse { private int base = 0; private int top = 0; //设置仓库的容量为10 private Products[] products = new Products[10]; //定义生产产品的方法,因为线程同步的问题,需要将方法设置为同步方法,即 //为其设置同步锁 public synchronized void produce(Products product) { //判断仓库是否已经满了,如果满了的话,先让生产者歇一歇 //之所以把notify写到外面是为了唤醒消费线程; //因为开始生产了,那么消费者也可以开始消费了 notify(); while(top == products.length) { try { System.out.println("仓库已经满了,等待消费者消费..."); wait(); }catch(InterruptedException e){e.printStackTrace();} } //如果仓库没满的话,那么继续将生产好的商品添加到仓库中,仓库产品数量+1 products[top] = product; top++; } //定义消费产品的方法,同理,需要为其设置同步锁 public synchronized Products consume() { Products product = null; //判断仓库是否为空,空的话,消费者等一等,把生产者叫醒 while(top == base) { notify(); try{ System.out.println("仓库中木有商品了,等待生产者生产..."); wait(); }catch(InterruptedException ex){ex.printStackTrace();} } //仓库没空,继续消费,消费一个产品,仓库产品数量-1 top--; product = products[top]; products[top] = null; return product; } }
生产者线程:Producer.java
package com.jay.example; public class Producer implements Runnable{ private String produceName; private WareHouse wareHouse; public Producer(String produceName, WareHouse wareHouse) { super(); this.produceName = produceName; this.wareHouse = wareHouse; } public String getProduceName() { return produceName; } public void setProduceName(String produceName) { this.produceName = produceName; } @Override public void run() { int i = 0; int j = 0; while(j <= 100) { j++; i++; Products products = new Products(i); wareHouse.produce(products); System.out.println(getProduceName() + " 生产了 " + products); try { Thread.sleep(200); }catch(InterruptedException ex){ex.printStackTrace();} } } }
消费者进程:Consumer.java
package com.jay.example; public class Consumer implements Runnable{ private String consumerName = null; private WareHouse wareHouse = null; public Consumer(String consumerName, WareHouse wareHouse) { super(); this.consumerName = consumerName; this.wareHouse = wareHouse; } public String getConsumerName() { return consumerName; } public void setConsumerName(String consumerName) { this.consumerName = consumerName; } @Override public void run() { int j = 0; while(j < 100) { j++; System.out.println(getConsumerName() + " 消费了 " + wareHouse.consume()); try{ Thread.sleep(300); }catch(InterruptedException ex){ex.printStackTrace();} } } }
测试类:ThreadTest.java
package com.jay.example; public class ThreadTest { public static void main(String[] args) { WareHouse wh = new WareHouse(); Producer pd = new Producer("生产者", wh); Consumer cs = new Consumer("消费者", wh); Thread t1 = new Thread(pd); Thread t2 = new Thread(cs); t1.start(); t2.start(); } }
部分运行截图:因为太长,读者可以自己把代码运行一次,就知道了
ps:如果是多个生产者和消费者就稍微复杂点,这个时候可能就需要用notifyAll方法了!
自定义的一个线程类:MyThread.java
package com.jay.example; public class MyThread extends Thread { private String name; public MyThread(String name) { super(name); this.name = name; } @Override public void run() { System.out.println(Thread.currentThread().getName() + "\t" + name +"开始执行..."); System.out.println(name + "结束运行!"); } }
package com.jay.example; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /* * 本程序演示的是使用Executors生成简单的线程池 * 调用它的三个方法进行演示 * newSingleThreadExecutor:生成单线程池 * newFixedThreadPool:生成固定大小的线程池 * newCachedThreadPool:生成可缓存的线程池 * * 只需要把注释依次撤销就可以看到效果了! * */ public class SingleThreadExecutorTest { public static void main(String[] args) { ExecutorService pool = Executors.newSingleThreadExecutor(); // ExecutorService pool = Executors.newFixedThreadPool(3); // ExecutorService pool = Executors.newCachedThreadPool(); MyThread mt1 = new MyThread("线程一"); MyThread mt2 = new MyThread("线程二"); MyThread mt3 = new MyThread("线程三"); MyThread mt4 = new MyThread("线程四"); MyThread mt5 = new MyThread("线程五"); //将线程放到线程池中 pool.execute(mt1); pool.execute(mt2); pool.execute(mt3); pool.execute(mt4); pool.execute(mt5); //关闭线程池 pool.shutdown(); } }
ScheduledThreadThreadPool.java
package com.jay.example; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; //该代码演示的是第四个静态方法:newScheduledThreadPool();生成大小无限 //可执行定时周期性操作的线程池,代码中设置了该线程池容量为3,一,四号线程 //延迟10ms再 public class ScheduledThreadThreadPool { public static void main(String[] args) { ScheduledExecutorService pool = Executors.newScheduledThreadPool(3); MyThread mt1 = new MyThread("线程一"); MyThread mt2 = new MyThread("线程二"); MyThread mt3 = new MyThread("线程三"); MyThread mt4 = new MyThread("线程四"); MyThread mt5 = new MyThread("线程五"); pool.execute(mt3); pool.execute(mt2); pool.execute(mt5); //使用延时执行的方法,让后面两线程10ms以后才执行 pool.schedule(mt1, 10, TimeUnit.MILLISECONDS); pool.schedule(mt4, 10, TimeUnit.MILLISECONDS); } }
三个文件:线程池类 + 线程 + 测试类
MyThread.java
package com.jay.example; public class MyThread implements Runnable{ //设置哨岗值 private boolean flag; private String str; private String index; //在构造方法中完成初始化 public MyThread(String index) { flag = false; this.index = index; System.out.println(index + "开始启动..."); } //因为线程池中所有的线程都是一个线程类,需要对run方法加锁 @Override public synchronized void run() { while(true) { if(!isFlag()) { try{ wait(); }catch(InterruptedException ex){ex.printStackTrace();} } else { System.out.println(index + "正在运行" + getStr()); try { Thread.sleep(3000); } catch (InterruptedException e) {e.printStackTrace();} } } } //通过改变flag的值,唤醒线程 public synchronized void setFlag(boolean flag) { this.flag = flag; if(flag)this.notify(); } public boolean isFlag() { return flag; } public String getStr() { return str; } public void setStr(String str) { this.str = str; } }
线程池类: ThreadPoolManager,java
package com.jay.example; import java.util.ArrayList; public class ThreadPoolManager { private int num; //定义一个集合用于存放线程池中的线程对象 private ArrayList<MyThread> alist; //初始化线程池集合,同时启动线程 public ThreadPoolManager(int num) { this.num = num; alist = new ArrayList<MyThread>(num); //通过for循环初始化集合中的线程 for(int i = 0; i < num;i++) { MyThread mt = new MyThread("线程" + (i+1)); alist.add(mt); Thread t = new Thread(mt); try { Thread.sleep(1000); }catch(InterruptedException ex){ex.printStackTrace();} t.start(); } } //如果改变了条件则唤醒线程 public void notifyThread(String str) { int i; for(i = 1; i < alist.size() + 1;i++) { MyThread mt = alist.get(i); if(!mt.isFlag()) { System.out.println("线程" + (i + 1) + "正在唤醒" + str); mt.setFlag(true); mt.setStr(str); return; } } if(i == alist.size() + 1)System.out.println("线程池中的所有线程已经启动..."); } }
测试类:ThreadPoolTest.java
package com.jay.example; import java.io.BufferedReader; import java.io.InputStreamReader; /* * 因为线程池已经启动了所有线程,我故意通过一个输入事件 * 将所有的线程处于堵塞状态,然后没输入一次按回车 * 将会随机的激活线程池中的一个线程 * * */ public class ThreadPoolTest { public static void main(String[] args) { ThreadPoolManager tpm = new ThreadPoolManager(10); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); try { String str = ""; while((str = br.readLine())!= null) { System.out.println("==========================="); tpm.notifyThread(str); } }catch(Exception e){e.printStackTrace();} } }
运行截图:
具体的,读者把代码复制下运行就可以知道大概效果了!
多线程第二节足足花了我一天多的时间去深入理解,这一节的有点难理解,
进程的堵塞倒没什么,是一些方法的了解而已,守护线程也是;
线程组是线程池的雏形;
经典线程同步问题:消费者与生产者问题,这是多线程的一个难点,需要花比较多的时间理解
还有线程池,这个真心纠结,查阅大量资料,才可以说基本掌握(ps:网上的都很乱..),线程池在web方面用处比较大;
这个在这里能够大概能掌握基本的使用就可以了,在后面的web博文中会再深入讲解线程池这个东东!
ps:这两天看线程看到相死的心都有了,最纠结的莫过于线程池,不过现在已经有个大概的轮廓了;
等到深入web部分的时候在深入研究这个东东吧,线程池对性能的优化很明显的说!
好了,这一节就到这里,如果有什么错误,纰漏,疑问,或者好的建议,欢迎读者指出
由衷感激!O(∩_∩)O谢谢~