目标:
了解设置和取得线程的名称。
了解线程的强制运行。
了解线程的休眠。
了解线程的礼让。
了解线程的中断操作。
具体内容:在多线程中所有的操作方法实际上都是从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() ; // 启动线程 } };