在这篇文章里,我们关注多线程。多线程是一个复杂的话题,包含了很多内容,这篇文章主要关注线程的基本属性、如何创建线程、线程的状态切换以及线程通信,我们把线程同步的话题留到下一篇文章中。
线程是操作系统运行的基本单位,它被封装在进程中,一个进程可以包含多个线程。即使我们不手动创造线程,进程也会有一个默认的线程在运行。
对于JVM来说,当我们编写一个单线程的程序去运行时,JVM中也是有至少两个线程在运行,一个是我们创建的程序,一个是垃圾回收。
线程基本信息
我们可以通过Thread.currentThread()方法获取当前线程的一些信息,并对其进行修改。
我们来看以下代码:
1 public class test_1 { 2 3 public static void main(String[] args){ 4 //查看并修改当前线程的属性 5 String name = Thread.currentThread().getName(); 6 int priority = Thread.currentThread().getPriority(); 7 String groupName = Thread.currentThread().getThreadGroup().getName(); 8 //该线程是否为守护线程。 9 boolean isDaemon = Thread.currentThread().isDaemon(); 10 //测试线程是否处于活动状态。如果线程已经启动且尚未终止,则为活动状态。 11 boolean isAlive = Thread.currentThread().isAlive(); 12 System.out.println("Thread Name:"+name); 13 System.out.println("poiority:"+priority); 14 System.out.println("groupName:"+groupName); 15 System.out.println("isDaemon:"+isDaemon); 16 System.out.println("isAlive:"+isAlive); 17 System.out.println("-------------"); 18 Thread.currentThread().setName("Test_修改后名字"); 19 Thread.currentThread().setPriority(Thread.MAX_PRIORITY); 20 name = Thread.currentThread().getName(); 21 priority = Thread.currentThread().getPriority(); 22 groupName = Thread.currentThread().getThreadGroup().getName(); 23 isDaemon = Thread.currentThread().isDaemon(); 24 System.out.println("Thread Name:" + name); 25 System.out.println("Priority:" + priority); 26 27 } 28 29 30 }
其中列出的属性说明如下:
- GroupName,每个线程都会默认在一个线程组里,我们也可以显式的创建线程组,一个线程组中也可以包含子线程组,这样线程和线程组,就构成了一个树状结构。
- Name,每个线程都会有一个名字,如果不显式指定,那么名字的规则是“Thread-xxx”。
- Priority,每个线程都会有自己的优先级,JVM对优先级的处理方式是“抢占式”的。当JVM发现优先级高的线程时,马上运行该线程;对于多个优先级相等的线程,JVM对其进行轮询处理。Java的线程优先级从1到10,默认是5,Thread类定义了2个常量:MIN_PRIORITY和MAX_PRIORITY来表示最高和最低优先级。
我们可以看下面的代码,它定义了两个不同优先级的线程:
1 public class test_2 { 2 3 public static void main(String[] args) 4 { 5 Thread thread1 = new Thread("low") 6 { 7 public void run(){ 8 for(int i=0;i<5;i++){ 9 System.out.println(this.getName() +" is running!"); 10 } 11 } 12 }; 13 14 Thread thread2 = new Thread("high"){ 15 public void run(){ 16 for(int i=0;i<5;i++){ 17 System.out.println(this.getName() +" is running!"); 18 } 19 } 20 }; 21 thread1.setPriority(Thread.MIN_PRIORITY); 22 thread2.setPriority(Thread.MAX_PRIORITY); 23 thread1.start(); 24 thread2.start(); 25 } 26 27 }
每个线程执行时都有一个优先级的属性,优先级高的线程可以获得较多的执行机会,而优先级低的线程则获得较少的执行机会。与线程休眠类似,线程的优先级仍然无法保障线程的执行次序。只不过,优先级高的线程获取CPU资源的概率较大,优先级低的也并非没机会执行。
每个线程默认的优先级都与创建它的父线程具有相同的优先级,在默认情况下,main线程具有普通优先级。
Thread类提供了setPriority(int newPriority)和getPriority()方法来设置和返回一个指定线程的优先级,其中setPriority方法的参数是一个整数,范围是1~·0之间,也可以使用Thread类提供的三个静态常量:
MAX_PRIORITY =10
MIN_PRIORITY =1
NORM_PRIORITY =5
- isDaemon,这个属性用来控制父子线程的关系,如果设置为true,当父线程结束后,其下所有子线程也结束,反之,子线程的生命周期不受父线程影响。
我们来看下面的例子:
1 public class test_3 { 2 3 public static void main(String[] args) 4 { 5 Thread thread = new Thread("daemon") 6 { 7 public void run() 8 { 9 Thread subThread = new Thread("sub") 10 { 11 public void run() 12 { 13 for(int i=0;i<100;i++) 14 { 15 System.out.println(this.getName()+" is running! "+i); 16 } 17 } 18 }; 19 //对比注释该语句 20 subThread.setDaemon(true); 21 System.out.println("subThread isDaemon:"+subThread.isDaemon()); 22 subThread.start(); 23 System.out.println("main thread is end!"); 24 25 } 26 }; 27 thread.start(); 28 } 29 30 }
上面代码的运行结果,对比注释掉语句//subThread.setDaemon(true);
如果 subThread isDaemon:false, 子进程会全部执行完
如果 subThread isDaemon:true, 子进程很快就结束了
扩展:
JAVA并发编程——守护线程(Daemon Thread)
在Java中有两类线程:用户线程 (User Thread)、守护线程 (Daemon Thread)。
所谓守护线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因 此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。
用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。
将线程转换为守护线程可以通过调用Thread对象的setDaemon(true)方法来实现。在使用守护线程时需要注意一下几点:
(1) thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。
(2) 在Daemon线程中产生的新线程也是Daemon的。
(3) 守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。