目标:
了解设置和取得线程的名称。
了解线程的强制运行。
了解线程的休眠。
了解线程的礼让。
了解线程的中断操作。
具体内容:在多线程中所有的操作方法实际上都是从Thread类开始的。所有的操作都是在Thread类之中的。
线程操作的主要方法
-
NO 方法名称 类型 描述
-
1 public Thread(Runnable target) 构造 接收Runnable接口子类对象,实例化Thread对象
-
2 public Thread(Runnable target,String name) 构造 接收Runnable接口子类对象,实例化Thread对象,并设置线程名称
-
3 public Thread(String name) 构造 实例化Thread对象,并设置线程名称。
-
4 public static Thread currentThread() 普通 返回目前正在执行的线程。
-
5 public final String getName() 普通 返回线程的名称
-
6 public final int getPriority() 普通 返回线程的优先级
-
7 public boolean isInterrupted() 普通 判断目前线程是否被中断,如果是,返回true,否则返回false
-
8 public final boolean isActive() 普通 判断线程是否在活动,如果是返回true,否则返回false
-
9 public final void join() throws InterruptedException 普通 等待线程死亡
-
10 public final synchronized void join(long millis) throws
-
InterruptedException 普通 等待millis毫秒后,线程死亡。
-
11 public void run() 普通 执行线程
-
12 public final void setName() 普通 设定线程名称
-
13 public final void setPriority(int newPriority) 普通 设定线程的优先级
-
14 public static void sleep(long millis) throws
-
InterruptedException 普通 使目前正在执行的线程休眠millis毫秒
-
15 public void start() 普通 开始执行线程。
-
16 public static void yield() 普通 将目前正在执行的线程暂停一次,允许其他线程执行
-
17 public final void setDaemon(boolean on) 普通 将一个线程设置成后台运行
-
18 public final void setPriority(int newPriority) 普通 更改线程的优先级
线程名称
取得和设置线程名称
在Thread类中,可以通过getName()方法取得线程名称,通过setName()方法设置线程的名称。
线程的名称一般在启动线程前设置,但也允许为已经存在的线程设置名称。允许两个Thread对象有相同的名字,但为了清晰,应该尽量避免这种情况的发生。
另外,如果程序并没有为线程指定名称,则系统会自动的为线程分配一个名称。
线程的名称最好在线程启动前设置,避免重名。
-
class MyThread implements Runnable{ // 实现Runnable接口
-
public void run(){ // 覆写run()方法
-
for(int i=0;i<3;i++){
-
System.out.println(Thread.currentThread().getName()
-
+ "运行,i = " + i) ; // 取得当前线程的名字
-
}
-
}
-
};
-
public class ThreadNameDemo{
-
public static void main(String args[]){
-
MyThread mt = new MyThread() ; // 实例化Runnable子类对象
-
new Thread(mt).start() ; // 系统自动设置线程名称
-
new Thread(mt,"线程-A").start() ; // 手工设置线程名称
-
new Thread(mt,"线程-B").start() ; // 手工设置线程名称
-
new Thread(mt).start() ; // 系统自动设置线程名称
-
new Thread(mt).start() ; // 系统自动设置线程名称
-
}
-
};
从执行效果来看,指定的名称会自动出现,如果没有指定会发现线程使用自动编号的方式完成,按照:Thread-0、Thread-1 依次编号,实际上肯定在类中存在一个static属性,用于记录编号。
取得当前线程
程序可以通过currentThread()方法取得当前正在运行的线程对象。
-
class MyThread implements Runnable{ // 实现Runnable接口
-
public void run(){ // 覆写run()方法
-
for(int i=0;i<3;i++){
-
System.out.println(Thread.currentThread().getName()
-
+ "运行,i = " + i) ; // 取得当前线程的名字
-
}
-
}
-
};
-
public class CurrentThreadDemo{
-
public static void main(String args[]){
-
MyThread mt = new MyThread() ; // 实例化Runnable子类对象
-
new Thread(mt,"线程").start() ; // 启动线程
-
mt.run() ; // 直接调用run()方法
-
}
-
};

