标签:线程的生命周期 复习 exec exce rac 知识 void 方便 ret
前言在上一篇文章中,回顾了Java的集合。而在本篇文章中主要介绍多线程的相关知识。主要介绍的知识点为线程的介绍、多线程的使用、以及在多线程中使用的一些方法。
表示进程中负责程序执行的执行单元,依靠程序进行运行。线程是程序中的顺序控制流,只能使用分配给程序的资源和环境。
表示资源的分配和调度的一个独立单元,通常表示为执行中的程序。一个进程至少包含一个线程。
线程和进程一样分为五个阶段:创建、就绪、运行、阻塞和终止。
可以用下述图来进行理解线程的生命周期:
注:上述图来自http://www.runoob.com/wp-content/uploads/2014/01/java-thread.jpg。
在了解了线程和进程之后,我们再来简单的了解下单线程和多线程。
单线程
程序中只存在一个线程,实际上主方法就是一个主线程。
多线程
多线程是指在同一程序中有多个顺序流在执行。 简单的说就是在一个程序中有多个任务运行。
那么在什么情况下用多线程呢?
一般来说,程序中有两个以上的子系统需要并发执行的,这时候就需要利用多线程编程。通过对多线程的使用,可以编写出高效的程序。
那么是不是使用很多线程就能提高效率呢?
不一定的。因为程序中上下文的切换开销也很重要,如果创建了太多的线程,CPU
花费在上下文的切换的时间将多于执行程序的时间!这时是会降低程序执行效率的。
所以有效利用多线程的关键是理解程序是并发执行而不是串行执行的。
一般来说,我们在对线程进行创建的时候,一般是继承Thread 类或实现Runnable 接口。其实还有一种方式是实现 Callable接口,然后与Future 或线程池结合使用, 类似于Runnable接口,但是就功能上来说更为强大一些,也就是被执行之后,可以拿到返回值。
这里我们分别一个例子使用继承Thread 类、实现Runnable 接口和实现Callable接口与Future结合来进行创建线程。
代码示例:
注:线程启动的方法是start而不是run。因为使用start方法整个线程处于就绪状态,等待虚拟机来进行调度。而使用run,也就是当作了一个普通的方法进行启动,这样虚拟机不会进行线程调度,虚拟机会执行这个方法直到结束后自动退出。
代码示例:
public class Test {
public static void main(String[] args) {
ThreadTest threadTest=new ThreadTest();
threadTest.start();
RunalbeTest runalbeTest=new RunalbeTest();
Thread thread=new Thread(runalbeTest);
thread.start();
CallableTest callableTest=new CallableTest();
FutureTask<Integer> ft = new FutureTask<Integer>(callableTest);
Thread thread2=new Thread(ft);
thread2.start();
try {
System.out.println("返回值:"+ft.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class ThreadTest extends Thread{
@Override
public void run() {
System.out.println("这是一个Thread的线程!");
}
}
class RunalbeTest implements Runnable{
@Override
public void run() {
System.out.println("这是一个Runnable的线程!");
}
}
class CallableTest implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("这是一个Callable的线程!");
return 2;
}
}
运行结果:
这是一个Thread的线程!
这是一个Runnable的线程!
这是一个Callable的线程!
返回值:2
通过上述示例代码中,我们发现使用继承 Thread 类的方式创建线程时,编写最为简单。而使用Runnable、Callable 接口的方式创建线程的时候,需要通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用start方法来运行线程代码。顺便说下,其实Thread类实际上也是实现了Runnable接口的一个类。
但是在这里,我推荐大家创建单线程的时候使用继承 Thread 类方式创建,多线线程的时候使用Runnable、Callable 接口的方式来创建创建线程。
至于为什么呢?在下面中的描述已给出理由。
使用Runnable、Callable 接口的方式创建的线程,可以实现资源共享,增强代码的复用性,并且可以避免单继承的局限性,可以和线程池完美结合。但是也有不好的,就是写起来不太方便,使用其中的方法不够简介。
总的来说就是,单线程建议用继承 Thread 类创建,多线程建议- 使用Runnable、Callable 接口的方式创建。
使用yield方法表示暂停当前正在执行的线程对象,并执行其他线程。
代码示例:
public class YieldTest {
public static void main(String[] args) {
Test1 t1 = new Test1("张三");
Test1 t2 = new Test1("李四");
new Thread(t1).start();
new Thread(t2).start();
}
}
class Test1 implements Runnable {
private String name;
public Test1(String name) {
this.name=name;
}
@Override
public void run() {
System.out.println(this.name + " 线程运行开始!");
for (int i = 1; i <= 5; i++) {
System.out.println(""+this.name + "-----" + i);
// 当为3的时候,让出资源
if (i == 3) {
Thread.yield();
}
}
System.out.println(this.name + " 线程运行结束!");
}
}
执行结果一:
张三 线程运行开始!
张三-----1
张三-----2
张三-----3
李四 线程运行开始!
李四-----1
李四-----2
李四-----3
张三-----4
张三-----5
张三 线程运行结束!
李四-----4
李四-----5
李四 线程运行结束!
执行结果二:
张三 线程运行开始!
李四 线程运行开始!
李四-----1
李四-----2
李四-----3
张三-----1
张三-----2
张三-----3
李四-----4
李四-----5
李四 线程运行结束!
张三-----4
张三-----5
张三 线程运行结束!
上述中的例子我们可以看到,启动两个线程之后,哪个线程先执行到3,就会让出资源,让另一个线程执行。
在这里顺便说下,yield和sleep的区别。
使用join方法指等待某个线程终止。也就是说当子线程调用了join方法之后,后面的代码只有等待该线程执行完毕之后才会执行。
如果不好理解,这里依旧使用一段代码来进行说明。
这里我们创建两个线程,并使用main方法执行。顺便提一下,其实main方法也是个线程。如果直接执行的话,可能main方法执行完毕了,子线程还没执行完毕,这里我们就让子线程使用join方法使main方法最后执行。
代码示例:
public class JoinTest {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+ "主线程开始运行!");
Test2 t1=new Test2("A");
Test2 t2=new Test2("B");
t1.start();
t2.start();
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "主线程运行结束!");
}
}
class Test2 extends Thread{
public Test2(String name) {
super(name);
}
public void run() {
System.out.println(this.getName() + " 线程运行开始!");
for (int i = 0; i < 5; i++) {
System.out.println("子线程"+this.getName() + "运行 : " + i);
try {
sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(this.getName() + " 线程运行结束!");
}
}
执行结果:
main主线程开始运行!
B 线程运行开始!
子线程B运行 : 0
A 线程运行开始!
子线程A运行 : 0
子线程A运行 : 1
子线程B运行 : 1
子线程B运行 : 2
子线程B运行 : 3
子线程B运行 : 4
B 线程运行结束!
子线程A运行 : 2
子线程A运行 : 3
子线程A运行 : 4
A 线程运行结束!
main主线程运行结束!
上述示例中的结果显然符合我们的预期。
使用setPriority表示设置线程的优先级。
每个线程都有默认的优先级。主线程的默认优先级为Thread.NORM_PRIORITY。
线程的优先级有继承关系,比如A线程中创建了B线程,那么B将和A具有相同的优先级。
JVM提供了10个线程优先级,但与常见的操作系统都不能很好的映射。如果希望程序能移植到各个操作系统中,应该仅仅使用Thread类有以下三个静态常量作为优先级,这样能保证同样的优先级采用了同样的调度方式
但是设置优先级并不能保证线程一定先执行。我们可以通过一下代码来验证。
代码示例:
public class PriorityTest {
public static void main(String[] args) {
Test3 t1 = new Test3("张三");
Test3 t2 = new Test3("李四");
t1.setPriority(Thread.MIN_PRIORITY);
t2.setPriority(Thread.MAX_PRIORITY);
t1.start();
t2.start();
}
}
class Test3 extends Thread {
public Test3(String name) {
super(name);
}
@Override
public void run() {
System.out.println(this.getName() + " 线程运行开始!");
for (int i = 1; i <= 5; i++) {
System.out.println("子线程"+this.getName() + "运行 : " + i);
try {
sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(this.getName() + " 线程运行结束!");
}
}
执行结果一:
李四 线程运行开始!
子线程李四运行 : 1
张三 线程运行开始!
子线程张三运行 : 1
子线程张三运行 : 2
子线程李四运行 : 2
子线程李四运行 : 3
子线程李四运行 : 4
子线程张三运行 : 3
子线程李四运行 : 5
李四 线程运行结束!
子线程张三运行 : 4
子线程张三运行 : 5
张三 线程运行结束!
执行结果二:
张三 线程运行开始!
子线程张三运行 : 1
李四 线程运行开始!
子线程李四运行 : 1
子线程张三运行 : 2
子线程张三运行 : 3
子线程李四运行 : 2
子线程张三运行 : 4
子线程李四运行 : 3
子线程张三运行 : 5
子线程李四运行 : 4
张三 线程运行结束!
子线程李四运行 : 5
李四 线程运行结束!
执行结果三:
李四 线程运行开始!
子线程李四运行 : 1
张三 线程运行开始!
子线程张三运行 : 1
子线程李四运行 : 2
子线程李四运行 : 3
子线程李四运行 : 4
子线程张三运行 : 2
子线程张三运行 : 3
子线程张三运行 : 4
子线程李四运行 : 5
子线程张三运行 : 5
李四 线程运行结束!
张三 线程运行结束!
线程中还有许多方法,但是这里并不会全部细说。只简单的列举了几个方法使用。更多的方法使用可以查看相关的API文档。这里我也顺便总结了一些关于这些方法的描述。
其实这篇文章很久之前都已经打好草稿了,但是由于各种原因,只到今天才写完。虽然也只是简单的介绍了一下多线程的相关知识,也只能算个入门级的教程吧。不过写完之后,感觉自己又重新复习了一遍多线程,对多线程的理解又加深了一些。
话已尽此,不在多说。
原创不易,如果感觉不错,希望给个推荐!您的支持是我写作的最大动力!
参考:https://blog.csdn.net/evankaka/article/details/44153709#t1
版权声明:
作者:虚无境
博客园出处:http://www.cnblogs.com/xuwujing
CSDN出处:http://blog.csdn.net/qazwsxpcm
个人博客出处:http://www.panchengming.com
标签:线程的生命周期 复习 exec exce rac 知识 void 方便 ret
原文地址:http://blog.51cto.com/12965378/2322819