Android(java)学习笔记4:线程的控制
1. 线程休眠:
Java中线程休眠指让正在运行的线程暂停执行一段时间,进入阻塞状态,通过调用Thread类的静态方法sleep得以实现。
当线程调用sleep进入阻塞状态后,在其休眠的时间内,该线程不会获得执行的机会,即使系统中没有其他可运行的线程,处于休眠中的线程也不会执行,常用sleep方法来暂停程序的执行。
1 线程休眠的sleep()方法的语法格式: 2 try { 3 Thread.sleep (2000); 4 } catch (InterruptedException e) { 5 e.printStackTrace(); 6 }
下面我们根据具体的实例来了解sleep()的用法:
当然这里是利用自己新建一个新的类ThreadSleep,也可以利用匿名类:
1 package cn.itcast_04; 2 3 import java.util.Date; 4 5 public class ThreadSleep extends Thread { 6 @Override 7 public void run() { 8 for (int x = 0; x < 100; x++) { 9 System.out.println(getName() + ":" + x + ",日期:" + new Date()); 10 // 睡眠 11 // 困了,我稍微休息1秒钟 12 try { 13 Thread.sleep(1000); 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 } 18 } 19 }
下面我们新建的一个ThreadSleepDemo类,调用main方法测试上面这个类:
1 package cn.itcast_04; 2 3 /* 4 * 线程休眠 5 * public static void sleep(long millis) 6 */ 7 public class ThreadSleepDemo { 8 public static void main(String[] args) { 9 ThreadSleep ts1 = new ThreadSleep(); 10 ThreadSleep ts2 = new ThreadSleep(); 11 ThreadSleep ts3 = new ThreadSleep(); 12 13 ts1.setName("林青霞"); 14 ts2.setName("林志玲"); 15 ts3.setName("林志颖"); 16 17 ts1.start(); 18 ts2.start(); 19 ts3.start(); 20 } 21 }
程序运行得出结果:
2. 线程的挂起
线程的挂起是指暂停当前正在执行的线程,当另一个线程执行完毕后,才继续执行当前线程(类似于单片机程序中的中断)。实现线程的挂起,可以使用Thread类中的join方法来完成。这就好比此时你正在看电视,却突然有人上门收水费,读者必须付完水费后才能继续看电视。
当在某个程序执行流中调用其他线程的join()方法时候,调用线程将被阻塞,直到被join方法加入的join线程执行完毕为止。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
join()方法通常由使用线程的程序调用,将大问题划分为许多小问题,每个小问题分配一个线程。当所有的小问题都得到处理之后,再调用主线程来进一步操作。
下面是使用join()方法的实例:
1 package cn.itcast_04; 2 3 public class ThreadJoin extends Thread { 4 @Override 5 public void run() { 6 for (int x = 0; x < 100; x++) { 7 System.out.println(getName() + ":" + x); 8 } 9 } 10 }
测试类如下:
1 package cn.itcast_04; 2 3 /* 4 * public final void join():等待该线程终止。 5 */ 6 public class ThreadJoinDemo { 7 public static void main(String[] args) { 8 ThreadJoin tj1 = new ThreadJoin(); 9 ThreadJoin tj2 = new ThreadJoin(); 10 ThreadJoin tj3 = new ThreadJoin(); 11 12 tj1.setName("李渊"); 13 tj2.setName("李世民"); 14 tj3.setName("李元霸"); 15 16 tj1.start(); 17 try { 18 tj1.join(); 19 } catch (InterruptedException e) { 20 e.printStackTrace(); 21 } 22 23 tj2.start(); 24 tj3.start(); 25 } 26 }
执行结果:等"李渊"这个线程走完了,其余两个才会开始抢。
首先是Main线程中,使用join方法调用tj1线程,此时就会先执行tj1线程,当tj1执行完了,再去让tj2 和 tj3 去抢CPU执行权。
下面看看JDK中join方法源码,如下:
1 /** 2 * Waits at most <code>millis</code> milliseconds for this thread to 3 * die. A timeout of <code>0</code> means to wait forever. 4 */ 5 //此处A timeout of 0 means to wait forever 字面意思是永远等待,其实是等到t结束后。 6 public final synchronized void join(long millis) throws InterruptedException { 7 long base = System.currentTimeMillis(); 8 long now = 0; 9 10 if (millis < 0) { 11 throw new IllegalArgumentException("timeout value is negative"); 12 } 13 14 if (millis == 0) { 15 while (isAlive()) { 16 wait(0); 17 } 18 } else { 19 while (isAlive()) { 20 long delay = millis - now; 21 if (delay <= 0) { 22 break; 23 } 24 wait(delay); 25 now = System.currentTimeMillis() - base; 26 } 27 } 28 }
join方法实现是通过wait(小提示:Object 提供的方法)。 当main线程调用t.join时候,main线程会获得线程对象t的锁(wait 意味着拿到该对象的锁),调用该对象的wait(等待时间),直到该对象唤醒main线程 ,比如退出后。这就意味着main 线程调用t.join时,必须能够拿到线程t对象的锁。
接下来我们看看下面代码,如下:
public class JoinTest implements Runnable{ public static int a = 0; public void run() { for (int k = 0; k < 5; k++) { a = a + 1; } } public static void main(String[] args) throws Exception { Runnable r = new JoinTest(); Thread t = new Thread(r); t.start(); System.out.println(a); } }
输出结果,如下:
请问程序的输出结果是 5 吗?
答案是:有可能。其实你很难遇到输出5的时候,通常情况下都不是5。(上面输出结果就是0,不是5)当然这也和机器有严重的关系。
为什么呢?
我的解释是当主线程main方法执行System.out.println(a);这条语句时,线程还没有真正开始运行,或许正在为它分配资源准备运行。
因为为线程分配资源需要时间,而main方法执行完t.start()方法后继续往下执行System.out.println(a);
这个时候得到的结果是a还没有被 改变的值0 。怎样才能让输出结果为5!其实很简单,join() 方法提供了这种功能。join() 方法,它能够使调用该方法的线程在此之前执行完毕。
根据上面修改代码,如下:
1 package com.himi.join; 2 3 public class JoinTest implements Runnable { 4 5 public static int a = 0; 6 7 public void run() { 8 for (int k = 0; k < 5; k++) { 9 a = a + 1; 10 } 11 } 12 13 public static void main(String[] args) throws Exception { 14 Runnable r = new JoinTest(); 15 Thread t = new Thread(r); 16 t.start(); 17 //加入join()方法 18 t.join(); 19 System.out.println(a); 20 } 21 22 }
运行效果,如下:
3. 线程的礼让:
线程礼让就是给当前正在处于运行状态下的线程一个提醒,告知它可以将资源礼让给其他线程,这仅仅是一种暗示,没有任何一种机制保证当前的线程会将资源礼让。
代码案例:
1 package cn.itcast_04; 2 3 public class ThreadYield extends Thread { 4 @Override 5 public void run() { 6 for (int x = 0; x < 100; x++) { 7 System.out.println(getName() + ":" + x); 8 Thread.yield(); 9 } 10 } 11 }
测试类的代码:
1 package cn.itcast_04; 2 3 /* 4 * public static void yield():暂停当前正在执行的线程对象,并执行其他线程。 5 * 让多个线程的执行更和谐,但是不能靠它保证一人一次。 6 */ 7 public class ThreadYieldDemo { 8 public static void main(String[] args) { 9 ThreadYield ty1 = new ThreadYield(); 10 ThreadYield ty2 = new ThreadYield(); 11 12 ty1.setName("林青霞"); 13 ty2.setName("刘意"); 14 15 ty1.start(); 16 ty2.start(); 17 } 18 }
运行结果如下:
4. 守护线程(后台):
代码示例如下:
1 package cn.itcast_04; 2 3 public class ThreadDaemon extends Thread { 4 @Override 5 public void run() { 6 for (int x = 0; x < 100; x++) { 7 System.out.println(getName() + ":" + x); 8 } 9 } 10 }
测试代码如下:
1 package cn.itcast_04; 2 3 /* 4 * public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程。 5 * 当正在运行的线程都是守护线程时,Java 虚拟机退出。 该方法必须在启动线程前调用。 6 * 7 * 游戏:坦克大战。 8 */ 9 public class ThreadDaemonDemo { 10 public static void main(String[] args) { 11 ThreadDaemon td1 = new ThreadDaemon(); 12 ThreadDaemon td2 = new ThreadDaemon(); 13 14 td1.setName("关羽"); 15 td2.setName("张飞"); 16 17 // 设置守护线程,表示一旦主线程main结束,这两个守护main线程的守护线程就要跟着结束 18 td1.setDaemon(true); 19 td2.setDaemon(true); 20 21 td1.start(); 22 td2.start(); 23 24 Thread.currentThread().setName("刘备"); 25 for (int x = 0; x < 5; x++) { 26 System.out.println(Thread.currentThread().getName() + ":" + x); 27 } 28 } 29 }
执行结果如下:
线程"刘备"结束之后,其他两个线程也跑了一会再结束。(缓冲)
可以根据经典坦克大战游戏类比学习:
5. 线程的中断:
(1)代码演示:
1 package cn.itcast_04; 2 3 import java.util.Date; 4 5 public class ThreadStop extends Thread { 6 @Override 7 public void run() { 8 System.out.println("开始执行:" + new Date()); 9 10 // 我要休息10秒钟,亲,不要打扰我哦 11 try { 12 Thread.sleep(10000); 13 } catch (InterruptedException e) { 14 // e.printStackTrace(); 15 System.out.println("线程被终止了"); 16 } 17 18 System.out.println("结束执行:" + new Date()); 19 } 20 }
1 package cn.itcast_04; 2 3 /* 4 * public final void stop():让线程停止,过时了,但是还可以使用。 5 * public void interrupt():中断线程。 把线程的状态终止,并抛出一个InterruptedException。 6 */ 7 public class ThreadStopDemo { 8 public static void main(String[] args) { 9 ThreadStop ts = new ThreadStop(); 10 ts.start(); 11 12 // 你超过三秒不醒过来,我就干死你 13 try { 14 Thread.sleep(3000); 15 // ts.stop(); 16 ts.interrupt(); 17 } catch (InterruptedException e) { 18 e.printStackTrace(); 19 } 20 } 21 }
执行结果:休眠了3秒钟之后,这个ts线程就被杀死了,如下图中terminated
图1:ts.stop()方法
图2:ts.interrupt()方法
(2)修改上面ThreadStopDemo的代码,ThreadStop不做修改,如下:
ThreadStop:
1 package cn.itcast_04; 2 3 import java.util.Date; 4 5 public class ThreadStop extends Thread { 6 7 @Override 8 public void run() { 9 10 11 System.out.println("开始执行:"+new Date()); 12 //子线程休眠10s 13 try { 14 Thread.sleep(10000); 15 } catch (InterruptedException e) { 16 // TODO Auto-generated catch block 17 //e.printStackTrace(); 18 System.out.println("线程被终止了"); 19 } 20 21 System.out.println("结束执行:"+new Date()); 22 } 23 24 }
ThreadStopDemo,如下:
1 package cn.itcast_04; 2 3 public class ThreadStopDemo { 4 5 public static void main(String[] args) { 6 7 ThreadStop ts = new ThreadStop(); 8 9 ts.start(); 10 11 try { 12 Thread.sleep(15000); 13 ts.interrupt(); 14 } catch (InterruptedException e) { 15 // TODO Auto-generated catch block 16 e.printStackTrace(); 17 } 18 } 19 20 }
运行程序,如下:
(3)修改上面的ThreadStop,ThreadStopDemo,如下:
ThreadStop,如下:
1 package cn.itcast_04; 2 3 import java.util.Date; 4 5 public class ThreadStop extends Thread { 6 7 @Override 8 public void run() { 9 10 11 System.out.println("开始执行:"+new Date()); 12 //子线程休眠16s 13 try { 14 Thread.sleep(16000); 15 } catch (InterruptedException e) { 16 // TODO Auto-generated catch block 17 //e.printStackTrace(); 18 System.out.println("线程被终止了"); 19 } 20 21 System.out.println("结束执行:"+new Date()); 22 } 23 24 }
ThreadStopDemo,如下:
1 package cn.itcast_04; 2 3 public class ThreadStopDemo { 4 5 public static void main(String[] args) { 6 7 ThreadStop ts = new ThreadStop(); 8 9 ts.start(); 10 11 try { 12 Thread.sleep(15000); 13 ts.interrupt(); 14 } catch (InterruptedException e) { 15 // TODO Auto-generated catch block 16 e.printStackTrace(); 17 } 18 } 19 20 }
运行效果,如下: