多线程,线程的创建和启动,Thread、Runnable、Callable的用法

编程入门 行业动态 更新时间:2024-10-28 12:19:17

多<a href=https://www.elefans.com/category/jswz/34/1771240.html style=线程,线程的创建和启动,Thread、Runnable、Callable的用法"/>

多线程,线程的创建和启动,Thread、Runnable、Callable的用法

文章目录

  • 前言
  • 一、线程和进程的定义
  • 二、进程和线程的关系
  • 三、线程的创建和启动
    • 3.1 继承java.lang.Thread
    • 3.2 实现java.lang.Runnable接口
    • 3.3 实现java.util.concurrent.Callable接口
  • 四、Runnable和Callable对比
    • 4.1 相同点:
    • 4.2 不同点:
  • 五、Runnable和Callable与Thread比较
    • 5.1 使用Runnable和Callable的优势
    • 5.2 使用Runnable和Callable的劣势
    • 5.3 使用Thread的劣势
  • 拓展思考


前言

大部分时候我们都作者单线程编程,程序都只有一条顺序执行流:从main方法开始执行,一次向下执行每行代码。

但实际情况是,单线程的程序往往功能非常有限,例如我们要开发一个煎蛋的服务器程序,这个服务器程序需要向不同的客户端提供服务,不同客户端之间互不干扰,这就需要多线程。

多线程程序可以包括多个顺序执行流,多个顺序流之间互不干扰。Java提供了非常优秀的多线程支持,程序可以通过非常简单的方式来启动多线程。


一、线程和进程的定义

进程就是一个应用程序(一个进程是一个软件)
线程是一个进程中的执行场景/执行单元
一个进程可以启动多个线程

对于java程序来说,当在DOS命令窗口中输入:
java HelloWorld 回车之后,会先启动JVM,而JVM就是一个进程。
JVM再启动一个主线程调用main方法,同时再启动一个垃圾回收线程负责看护,回收垃圾。
最起码,现在的java程序中至少有两个线程并发,一个是垃圾回收线程,一个是执行main方法的主线程。

举个例子:

	阿里巴巴:进程马云:阿里巴巴的一个线程童文红:阿里巴巴的一个线程京东:进程强东:京东的一个线程奶茶:京东的一个线程进程可以看做是现实生活当中的公司。线程可以看做是公司当中的某个员工。

二、进程和线程的关系

进程A和进程B之间的内存独立不共享,就像阿里巴巴和京东是两个独立的公司,资源不会共享。QQ一个进程,微信是一个进程,这两个进程是独立的,不共享资源。

但是线程不一样。

同一个进程中的线程A和线程B堆内存和方法区内存共享,栈内存独立,一个线程一个栈。
假设启动10个线程,就会有10个栈空间,每个栈之间互不干扰,各自执行各自的,这就是多线程开发。

多线程的例子:

		火车站,可以看做是一个进程。火车站中的每一个售票窗口可以看做是一个线程。我在窗口1购票,你可以在窗口2购票,你不需要等我,我也不需要等你。所以多线程并发可以提高效率。java中之所以有多线程机制,目的就是为了提高程序的处理效率。

三、线程的创建和启动

Java支持多线程机制。并且java已经将多线程实现了,我们只需要继承就行了。

3.1 继承java.lang.Thread

通过继承Thread类来创建并启动多线程步骤:
(1)定义一个类继承Thread,并重写run()方法,该run()方法的方法体代表了该线程需要完成的任务。把run()方法称为线程执行体;
(2)创建Thread子类MyThread的实例,即创建了线程对象;
(3)调用线程对象的start()方法来启动该线程。

public class MyThread  extends Thread {private int i;@Overridepublic void run() {for ( ; i < 100; i++) {System.out.println(getName() + " " + i);}}public static void main(String[] args) {for (int i = 0; i < i; i++) {//调用Thread的currentThread().getName()方法获取当前线程System.out.println(Thread.currentThread().getName() + " " + i);}MyThread myThread1 = new MyThread();myThread1.start();MyThread myThread2 = new MyThread();myThread2.start();}
}

运行结果:

3.2 实现java.lang.Runnable接口

通过实现Runnable接口来创建并启动多线程步骤:
(1)定义一个类实现Runnable接口,并重写run()方法,该run()方法的方法体代表了该线程需要完成的任务。把run()方法称为线程执行体;
(2)创建Runnable实现类MyRunnable 的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象;
(3)调用线程对象的start()方法来启动该线程。

public class MyRunnable implements Runnable {private int i;@Overridepublic void run() {for ( ; i < 100; i++) {//与继承Thread类不同的事,这里不能用getName()方法来获取当前线程//System.out.println(getName() + " " + i);//只能用Thread.currentThread().getName()来获取当前线程System.out.print(Thread.currentThread().getName() + " " + i +";");}}public static void main(String[] args) {for (int i = 0; i < i; i++) {System.out.println(Thread.currentThread().getName() + " " + i);}MyRunnable myRunnable = new MyRunnable();//此处创建线程的时候取了名字new Thread(myRunnable,"A线程").start();new Thread(myRunnable,"B线程").start();}
}

运行结果:

3.3 实现java.util.concurrent.Callable接口

从Java5开始,Java提供了Callable接口,该接口就像是Runnable接口的增强版,Callable接口提供了一个call()方法可以作为线程执行体,但call()方法比run()方法功能更强大。

利用Callable创建并启动线程步骤:
(1)创建Callable接口的实现类,并实现call()方法,该call()方法作为线程的执行体,且该call()方法有返回值;
(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值;
(3)使用FutureTask对象作为Thread对象的target创建并启动新线程;
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。

public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int i = 0;for ( ; i < 100; i++) {System.out.println(Thread.currentThread().getName() + "的循环变量i的值:"+i );}return i;}public static void main(String[] args) {MyCallable myCallable = new MyCallable();FutureTask<Integer> task = new FutureTask<>(myCallable);for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()  + "的循环变量i的值:"+i);new Thread(task).start();}try {System.out.println("子线程的返回值:"+task.get());} catch (Exception e) {e.printStackTrace();}}
}

运行结果:

四、Runnable和Callable对比

4.1 相同点:

(1)都是接口;
(2)都可以编写多线程程序;
(3)都采用Thread.start()启动线程。

4.2 不同点:

(1)Runnable没有返回值;Callable可以返回执行结果;
(2)Callable接口的call()允许抛出异常;Runnable的run()不能抛出。

二者实现方式基本相同,只是Callable接口里定义的方法有返回值,可以声明抛出异常而已,因此将二者归为一种方式。

五、Runnable和Callable与Thread比较

5.1 使用Runnable和Callable的优势

(1)线程类只是实现了接口,还可以继承其他类;
(2)多个线程可以共享一个target,非常适合多个相同的线程处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现面向对象的思想;
(3)线程池技术,接受Runnable,不接受Thread。

5.2 使用Runnable和Callable的劣势

(1)编程稍稍复杂,如果需要访问当前程序,必须使用Thread.currentThread()方法;

5.3 使用Thread的劣势

(1)线程继承了Thread类,不能继承其他类;

鉴于上面的分析,一般推荐采用实现Runnable和Callable接口方式来创建多线程。


拓展思考

思考题1:使用多线程后,main方法结束,是不是程序就结束了?

	不是。main方法结束只是主线程结束了,主栈空了,但是其它的栈(线程)可能还在压栈弹栈。

思考题2:对于单核的CPU来说,真的可以做到真正的多线程并发吗?

	对于多核的CPU电脑来说,真正的多线程并发是没问题的。4核CPU表示同一个时间点上,可以真正的有4个进程并发执行。什么是真正的多线程并发?t1线程执行t1的。t2线程执行t2的。t1不会影响t2,t2也不会影响t1。这叫做真正的多线程并发。单核的CPU表示只有一个大脑:不能够做到真正的多线程并发,但是可以做到给人一种“多线程并发”的感觉。对于单核的CPU来说,在某一个时间点上实际上只能处理一件事情,但是由于CPU的处理速度极快,多个线程之间频繁切换执行,跟人来的感觉是:多个事情同时在做!!!!!线程A:播放音乐线程B:运行魔兽游戏线程A和线程B频繁切换执行,人类会感觉音乐一直在播放,游戏一直在运行,给我们的感觉是同时并发的。电影院采用胶卷播放电影,一个胶卷一个胶卷播放速度达到一定程度之后,人类的眼睛产生了错觉,感觉是动画的。这说明人类的反应速度很慢,就像一根钢针扎到手上,到最终感觉到疼,这个过程是需要“很长的”时间的,在这个期间计算机可以进行亿万次的循环。所以计算机的执行速度很快。

更多推荐

多线程,线程的创建和启动,Thread、Runnable、Callable的用法

本文发布于:2024-03-13 11:01:28,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1733842.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:线程   多线程   Thread   Runnable   Callable

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!