nullnull第二十一章 并发★ 线程
★ 创建线程的方式null四、 从任务中返回值★ Runnable接口是执行工作的独立任务,
但是它不返回任何值
◆ 如果你希望在完成时能够返回一个值,
那么应该实现Callable接口null★ 背景知识:
◆ Future接口
◆ Callable接口
◆ ExecutorService接口中的submit()方法null五、 休眠★ 影响任务行为的一种简单方法是:
◆ 调用sleep()方法
◆ 这将使任务中止执行给定的时间null1、典例:P659~670
import java.util.concurrent.*;
public class SleepingTask extends LiftOff
{
public void run()
{ nulltry
{
while(countDown-- > 0)
{ System.out.println(status());
TimeUnit.MILLISECONDS.sleep(100);
}
} catch(InterruptedException e)
{ System.err.println(“Interrupted”);} } null★ 分析:
⑴ 对Sleep的调用,将会导致InterruptedException
异常的抛出
◆ 该异常在run()中被捕获
◆ 异常不能跨线程传播回主线程main(),所以
你必须在本地处理所有在任务内部产生的异常null★ 分析:
⑵ sleep的两种版本
◆ 旧版本:Thread.sleep(100);
◆ 新版本:作为TimeUnit类的一部分nullpublic static void main(String[] args)
{
ExecutorService exec =
Executors.newCachedThreadPool();
for(int i = 0;i < 5;i++)
exec.execute(new SleepingTask());
exec.shutdown(); } } nullnull2、归纳
★ 你可能注意到:这些任务是按照完美的分布而
顺利执行的
◆ 顺序行为取决于底层的线程机制,而这种机制
在不同的操作系统之间是有区别的
◆ 精确控制的方法:使用同步null六、 优先级★ 线程的优先级将该线程的重要性传递
给线程调度器
◆ 调度器倾向于让优先级最高的线程先
执行,优先级低的线程后执行null1、主要方法
★ getPriority()方法
◆ 读取现有线程的优先级
★ setPriority()方法
◆ 设置和修改线程的优先级null2、主要属性
★ 优先级分为三个等级:
◆ MAX_PRIORITY
◆ NORM_PRIORITY
◆ MIN_PRIORITY null七、 让步★ 让步
◆ 通过yield()方法完成null1、让步的含义
★ 如果在run()方法中,已经完成了完整的一次
循环和迭代过程
◆ 就可以给线程调度器机制一个暗示: 你的工作
已做得差不多了, 可以让别的线程使用CPU了null2、让步的实质
★ 这仅仅只是一个暗示而已, 没有任何机制保证
它将会被采纳
◆ 当调用yield()方法的时候,你仅仅只是建议:
具有相同优先级的其它线程可以运行null八、 后台线程1、 后台线程(daemon)
★ daemon:守护神
◆ 所以daemon也常常称为守护线程null1、守护线程的概念和作用
★ 守护线程在Java中属于比较特殊的一种线程,
它具有最低的优先级
◆ 作用:为系统中的其它对象和线程提供服务
◆ 注意:如果当前运行的线程都是守护线程时,
Java虚拟机将会退出null2、设置守护线程的方法
★ 声明:public final void
setDaemon(boolean on)
◆ 功能:将该线程标记为守护线程
◆ 注意:该方法必须在启动线程前调用null3、守护线程的典例
★ Java垃圾回收线程:就是一个典型的守护线程
◆ 它始终在低级别的状态中运行,用于实时监控
和管理系统中的可回收资源
◆ 当垃圾回收线程是Java虚拟机上仅剩的线程时,
Java虚拟机会自动离开null八、 后台线程2、 后台线程的典例
★ 详见P662nullimport java.util.concurrent.*;
public class SimpleDaemons
implements Runnable
{
public void run()
{
◆ 提供run()方法的实现nulltry
{
while(true)
{
TimeUnit.MILLISECONDS.sleep(100);
print(Thread.currentThread()+this);
}
} catch(InterruptedException e)
{ print(“sleep() interrupted”); } }nullpublic static void main(String[] args)
throws Exception
{
for(int i = 0;i < 10;i++)
{
Thread daemon = new
Thread( new SimpleDaemons());
◆ 利用实现了Runnable的类来创建一个线程null daemon.setDaemon(true);
daemon.start();
}
print(“All daemons started”);
TimeUnit.MILLISECONDS.sleep(175);} }
◆ 首先:设置为后台线程
◆ 然后:启动线程nullnull★ 归纳:
◆ main()线程被设定为短暂休眠
◆ 一旦main()完成工作,就没有什么能阻止程序
终止了,因为除了后台线程之外,已经没有线程
在运行了null九、 编码的变体★ 到目前为止,在你所看到的示例当中,
任务类都实现了Runnable接口
◆ 在简单的情况下,你可能会希望直接
使用从Thread类继承这种可替换方式null★ 典例:P665~666
public class SimpleThread extends Thread
{
private int countDown = 5; // 计数器
private static int threadCount = 0; // 流水号
◆ 静态数据成员:用来标识每个线程nullpublic SimpleThread()
{
super(Integer.toString(++threadCount));
start();
}
◆ super
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
示调用父类构造器
◆ 即调用Thread的构造器,注意其提供的参数nullpublic String toString()
{
return “#” + getName()
+“(”+countDown+“),”;
}
◆ 当需要一个字符串,却提供了一个对象时
◆ 系统将自动调用toString()方法nullpublic void run()
{
while(true)
{ System.out.println(this);
if( --countDown == 0)
return;} }
◆ 继承Thread的子类,必须重写run()方法nullpublic static void main(String[] args)
{
for(int i = 0;i < 5;i++)
new SimpleThread();
}}
◆ 测试函数
◆ 运行结果参见P666null十、 术语★ 有了上面的知识作为基础,下面可以
回过头来讨论一些令人混淆的概念
◆ 概念的区分:线程与任务null1、线程与任务的区别
⑴ 到目前为止,你应该看到要执行的任务与驱动
它的线程之间有一个差异
★ 这个差异在Java类库中尤为明显
◆ 因为你对Thread类没有任何的控制权null⑵ 由于Executor执行器将会替你处理线程的创建
和管理
★ 这将使得,线程与任务之间的差异更加明显
⑶ 归纳:
★ 首先创建任务,并且通过某种方式将一个任务
附着在线程上,以使得这个线程可以驱动任务null2、令人混淆之处
⑴ 在Java中,Thread类自身并不执行任何的操作
★ 它只是驱动它的任务
◆ 但令人郁闷的是,在线程的研究过程中,总是
喜欢使用线程执行这项动作或者线程执行那项
动作这样的语言null⑵ 因此,你得到的印象就是线程就是任务
★ 当第一次碰见Java线程时,这种印象非常强烈
◆ 似乎很明显,我应该从Thread类继承一个任务
◆ 然而,这种印象是错误的印象null⑶ 此外,Runnable接口的名字也选得很糟糕
★ 从实现了Runnable接口的类,它所具有功能和
所扮演的角色来看:
◆ Task应该是个好得多的名字null⑷ 最后,Java的线程机制
★ 从C语言的低级P线程方式而来
◆ 这种低级特性决定了:
◆ 你必须深入研究该机制,并且必须完全理解其
所有事物的所有细节null十一、 创建有响应的用户界面★ 使用线程的动机之一:
◆ 就是建立有响应的用户界面null1、基于控制太界面的简单教学示例
★ 下面的示例有两个版本:
◆ UnresponsiveUI:无响应用户界面
◆ ResponsiveUI: 有响应用户界面null2、版本之一:无响应用户界面
class UnresponsiveUI
{
private volatile double d = 1;
public UnresponsiveUI()
throws Exception
{ nullwhile(d > 0)
d = d + (Math.PI + Math.E) / d;
System.in.read(); // Never gets here
} }
◆ 可能由于:一直关注于运算
◆ 从而影响:从控制台读取输入null3、版本之二:有响应用户界面
public class ResponsiveUI
extends Thread
{
private static volatile double d = 1;nullpublic ResponsiveUI()
{
setDaemon(true);
start();
}
◆ 设置为优先级别最低的后台线程,并且启动它
◆ 这也意味着:只有CPU空闲时,才会进行计算nullpublic void run()
{
while(true)
{
d = d + (Math.PI + Math.E) / d;
}
}
◆ 将运算放在任务里单独运行null4、测试程序
public static void main(String[] args)
throws Exception
{
//! new UnresponsiveUI();
◆ 陷入无限循环,必须杀死进程才能终止nullnew ResponsiveUI();
System.in.read();
System.out.println(d);} }
◆ 只有这样才能保证:
◆ 在运算的同时,监听控制台输入