这篇文章主要讲解了“多线程编程的三种实现方式是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“多线程编程的三种实现方式是什么”吧!
进程: 是程序的一次动态执行过程,它经历了从代码加载、执行到执行完毕的一个完整过程,这个过程就是进程产生、发展到最终消亡的过程;
多进程: 操作系统能同时运行多个进程(程序),由于CPU
具备分时机制,在每个进程都能循环获得自己的CPU时间片;由于CPU执行的速度非常快,使得所有的程序好像是在同时运行一样。
进程是资源调度的基本单位,运行一个可执行程序会创建一个或多个线程,进程就是运行起来的可执行程序;
线程是程序执行的基本单位,是轻量级的进程,每个进程中都有唯一的主线程,且只能有一个,主线程和进程是相互依存的关系,主线程结束,进程也会结束。
具体实例(word):
每次启动Word对于操作系统而言就相当于启动了一个系统的进程,而在这个进程之上又有许多其他程序在运行(拼写检查等),那么对于这些程序就是一个个多线程。如果Word关闭了,则这些拼写检查的线程也肯定会消失,但是如果拼写检查的线程消失了,并不一定会让Word的进程消失;
多插一句:如果打开两个word文档,则表示当前操作系统创建了两个进程。
实现多线程需要一个线程的主体类,这个类可以继承Thread
、实现Runnable
以及Callable
接口完成定义;
继承结构如下:
public class Thread extends Object implements Runnable
实现接口Runnable
,所以必须实现接口中的抽象方法:
Modifier and Type | Method | Description |
---|---|---|
void | run() | 当一个实现接口Runnable的对象被用来创建线程时,启动线程会导致对象的run方法在单独执行的线程中被调用。 |
void | start() | 使线程开始执行;Java虚拟机调用这个线程的run方法。 |
当产生多个对象时,这些对象就会并发的执行run()方法中的代码;
虽然多线程的执行方法都在run()方法中定义,但是在实际进行多线程启动时并不能直接调用此方法,由于多线程时需要并发执行的,所以需要通过操作系统的资源调度才能执行,这样多线程的启动就必须利用Thread类中的start()方法完成,调用此方法会间接的调用run()方法。
实例:
package Java从入门到项目实战.多线程编程.Java多线程实现; class MyThread extends Thread{ //单继承 private String title; public MyThread(String title){ this.title = title; } //覆写线程的run方法 @Override public void run() { for (int i = 0 ; i < 10; i++){ System.out.println(this.title+"运行,i =" +i); } } } public class Main{ public static void main(String[] args){ new MyThread("线程A").start(); //实例化线程对象并启动 new MyThread("线程B").start(); new MyThread("线程C").start(); //对照 /*没有开启多线程*/ new MyThread("线程A").run(); new MyThread("线程B").run(); new MyThread("线程C").run(); } }
由效果图可以看出,三个线程在交替执行:
假如面试题:
为什么线程启动的时候必须调用start()方法而不是直接调用run()方法?
在本程序中,程序调用了Thread类继承而来的start()方法后,实际上他执行的还是覆写后的run()方法,那为什么不直接调用run()?
简单的说下:是因为多线程需要调用操作系统的资源,在start()下有一个关键的部分start0()方法,并且在start0()方法上使用了navite关键字定义;
public synchronized void start() { if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { } } } private native void start0(); //navite
什么是navite?
navite是指:Java本机接口(Java Native Interface)简称:JNI;使用Java调用本机操作系统的函数功能完成一些特殊操作;
在Java中将start0()方法体交给JVM进行实现,所以这样就会出现在windows或者在Linux中实现的start0()的是不同,不关系过程,只关心结果(是否调用了本机的操作系统的函数);
start0()作用:交由JVM进行匹配不同的操作系统,实现start0()方法体,功能:实现本机函数的调用;
具体百度、Google吧。
出现的原因:为了解决Thread实现多线程出现的单继承问题;并且增加了函数式接口;
Modifier and Type | Method | Description |
---|---|---|
void | run() | 当一个实现接口Runnable的对象被用来创建线程时,启动线程会导致对象的run方法在单独执行的线程中被调用。 |
实现代码:
class MyThread implements Runnable{ private String title; public MyThread(String title){ this.title = title; } @Override public void run() { //线程方法覆写 for (int i = 0; i< 10;i++){ System.out.println(this.title+"运行,i"+i); } } }
启动方式一:
Thread threadA = new Thread(new MyThread("线程A")); Thread threadB = new Thread(new MyThread("线程B")); Thread threadC = new Thread(new MyThread("线程C")); Thread threadD = new Thread(new MyThread("线程D")); Thread threadE = new Thread(new MyThread("线程E")); threadA.start(); threadB.start(); threadC.start(); threadD.start(); threadE.start();
启动方式二:
//通过Lambal表达式定义线程主体 for(int x = 0; x < 3;x++){ String title = "线程对象-"+x; //实际上Thread传入的类型是Runnable new Thread(()->{ //Lambda实现线程主体 for(int y = 0; y < 20; y++){ System.out.println(title+"运行,y"+y); } }).start(); }
Thread与Runnable的联系:
继承结构:
public class Thread extends Object implements Runnable
在之前继承Thread类的时候实际上覆写的还是Runnable接口的run()方法。
实现并发访问资源:
package Java从入门到项目实战.多线程编程.Java多线程实现; class MyThreadConcurrent implements Runnable { private int ticket = 5; @Override public void run() { for (int i = 0; i < 100; i++) { //同步操作--》从5-1票数 /*synchronized(this){ if(this.ticket > 0){ System.out.println("卖票,ticket = "+this.ticket--); } }*/ //票数乱数 if(this.ticket > 0){ System.out.println("卖票,ticket = "+this.ticket--); } } } } public class 并发资源访问 { public static void main(String[] args) { MyThreadConcurrent thread = new MyThreadConcurrent(); new Thread(thread).start(); //第一个线程 new Thread(thread).start(); //第二个线程 new Thread(thread).start(); //第三个线程 } }
总结一句话:Thread有单继承的局限性以及在有些情况下结构的不合理性;所以后面多线程的实现使用的都是Runnable接口。
为什么要使用Callable接口来实现多线程?
因为使用Callable接口实现弥补了Runnable实现多线程没有返回值的问题。
继承结构如下:
@FunctionalInterface public interface Callable<V>{ public V call() throws Exception{ } }
定义的时候可以设置一个泛型,此泛型的类型就是call()方法的返回的数据类型,好处:可以避免向下转型的安全隐患。
线程类主体完成后,需要启动多线程的话还是需要通过Thread类实现的,又因为我们的Callable接口与Thread没有联系,所以我们需要FutureTask类实现两者之间的联系;如图所示:
通过FutureTask类继承结构可以发现它是Runnable接口的子类;
代码实现如下:
package Java从入门到项目实战.多线程编程.Java多线程实现; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; class CallableThread implements Callable<String> { @Override public String call() throws Exception { for (int i = 0; i < 10; i++) { System.out.println("线程执行 x = "+i); } return "xbhog"; } } public class Callable接口实现多线程 { public static void main(String[] args) throws ExecutionException, InterruptedException { //将Callable实例化包装在FutureTask类中,这样就可以与Runnable接口关联 FutureTask<String> task = new FutureTask<String>(new CallableThread()); //线程启动 new Thread(task).start(); //获取call()的返回值 System.out.println("【线程返回数据】:"+task.get()); } }
感谢各位的阅读,以上就是“多线程编程的三种实现方式是什么”的内容了,经过本文的学习后,相信大家对多线程编程的三种实现方式是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。