import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* 休眠
* 影响任务的行为的一种简单方法是调用sleep,这将使任务中止执行给定的时间。在LiftOff类中,要是把对yield的调用换成调用sleep
* 对sleep的调用可以抛出InterruptedException异常,并且你可以看到,它在run中被捕获,因为异常不能跨线程传播回main,所以你必须在本地处理所有的任务
* 内部产生的异常
*
* 每个任务都将要睡眠(阻塞),这使得线程调度器可以切换到另一个线程,进而驱动另一个任务。顺序行为依赖于底层的线程机制,这种机制在不同的操作系统之间是有差异的,
* 因此,你不能依赖于它,如果你必须控制任务执行的顺序,那么最好的就是使用同步控制,或者在某些情况下,压根不使用线程,但是要编写自己的协同例程,
* 这些例程将会按照指定的顺序在互相之间传递控制权。
*/
public class SleepingTask extends LiftOff{
public void run(){
try{
while (countDown-- > 0) {
System.out.println(status());
TimeUnit.MILLISECONDS.sleep(100);
}
} catch(InterruptedException e){
System.err.println("Interrupted");
}
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
executorService.execute(new SleepingTask());
}
executorService.shutdown();
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 优先级
* 线程的优先级将该线程的重要性传递给了调度器。尽管CPU处理现有线程集的顺序是不确定的,
* 但是调度器将倾向于让优先权最高的线程先执行。然而,这并不是意味着优先权较低的线程将得不到执行
* (优先权不会导致死锁).优先级较低的线程仅仅是执行的频率较低。
*
* 在绝大多少时间里,所有线程都应该以默认的优先级运行,试图操纵线程优先级通常是一种错误。
*
* 1.线程的可见性:当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。
* 2. 顺序一致性:禁止指令重排序。
*
* 在run里,执行了100000次开销相当大的浮点运算,包括double类型的加法与除法。
* 变量d是volatile的 以努力确保不进行任何编译器优化。如果没有加入这些运算的话,
* 就看不见设置优先级的效果,有了这些运算,就能观察到优先级为MAX_PRIORITY的线程
* 被线程调度器优先选择,尽管向控制台打印也是开销较大的操作,但在那种情况下看不见优先级的
* 效果,因为向控制台打印不能被中断,而数学运算是可以终端的,这里运算时间足够的长,因此线程调度
* 机制才来得及
* 介入,交换任务并关注优先级,使得最高优先级线程被有限选择。
* 尽管JDK有10个优先级,但它与多数操作系统都不能映射得很好,比如Window有7个优先级且不是固定的,所以这种映射关系也是不确定的,Sun的
* Solaris有2的31个优先级,唯一可移植的是当调整优先级的时候,只使用MAX_PRIORITY NORM_PRIORITY和MIN_PRIORITY三种级别
*/
public class SimplePriorities implements Runnable{
private int countDown = 5;
private volatile double d; //确保不进行任何编译器优化。
private int priority;
public SimplePriorities(int priority) {
this.priority = priority;
}
public String toString() {
// 打印线程的名称,线程的优先级以及线程所属的线程组。
// 你可以通过构造器来自己设置这个名称
return Thread.currentThread() +": " +countDown; //返回当前线程 Thread.currentThread()是Thread的一个静态方法 用于获取当前线程对象的一个引用
}
@Override
public void run() {
Thread.currentThread().setPriority(priority); //设置优先级
while (true) {
for (int i = 1; i < 100000; i++) {
d += (Math.PI+Math.E)/(double)i;
if (i%1000 == 0) {
Thread.yield();
}
System.out.println(this);
if (--countDown == 0) {
return;
}
}
}
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
executorService.execute(new SimplePriorities(Thread.MIN_PRIORITY));
}
executorService.execute(new SimplePriorities(Thread.MAX_PRIORITY));
executorService.shutdown();
}
}
让步
如果知道已经完成了在run方法的循环的一次迭代过程中所需的工作,就可以给线程调度机制一个暗示:你的工作已经注意的差不多了,可以让别的线程使用CPU了。这个暗示将通过调用yield方法来作出。当调用yield时,你也是在建议具有相同优先级的其他线程可以运行。
LiftOff.java使用yield在各种不同的LiftOff任务之间产生分布良好的处理机制。尝试着注释掉LiftOff.run中的Thread.yield,以查看区别。但是,大体上,对于任何重要的控制或在调整应用时,都不能依赖于yield。实际上,yield经常被误用。
后台线程
import sun.awt.windows.WPrinterJob;
import java.util.concurrent.TimeUnit;
import net.mindview.util.*;
/**
* 后台线程
* 所谓后台线程,是指在程序运行的时候再后台提供一种通用服务的线程,并且这种线程并不属于程序中
* 不可或缺的部分。因此,当所有的非后台线程结束时,程序也就终止了,同时会杀死进程中的所有后台线程。
* 反过来说,只要有任何非后台线程还在运行,程序就不会终止。比如,执行main就是一个非后台程序。
*
* 必须在线程启动之前调用setDaemon方法,才能设置它为后台线程。
* 一旦main完成其工作,就没什么能阻止程序终止了,因为除了后台程序之外,已经没有线程在运行了。
* main线程被设定为短暂睡眠,所以可以观察到所有后台线程启动后的结果不这样的话,你就只能看见
* 一些后台线程创建时得到的结果,
*/
public class SimpleDaemons implements Runnable{
@Override
public void run() {
try {
while (true) {
// 时间单位,表示千分之一秒
TimeUnit.MILLISECONDS.sleep(100);
System.out.println(Thread.currentThread() +" " +this);
}
} catch (InterruptedException e) {
System.out.println("sleep() interrupted");
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(new SimpleDaemons());
thread.setDaemon(true);//守护线程 确保主方法结束时候,子线程随之结束(默认是false
thread.start();
}
System.out.printf("All daemons started");
TimeUnit.MILLISECONDS.sleep(175);
}
}
SimpleDaemons创建了显式的线程,以便可以设置他们后台标志。通过编写定制的ThreadFactory可以定制Executor创建的线程的属性(后台,优先级,名称)
import java.util.concurrent.ThreadFactory;
/**
* 该接口的目的是定制一个线程,可以设置线程的优先级、名字、是否后台线程、状态等。
*/
public class DaemonThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setDaemon(true);
return thread;
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/*
这与普通的ThreadFactory的唯一差异就是它将后台状态全部设置为了true
你现在可以用一个新的DaemonThreadFactory作为参数传递给Executor.newCachedThreadFactoryPool
*/
public class DaemonFromFactory implements Runnable{
@Override
public void run() {
try {
TimeUnit.MILLISECONDS.sleep(100);
System.out.println(Thread.currentThread()+" " + this);
} catch (InterruptedException e) {
System.out.println("Interrupted");
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool(new DaemonThreadFactory());
for (int i = 0; i < 10; i++) {
executorService.execute(new DaemonFromFactory());
}
System.out.println("All daemons started");
TimeUnit.MILLISECONDS.sleep(500);
}
}
每个静态的ExecutorService创建方法都被重载为接受一个ThreadFactory对象将被用来创建新的线程
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 每个静态的ExecutorService创建方法都被重载为接受一个ThreadFactory对象,而这个对象将被用来创建新的线程
*/
public class DaemonTreadPoolExecutor extends ThreadPoolExecutor {
public DaemonTreadPoolExecutor() {
super(0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS,new SynchronousQueue<Runnable>(),new DaemonThreadFactory());
}
}
import java.util.concurrent.TimeUnit;
/**
* 可以通过isDaemon方法来确定线程是否是一个后台线程,
* 如果是一个后台线程,那么它创建的任何线程将被自动设置成后台线程
*
* Daemon线程被设置成了后台线程,然后派生出许多子线程,这些线程
* 并没有被显式地设置为后台模式,不过他们确是后台线程,接着Daemon线程
* 进入了无限循环,并在循环里调用yield方法把控制权交给其他进程。
*/
public class Daemons {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Daemon());
thread.setDaemon(true);
thread.start();
System.out.println("d.isDaemon = "+thread.isDaemon()+", ");
TimeUnit.SECONDS.sleep(1);
}
}
/**
*
*/
public class Daemon implements Runnable {
private Thread[] t = new Thread[10];
@Override
public void run() {
for (int i = 0; i < t.length; i++) {
t[i] = new Thread(new DaemonSpawn());
t[i].start();
System.out.println("DaemonSpawn "+ i + " started ");
}
for (int i = 0; i < t.length; i++) {
System.out.println("t["+i+"]. isDaemon() ="+t[i].isDaemon()+". ");
}
while (true)
Thread.yield();
}
}
public class DaemonSpawn implements Runnable {
@Override
public void run() {
while (true)
Thread.yield();
}
}