日期:2014-05-20  浏览次数:20698 次

J2SE知识点归纳笔记(五)---Java多线程(二)

Java多线程

前言:

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

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

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

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

的更多知识!


线程堵塞:


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




守护线程:




线程组的使用

介绍:


在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方法了!



线程池:


线程池的相关概念:



使用Executors生成简单的线程池




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


自定义的一个线程类: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谢谢~



1楼u014450015昨天 18:31
一如既往的详细啊,赞一个,博主下次别在这个频道发了,都是手机开发的,去编程语言那里发吧,看的人多一点的
Re: zpj779878443昨天 20:56
回复u014450015n恩,好的,今天发表的时候没留意,直接按的,记着了,谢谢啊!(*^__^*) 嘻嘻……