此时发现,程序中由主方法直接通过线程对象调用里面的run()方法,所以输出的结果中包含了一个“main”,此线程就是“mt.run”, 因为调用此语句是由主方法完成的,也就是说实际上主方法本身也是一个线程——主线程。
问题:既然主方法都是以线程的形式出现的,那么Java运行时到底启动了多少个线程?
回答:至少启动了两个。从目前的知识上看,每当Java程序执行的时候,实际上都会启动一个JVM,每一个JVM实际上就是在操作系统中启动了一个进程。Java中本身具备了垃圾收集机制,所以Java运行时至少启动两个线程:主线程、GC。
判断线程是否启动
-
class MyThread implements Runnable{ // 实现Runnable接口
-
public void run(){ // 覆写run()方法
-
for(int i=0;i<3;i++){
-
System.out.println(Thread.currentThread().getName()
-
+ "运行,i = " + i) ; // 取得当前线程的名字
-
}
-
}
-
};
-
public class ThreadAliveDemo{
-
public static void main(String args[]){
-
MyThread mt = new MyThread() ; // 实例化Runnable子类对象
-
Thread t = new Thread(mt,"线程"); // 实例化Thread对象
-
System.out.println("线程开始执行之前 --> " + t.isAlive()) ; // 判断是否启动
-
t.start() ; // 启动线程
-
System.out.println("线程开始执行之后 --> " + t.isAlive()) ; // 判断是否启动
-
for(int i=0;i<3;i++){
-
System.out.println(" main运行 --> " + i) ;
-
}
-
// 以下的输出结果不确定
-
System.out.println("代码执行之后 --> " + t.isAlive()) ; // 判断是否启动
-
-
}
-
};

发现调用start方法之后线程就**了,如果线程执行任务完毕后就会被关闭。如果没有执行完毕,仍处于**状态。
线程的强制运行
在线程操作中,可以使用join()方法让一个线程强制运行,线程强制运行期间,其他线程无法运行,必须等待此线程完成之后才可以继续运行。
-
class MyThread implements Runnable{ // 实现Runnable接口
-
public void run(){ // 覆写run()方法
-
for(int i=0;i<50;i++){
-
System.out.println(Thread.currentThread().getName()
-
+ "运行,i = " + i) ; // 取得当前线程的名字
-
}
-
}
-
};
-
public class ThreadJoinDemo{
-
public static void main(String args[]){
-
MyThread mt = new MyThread() ; // 实例化Runnable子类对象
-
Thread t = new Thread(mt,"线程"); // 实例化Thread对象
-
t.start() ; // 启动线程
-
for(int i=0;i<50;i++){
-
if(i>10){
-
try{
-
t.join() ; // 线程强制运行
-
}catch(InterruptedException e){}
-
}
-
System.out.println("Main线程运行 --> " + i) ;
-
}
-
}
-
};


