您的位置:首页 > 编程语言 > Java开发

Java多线程(二)

2016-01-15 10:16 411 查看

Java多线程

前言:

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

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

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

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

的更多知识!

线程堵塞:

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



守护线程:



线程组的使用

介绍:

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

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

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

作用:

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

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

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

线程组提供的操作:

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

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

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

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

代码使用实例:

[java]
view plaincopyprint?





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("线程组中木有线程!");
}
}
}

运行截图:



线程组操作示例:

[java]
view plaincopyprint?





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

[java]
view plaincopyprint?





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

[java]
view plaincopyprint?





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

[java]
view plaincopyprint?





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

[java]
view plaincopyprint?





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

[java]
view plaincopyprint?





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

[java]
view plaincopyprint?





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 + "结束运行!");
}

}

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

[java]
view plaincopyprint?





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

[java]
view plaincopyprint?





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

[java]
view plaincopyprint?





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

[java]
view plaincopyprint?





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

[java]
view plaincopyprint?





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谢谢~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: