首页 技术 正文
技术 2022年11月15日
0 收藏 699 点赞 3,168 浏览 2838 个字

1.线程创建的方式常用有两种

1. 继承 Thread 类创建线程
2. 实现 Runnable 接口创建线程

2.Thread 和 Runnable的区别

Thread和Runnable的相同点:都是"多线程的实现方式”。
Thread和Runnable的不同点:Thread 是类,而Runnable是接口;Thread本身是实现了 Runnable 接口的类。我们知道“一个类只能有一个父类,但是却能实现多个接口”,因此Runnable具有更好的扩展性。此外,Runnable还可以用于“资源的共享”。即,多个线程都是基于某一个Runnable对象建立的,它们会共享Runnable对象上的资源。通常,建议通过“Runnable”实现多线程!

3.Thread和Runnable的多线程示例(暂不考虑数据竞态)

继承Thread创建步骤
1. 定义Thread类的子类,并重写该类的run()方法,该方法为线程执行体
2. 创建Thread子类的实例,也就是创建了线程对象
3. 启动线程,通过调用线程对象的start()方法来启动线程
Thread创建代码
package concurrent;import java.util.concurrent.atomic.AtomicInteger;/**
* @Description: 线程创建
* @Author: lizhouwei
* @CreateDate: 2018/5/20 16:17
* @Modify by:
* @ModifyDate:
*/
class MyThread extends Thread {
private AtomicInteger ticket = new AtomicInteger(10); @Override
public void run() {
for (int i = 1; i <= ticket.intValue(); i++) {
System.out.println(Thread.currentThread().getName() + " ticket " + i);
}
}
}public class ThreadDmo {
public static void main(String[] args) {
MyThread myThread1 = new MyThread();
myThread1.start();
}
}
实现Runnable创建步骤
1. 定义Runnable接口的实现类,重写run()方法,该方法为线程执行体
2. 创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象
3. 启动线程,通过调用线程对象的start()方法来启动线程
Runnable创建代码
 package concurrent;import java.util.concurrent.atomic.AtomicInteger;/**
* @Description: 线程创建
* @Author: lizhouwei
* @CreateDate: 2018/5/20 16:17
* @Modify by:
* @ModifyDate:
*/
class MyRunnable implements Runnable {
private AtomicInteger ticket = new AtomicInteger(10); public void run() {
for (int i = 1; i <= ticket.intValue(); i++) {
System.out.println(Thread.currentThread().getName() + " ticket " + i);
}
}
}public class RunnableDmo {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread myThread = new Thread(myRunnable);
myThread.start();
}
}

4. start() 和 run()的区别

start():启动一个新线程,新线程会执行相应的run()方法。start()不能被重复调用(会抛出异常)。
run() :run()就和普通的成员方法一样,可以被重复调用。单独调用run()的话,会在当前线程中执行run(),而并不会启动新线程!

2. start() 源码(by jdk1.8)

public synchronized void start() {         //判断状态是否为 New ;
if (threadStatus != 0)
throw new IllegalThreadStateException(); // 将线程添加到ThreadGroup中
group.add(this); boolean started = false;
try {
// 通过start0()启动线程
start0();
// 设置started标记
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}

5. 通过Thread的实例对象调用start()方法到底是怎么启动线程的?下面对其实现方式做一些简单的补充。

  1. 基于Kernel Thread(KLT)的映射来实现:KLT是内核线程,内核线程由OS直接完成调度切换,它相对应用程序的线程来讲只是一个接口,外部程序会使用一种轻量级进程(Light Weight Process,LWP)来与KLT进行一对一的接口调用。也就是说,进程内部会尝试利用OS的内核线程去参与实际的调度,而自己使用API调用作为中间桥梁与自己的程序进行交互。
  2. 基于用户线程(User Thread,UT)的实现:这种方式是考虑是否可以没有中间这一层映射,自己的线程直接由CPU来调度,或许理论上效率会更高。不过这样实现时,用户进程所需要关注的抽象层次会更低一些,跳过OS更加接近CPU,即自己要去做许多OS做的事情,自然的OS的调度算法、创建、销毁、上下文切换、挂起等都要自己来搞定(因为CPU只做计算)。这样做显然很麻烦,许多人曾经尝试过,后来放弃了。
  3. 混合实现方式:它的设计理念是希望保留Kernel线程原有架构,又想使用用户线程,轻量级进程依然与Kernel线程一一对应保持不变,唯一变化的就是轻量级进程不再与进程直接挂钩,而是与用户线程挂钩,用户线程并不一定必须与轻量级进程一一对应,而是多对多,就像在使用一个轻量级进程列表一样,这样增加了一层来解除轻量级进程与原进程之间的耦合,可能会使得调度更为灵活。

在以前的JDK版本中,尝试使用UT的方式来实现,但后来放弃了,采用了与Kernel线程对应的方式,至于一些细节,与具体的平台有很大的关系,JVM会适当考虑具体平台的因素去实现,在JVM规范中也没规定过必须如何去实现,所以对于程序员来讲,只需要知道在new Thread(),调用start()方法后,理论上就有一个可以被OS调度的线程了。

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,088
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,564
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,412
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,185
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,821
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,905