线程的休眠
在程序中允许一个线程进行暂时的休眠,直接使用Thread.sleep()方法即可。
-
class MyThread implements Runnable{ // 实现Runnable接口
-
public void run(){ // 覆写run()方法
-
for(int i=0;i<50;i++){
-
try{
-
Thread.sleep(500) ; // 线程休眠
-
}catch(InterruptedException e){}
-
System.out.println(Thread.currentThread().getName()
-
+ "运行,i = " + i) ; // 取得当前线程的名字
-
}
-
}
-
};
-
public class ThreadSleepDemo{
-
public static void main(String args[]){
-
MyThread mt = new MyThread() ; // 实例化Runnable子类对象
-
Thread t = new Thread(mt,"线程"); // 实例化Thread对象
-
t.start() ; // 启动线程
-
}
-
};
会发现每隔500毫秒,执行一次。
线程的中断
当一个线程运行的时候,另外一个线程可以直接通过interrupt()方法 中断其运行状态。
-
class MyThread implements Runnable{ // 实现Runnable接口
-
public void run(){ // 覆写run()方法
-
System.out.println("1、进入run()方法") ;
-
try{
-
Thread.sleep(10000) ; // 线程休眠10秒
-
System.out.println("2、已经完成了休眠") ;
-
}catch(InterruptedException e){
-
System.out.println("3、休眠被终止") ;
-
return ; // 返回调用处
-
}
-
System.out.println("4、run()方法正常结束") ;
-
}
-
};
-
public class ThreadInterruptDemo{
-
public static void main(String args[]){
-
MyThread mt = new MyThread() ; // 实例化Runnable子类对象
-
Thread t = new Thread(mt,"线程"); // 实例化Thread对象
-
t.start() ; // 启动线程
-
try{
-
Thread.sleep(2000) ; // 线程休眠2秒
-
}catch(InterruptedException e){
-
System.out.println("3、休眠被终止") ;
-
}
-
t.interrupt() ; // 中断线程执行
-
}
-
};
后台进程
在Java中,只要有一个程序没有执行完(一个线程在运行),则整个Java的进程就不会消失,所以此时可以设置一个后台线程,这样即使Java进程结束了,此后台线程依然会继续运行,也就是说不影响进程的结束。要想实现这样的操作,直接使用setDaemon()方法即可。
-
class MyThread implements Runnable{ // 实现Runnable接口
-
public void run(){ // 覆写run()方法
-
while(true){
-
System.out.println(Thread.currentThread().getName() + "在运行。") ;
-
}
-
}
-
};
-
public class ThreadDaemonDemo{
-
public static void main(String args[]){
-
MyThread mt = new MyThread() ; // 实例化Runnable子类对象
-
Thread t = new Thread(mt,"线程"); // 实例化Thread对象
-
t.setDaemon(true) ; // 此线程在后台运行
-
t.start() ; // 启动线程
-
}
-
};
会发现没有打印任何东西,如果设置为false的话则会一直打印,不会结束进程。
线程的优先级
在Java的线程操作中,所有的线程在运行前都会保持在就绪状态,那么此时哪个线程的优先级高,哪个线程就有可能被先执行。
-
class MyThread implements Runnable{ // 实现Runnable接口
-
public void run(){ // 覆写run()方法
-
for(int i=0;i<5;i++){
-
try{
-
Thread.sleep(500) ; // 线程休眠
-
}catch(InterruptedException e){}
-
System.out.println(Thread.currentThread().getName()
-
+ "运行,i = " + i) ; // 取得当前线程的名字
-
}
-
}
-
};
-
public class ThreadPriorityDemo{
-
public static void main(String args[]){
-
Thread t1 = new Thread(new MyThread(),"线程A") ; // 实例化线程对象
-
Thread t2 = new Thread(new MyThread(),"线程B") ; // 实例化线程对象
-
Thread t3 = new Thread(new MyThread(),"线程C") ; // 实例化线程对象
-
t1.setPriority(Thread.MIN_PRIORITY) ; // 优先级最低
-
t2.setPriority(Thread.MAX_PRIORITY) ; // 优先级最高
-
t3.setPriority(Thread.NORM_PRIORITY) ; // 优先级居中
-
t1.start() ; // 启动线程
-
t2.start() ; // 启动线程
-
t3.start() ; // 启动线程
-
}
-
};
主线程的优先级是NORM_PRIORITY 为5 是中等级别。
默认的线程优先级也是NORM_PRIORITY。
程序示例如下:
-
public class MainPriorityDemo{
-
public static void main(String args[]){
-
System.out.println("主方法的优先级:" +
-
Thread.currentThread().getPriority()) ; // 取得主方法的优先级
-
System.out.println("MAX_PRIORITY = " + Thread.MAX_PRIORITY) ;
-
System.out.println("NORM_PRIORITY = " + Thread.NORM_PRIORITY) ;
-
System.out.println("MIN_PRIORITY = " + Thread.MIN_PRIORITY) ;
-
}
-
};

