实现线程的三种形式总结
最近有看到Java线程的实现相干问题,在此对线程实现形式做一个小小的总结,当做笔记,便于日后查看。
平时罕用的线程形式有三种:
(1)、继承Thread类,并重写其run()办法。
(2)、实现Runnable接口,并实现其run()办法。
(3)、实现Callable接口,并实现其call()办法。
一、继承Thread类
Thread 类中创立线程最重要的两个办法为:
public void start();
public void run();
采纳 Thread 类创立线程,用户只须要继承 Thread,笼罩 Thread 中的 run 办法,父类 Thread 中的 run 办法没有抛出异样,那么子类也不能抛出异样,最初采纳 start 启动线程即可。
【示例代码1】不应用线程
public class ThreadTest01 {
public static void main(String[] args) {
Processor p = new Processor();
p.run();
method1();
}
private static void method1() {
System.out.println("--------method1()----------");
}
}
class Processor {
public void run() {
for (int i&#61;0; i<10; i&#43;&#43;) {
System.out.println(i);
}
}
}
【执行后果】
0
1
2
3
4
5
6
7
8
9
--------method1()----------
以上程序输入相应的后果(属于串行) &#xff0c; 也就是 run 办法齐全执行实现后&#xff0c;才执行 method1 办法&#xff0c; 也就是 method1 必须期待后面的办法返回才能够失去执行&#xff0c;这是一种“同步编程模型”。
这样执行存在什么样的弊病呢&#xff1f;
依照程序执行&#xff0c;这就极大的升高了程序的执行效率。无奈同时执行多个代码片段&#xff0c;这也是多线程并发所要达到的目标。
【示例代码2】应用线程
public class ThreadTest02 {
public static void main(String[] args) {
Processor p &#61; new Processor();
/*
如果是手动调用该办法,
则并不能采纳 run 来启动一个场景(线程)&#xff0c;
run 就是一个一般办法调用。
*/
//p.run();
/*
采纳 start 启动线程&#xff0c;不是间接调用 run,
start 不是马上执行线程&#xff0c;而是使线程进入就绪状态
线程的真正执行是由 Java 的线程调度机制实现的。
*/
p.start();
//线程只能启动一次&#xff0c;无奈启动屡次
//p.start();
method1();
}
private static void method1() {
System.out.println("--------method1()----------");
}
}
class Processor extends Thread {
/*
笼罩 Thread 中的 run 办法&#xff0c;该办法没有异样
该办法是由 java 线程调度机制调用的,因而
咱们不应该手动调用该办法
*/
public void run() {
for (int i&#61;0; i<10; i&#43;&#43;) {
System.out.println(i);
}
}
}
【执行后果】
--------method1()----------
0
1
2
3
4
5
6
7
8
9
通过输入后果大家会看到&#xff0c;没有依照程序执行&#xff0c;而在输入数字的同时执行了 method1()办法&#xff0c;如果从效率上看&#xff0c;采纳多线程的示例要快些&#xff0c;因为咱们能够看作他是同时执行的&#xff0c; mthod1()办法没有期待后面的操作实现才执行&#xff0c; 这叫“异步编程模型”。
那么&#xff0c;为什么会是这样的执行后果呢&#xff1f;
这就波及到Java线程的调度机制了&#xff0c;该程序蕴含两个线程一个是主线程也就是main线程&#xff0c;另外一个是用户创立的p线程&#xff0c;当类加载实现后&#xff0c;主线程启动&#xff0c;开始执行main办法栈帧&#xff0c;依照代码自上而下的执行程序&#xff0c;先创立Processor的实例化对象p&#xff0c;接着是执行p.start();启动p线程&#xff0c;这时method1()&#xff1b;办法还没有执行&#xff0c;此时两个线程均曾经启动&#xff0c;依照Java线程调度的规定&#xff0c;两个线程开始争夺执行程序的工夫片(即CPU的执行权)&#xff0c;留神&#xff0c;这种争夺是随机的&#xff0c;也就是说&#xff0c;不肯定输入后果就是method1办法先执行&#xff0c;for循环语句后执行。能够多执行几次即可看到不一样的执行后果。
二、实现 Runnable 接口
其实 Thread 对象自身就实现了 Runnable 接口&#xff0c;但个别倡议间接应用 Runnable 接口来写多线程程序&#xff0c;因为接口会比类带来更多的益处(面向接口编程的准则)。
【示例代码3】
public class ThreadTest03 {
public static void main(String[] args) {
//Processor r1 &#61; new Processor();
/*
应用多态机制父类型援用指向子类型对象,
因为这样能够调用Runnable接口的办法
*/
Runnable r1 &#61; new Processor();
//不能间接调用 run办法,起因见上文
//p.run();
//创立线程对象&#xff0c;并将r1对象作为参数传入(Thread的构造方法)
Thread t1 &#61; new Thread(r1);
//启动线程
t1.start();
method1();
}
private static void method1() {
System.out.println("--------method1()----------");
}
}
//实现 Runnable 接口
class Processor implements Runnable {
//实现 Runnable 中的 run 办法
public void run() {
for (int i&#61;0; i<10; i&#43;&#43;) {
System.out.println(i);
}
}
}
【执行后果】
--------method1()----------
0
1
2
3
4
5
6
7
8
9
后果剖析见上&#xff0c;起因是一样的&#xff0c;只不过是换了一种形式实现线程而已。
三、实现Callable接口
察看上文两种线程的执行形式&#xff0c;存在什么毛病。显然&#xff0c;以上两种线程的执行un办法时是没有返回值的&#xff0c;而实际上也会存在须要失去线程执行的返回后果的状况&#xff0c;那么怎么办呢&#xff1f;这时就能够思考应用第三种线程的实现形式。
长处&#xff1a;能够获取到线程的执行后果。
毛病&#xff1a;效率比拟低&#xff0c;在获取t线程执行后果的时候&#xff0c;以后线程受阻塞&#xff0c;效率较低。
【示例代码4-1】应用匿名外部类
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class ThreadTest04 {
public static void main(String[] args) throws Exception {
// 第一步&#xff1a;创立一个“将来工作类”对象。
// 参数十分重要&#xff0c;须要给一个Callable接口实现类对象。
FutureTask task &#61; new FutureTask(new Callable() {
&#64;Override
public Object call() throws Exception { // call()办法就相当于run办法。只不过这个有返回值
// 线程执行一个工作&#xff0c;执行之后可能会有一个执行后果
// 模仿执行
System.out.println("call method begin");
Thread.sleep(1000 * 10);//以后线程睡眠10秒
System.out.println("call method end!");
int a &#61; 100;
int b &#61; 200;
return a &#43; b; //主动装箱(300后果变成Integer)
}
});
// 创立线程对象
Thread t &#61; new Thread(task);
// 启动线程
t.start();
// 这里是main办法&#xff0c;这是在主线程中。
// 在主线程中&#xff0c;怎么获取t线程的返回后果&#xff1f;
// get()办法的执行会导致“以后线程阻塞”
Object obj &#61; task.get();
System.out.println("线程执行后果:" &#43; obj);
// main办法这里的程序要想执行必须期待get()办法的完结
// 而get()办法可能须要很久。因为get()办法是为了拿另一个线程的执行后果
// 另一个线程执行是须要工夫的。
System.out.println("hello world!");
}
}
【执行后果】
call method begin
call method end!
线程执行后果:300
hello world!
【示例代码4-2】不应用匿名外部类
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class ThreadTest01 {
public static void main(String[] args) throws Exception {
//创立Callable接口的实现类的实例化对象
CallableImpl callable &#61; new CallableImpl();
// 第一步&#xff1a;创立一个“将来工作类”对象。
// 参数十分重要&#xff0c;须要给一个Callable接口实现类对象。
FutureTask task &#61; new FutureTask(callable);
// 创立线程对象
Thread t &#61; new Thread(task);
// 启动线程
t.start();
// 这里是main办法&#xff0c;这是在主线程中。
// 在主线程中&#xff0c;怎么获取t线程的返回后果&#xff1f;
// get()办法的执行会导致“以后线程阻塞”
Object obj &#61; task.get();
System.out.println("线程执行后果:" &#43; obj);
// main办法这里的程序要想执行必须期待get()办法的完结
// 而get()办法可能须要很久。因为get()办法是为了拿另一个线程的执行后果
// 另一个线程执行是须要工夫的。
System.out.println("hello world!");
}
}
//实现Callable接口
class CallableImpl implements Callable{
&#64;Override
public Object call() throws Exception { // call()办法就相当于run办法。只不过这个有返回值
// 线程执行一个工作&#xff0c;执行之后可能会有一个执行后果
// 模仿执行
System.out.println("call method begin");
Thread.sleep(1000 * 10);//以后线程睡眠10秒
System.out.println("call method end!");
int a &#61; 100;
int b &#61; 200;
return a &#43; b; //主动装箱(300后果变成Integer)
}
}
【执行后果】
call method begin
call method end!
线程执行后果:300
hello world!
最初
谢谢你的观看&#xff0c;感觉文章对你有帮忙的话记得关注我点个赞反对一下&#xff01;也欢送大家关注我的公众号&#xff1a;前程有光&#xff0c;金三银四跳槽面试季&#xff0c;整顿了1000多道将近500多页pdf文档的Java面试题材料&#xff0c;每天都会分享java相干技术文章或行业资讯&#xff0c;整顿的材料也会放在外面。