《Java编程思想》整理的一些学习笔记,有不对的地方,欢迎指出。
1.控制线程行为的方法——让步:如果知道run()方法已经完成了所需的工作,可以给线程调度机制一个暗示:你的工作已经做的差不多了,可以让别的线程使用CPU了,可以通过调用yield()方法来作出(不过这只是个暗示,没有任何机制保证它将会被采纳。)使用yield()以后,程序的输出会平衡很多,但是如果输出的字符串要再长一点的话,它还会是打破这种平衡,因为调用机制是抢占式的,如果输出字符串过长,占用时间较长,调用机制会在有机会调用yield()方法之前切到下一线程。
public class Demo extends Thread{
private int countDown = 5;
private static int threadCount = 0;
public Demo(){
super(""+ ++threadCount); // 调用Tread构造器,给线程对象指定一个名字
start();
}
public String toString(){
return "#"+getName()+":"+ countDown; // 使用getName()方法获取线程的名字
}
public void run(){
while(true){
System.out.println(this);
if(--countDown == 0)
return;
yield();
}
}
public static void main(String[] args){
for(int i = 0; i < 5; i++){
new Demo();
}
}
}
2.另一只能控制线程行为的方法是调用sleep(),这将使线程停止执行一段时间,该时间由给定的毫秒数决定。如果把上例中的yield()的调用换成调用sleep():
public class Demo extends Thread{
private int countDown = 5;
private static int threadCount = 0;
public Demo(){
super(""+ ++threadCount); // 调用Tread构造器,给线程对象指定一个名字
start();
}
public String toString(){
return "#"+getName()+":"+ countDown; // 使用getName()方法获取线程的名字
}
public void run(){
while(true){
System.out.println(this);
if(--countDown == 0)
return;
try{
sleep(100);
}catch(InterruptedException e){
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) throws InterruptedException{
for(int i = 0; i < 5; i++){
new Demo().join();
}
}
}
其输出结果为:
#1:5
#1:4
#1:3
#1:2
#1:1
#2:5
#2:4
#2:3
#2:2
#2:1
#3:5
#3:4
#3:3
#3:2
#3:1
#4:5
#4:4
#4:3
#4:2
#4:1
#5:5
#5:4
#5:3
#5:2
#5:1
在调用sleep()方法的时候,必须把它放在try块中,这是因为sleep()方法在休眠时间到期之前有可能被中断。如果某人持有对此线程的引用,并且在此线程上调用了interrupt()方法,就会发生这种情况。(如果对线程调用了wait()或join()方法,interrupt()方法也会对线程有影响,所以对这些方法的调用也要放在try块中)通常如果想使用interrupt()方法来中断一个挂起的线程,那么挂起的时候最好使用wait()而不是sleep(),这样就不太可能在catch子句里结束了。
上例中调用了join()方法,所以main()在继续执行之前会等待此线程结束在执行下一个,如果不调用Join()方法,则输出还是任意顺序,这说明sleep()也不是控制线程执行顺序的方法,它仅仅使线程停止一段时间。(如果必须要控制线程的执行顺序,最好是根本不用线程,而是自己编写以特定顺序彼此控制的协作子程序)。
3.线程的“优先权”能告诉调度程序该线程的重要性如何。尽管CPU处理现有的线程集的顺序是不确定的,但是如果有许多线程被阻塞并在等待运行,那么调度程序将倾向于让优先权最高的线程先执行。然而,这并不意味着优先权较低的线程将得不到执行(即优先权不会导致死锁)。优先级较低的线程仅仅是执行的频率较低。线程的优先权是通过使用setPriority()方法进行调整的。
public class Demo extends Thread{
private int countDown = 5;
// private static int threadCount = 0;
private volatile double d = 0;
public Demo(int priority){
// super(""+ ++threadCount); // 调用Tread构造器,给线程对象指定一个名字
setPriority(priority);
start();
}
public String toString(){
// return "#"+getName()+":"+ countDown; // 使用getName()方法获取线程的名字
return super.toString()+":"+ countDown;
}
public void run(){
while(true){
// System.out.println(this);
// if(--countDown == 0)
// return;
// try{
// sleep(100);
// }catch(InterruptedException e){
// throw new RuntimeException(e);
// }
for(int i = 1; i < 100000; i++){
d = d+(Math.PI+Math.E)/(double)i;
System.out.println(this);
if(--countDown == 0)
return;
}
}
}
public static void main(String[] args) throws InterruptedException{
new Demo(Thread.MAX_PRIORITY);
for(int i = 0; i < 5; i++){
new Demo(Thread.MIN_PRIORITY);
}
}
}
运行结果:
Thread[Thread-0,10,main]:5
Thread[Thread-0,10,main]:4
Thread[Thread-0,10,main]:3
Thread[Thread-0,10,main]:2
Thread[Thread-0,10,main]:1
Thread[Thread-1,1,main]:4
Thread[Thread-1,1,main]:3
Thread[Thread-3,1,main]:5
Thread[Thread-3,1,main]:4
Thread[Thread-3,1,main]:3
Thread[Thread-3,1,main]:2
Thread[Thread-3,1,main]:1
Thread[Thread-2,1,main]:5
Thread[Thread-2,1,main]:4
Thread[Thread-2,1,main]:3
Thread[Thread-1,1,main]:2
Thread[Thread-4,1,main]:5
Thread[Thread-4,1,main]:4
Thread[Thread-4,1,main]:3
Thread[Thread-4,1,main]:2
Thread[Thread-4,1,main]:1
Thread[Thread-1,1,main]:1
Thread[Thread-2,1,main]:2
Thread[Thread-2,1,main]:1
Thread[Thread-5,1,main]:5
Thread[Thread-5,1,main]:4
Thread[Thread-5,1,main]:3
Thread[Thread-5,1,main]:2
Thread[Thread-5,1,main]:1
在本例中,toString()方法被覆盖,以便使用Thread.toString()方法来打印线程的名称(可以通过构造器自己设置名称,这里是自动生成的名称)以及线程的优先级、线程所属的“线程组”。线程0的优先级最高,其余线程的优先权被设为最低。
当然,对于已存在的线程,还可以用getPriority()方法得到其优先权,还可以在任何时候使用setPriority()方法更改其优先权。尽管JDK有10个优先级别,但是与操作系统映射不确定,所以一般只使用MAX_PRIORITY、NORM_PRIORITY、MIN_PRIORITY三种级别。
4. 后台进程:是指在程序运行的时候在后台提供的一种通用服务的线程,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非后台线程结束时,程序也就终止了。即只要有非后台线程还在运行,程序就不会终止。比如,执行main()的就是一个非后台线程。
public class Demo extends Thread{
public Demo(){
setDaemon(true);
start();
}
public void run(){
while(true){
try{
sleep(100);
}catch(InterruptedException e){
throw new RuntimeException(e);
}
System.out.println(this);
}
}
public static void main(String[] args) {
for(int i = 0; i < 5; i++){
new Demo();
}
}
}
必须在线程启动之前调用setDaemon()方法,才能把它设置为后台线程。在run()里面,线程被设定为休眠一段时间。一旦所有的线程都启动了,程序马上会在所有的线程能打印信息之前立刻终止,因为没有非后台线程(除了main())使得程序保持运行。因此,程序未打印任何信息就终止了。
可以通过调用isDaemon()方法来确定线程是否是一个后台线程,如果是一个后台线程,那么它创建任何的线程将被自动设置成后台线程。
5.如果你的类已经继承了其它的类,就不可能同时继承Thread,此时可以使用实现Runnable接口的方法来达到上述目的。要实现Runnable接口,只需实现run()方法,而且Thread也是从Runnable接口实现而来的。Runnable类中只有一个run()方法,如果想对你创建的这个Thread对象做点别的事情(比如在toString()里调用getName()),那么就必须通过调用Thread.currentThread( )方法明确得到对此线程的引用。例子:
public class Demo implements Runnable{
private int countDown = 5;
public String toString(){
return "#"+Thread.currentThread().getName()+":"+countDown;
}
public void run(){
while(true){
System.out.println(this);
if(--countDown == 0)
return;
}
}
public static void main(String[] args) {
for(int i = 1; i <= 5; i++){
new Thread(new Demo(),""+i).start();
}
}
}
new Thread(new Demo(),”“+i):调用Thread另一个构造器,以Runnable的对象作为参数。
6.当使用了Runnable,通常的意思就是,要用run()方法中所实现的这段代码创建一个进程,而不是创建一个对象表示该进程。一个是把线程作为一个对象来表示,另一个是作为一个完全不同的一个实体(即进程)来表示。这点是有争议的。如果仅仅是想开启一个进程以驱动程序的某个部分,就没有理由把整个类写成Runnable类型的。因此,使用内部类把和线程有关的代码隐藏在类的内部,似乎更合理。有五中内部类的形式:
1)使用普通内部类继承Thread类
2)使用匿名内部类构造Thread类,重写run()方法
3)使用普通内部类实现Runnable类
4)使用匿名内部类构造Thread类,参数1构造一个Runnable对象,参数2指明线程名称
5)使用局部内部类,即在方法内部构造Thread类,重写run()方法
(推荐使用第五种,代码会在后续的文章中给出)
7.建立有响应的用户界面,下面例子中比较了不使用线程和使用线程的区别。
class UnresponsiveUI{
private volatile double d = 1;
public UnresponsiveUI() throws Exception{
while(d > 0)
d = d + (Math.PI+Math.E) / d;
System.in.read();
}
}
public class ResponsiveUI extends Thread {
private static volatile double d = 1;
public ResponsiveUI(){
setDaemon(true);
start();
}
public void run(){
while(true){
d = d + (Math.PI+Math.E) / d;
}
}
public static void main(String[] args) throws Exception{
new UnresponsiveUI();
new ResponsiveUI();
Thread.sleep(300);
System.in.read();
System.out.println(d);
}
}
第一个UnresponsiveUI类中,while循环忙于计算,根本达不到System.in.read()这句。第二个ResponsiveUI 类中,由于把线程设为后台线程的缘故,一旦main()方法中输入数据,点击Enter后,main()方法执行结束,后台线程即使没结束,JVM也已经退出,程序结束。
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/lb_383691051/article/details/47700543