线程的礼让
在线程操作中,也可以使用yield()方法将一个线程的操作暂时让给其他线程执行。
-
class MyThread implements Runnable{ // 实现Runnable接口
-
public void run(){ // 覆写run()方法
-
for(int i=0;i<5;i++){
-
try{
-
Thread.sleep(500) ;
-
}catch(Exception e){}
-
System.out.println(Thread.currentThread().getName()
-
+ "运行,i = " + i) ; // 取得当前线程的名字
-
if(i==2){
-
System.out.print("线程礼让:") ;
-
Thread.currentThread().yield() ; // 线程礼让
-
}
-
}
-
}
-
};
-
public class ThreadYieldDemo{
-
public static void main(String args[]){
-
MyThread my = new MyThread() ; // 实例化MyThread对象
-
Thread t1 = new Thread(my,"线程A") ;
-
Thread t2 = new Thread(my,"线程B") ;
-
t1.start() ;
-
t2.start() ;
-
}
-
};
总结:
重点是掌握线程的操作方法,对于这些操作方法只需要从Thread类中查找即可。
线程操作范例
实例要求:设计一个线程操作类,要求可以产生三个线程对象,并可以分别设置三个线程的休眠时间,如下所示:
线程A,休眠10秒。
线程B,休眠20秒
线程C,休眠30秒
分析:已知线程的实现有两种方式,一种是继承Thread类,另外一种是实现Runnable接口。而且在类中应该保存线程名称和休眠时间两个属性。
(一)使用Thread类
在Thread类中直接存在了name属性。
-
class MyThread extends Thread{
-
private int time ;
-
public MyThread(String name,int time){
-
super(name) ; // 设置线程名称
-
this.time = time ; // 设置休眠时间
-
}
-
public void run(){
-
try{
-
Thread.sleep(this.time) ; // 休眠指定的时间
-
}catch(InterruptedException e){
-
e.printStackTrace() ;
-
}
-
System.out.println(Thread.currentThread().getName() + "线程,休眠"
-
+ this.time + "毫秒。") ;
-
}
-
};
-
public class ExecDemo01{
-
public static void main(String args[]){
-
MyThread mt1 = new MyThread("线程A",10000) ; // 定义线程对象,指定休眠时间
-
MyThread mt2 = new MyThread("线程B",20000) ; // 定义线程对象,指定休眠时间
-
MyThread mt3 = new MyThread("线程C",30000) ; // 定义线程对象,指定休眠时间
-
mt1.start() ; // 启动线程
-
mt2.start() ; // 启动线程
-
mt3.start() ; // 启动线程
-
}
-
};
(二)使用Runnable
如果使用Runnable接口,则类中是没有线程名称存在的,所以应该单独建立一个name属性,以保存线程的名称。
-
class MyThread implements Runnable{
-
private String name ;
-
private int time ;
-
public MyThread(String name,int time){
-
this.name = name ; // 设置线程名称
-
this.time = time ; // 设置休眠时间
-
}
-
public void run(){
-
try{
-
Thread.sleep(this.time) ; // 休眠指定的时间
-
}catch(InterruptedException e){
-
e.printStackTrace() ;
-
}
-
System.out.println(this.name + "线程,休眠"
-
+ this.time + "毫秒。") ;
-
}
-
};
-
public class ExecDemo02{
-
public static void main(String args[]){
-
MyThread mt1 = new MyThread("线程A",10000) ; // 定义线程对象,指定休眠时间
-
MyThread mt2 = new MyThread("线程B",20000) ; // 定义线程对象,指定休眠时间
-
MyThread mt3 = new MyThread("线程C",30000) ; // 定义线程对象,指定休眠时间
-
new Thread(mt1).start() ; // 启动线程
-
new Thread(mt2).start() ; // 启动线程
-
new Thread(mt3).start() ; // 启动线程
-
}
-
};