您的位置:首页 > 编程语言 > Java开发

Java中线程实现的两种方法

2017-01-31 12:54 337 查看

Java中线程实现的两种方法

进程与线程

进程,是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竟争计算机系统资源的基本单位。每一个进程都有一个自己的地址空间,即进程空间或(虚空间)。进程空间的大小 只与处理机的位数有关,一个 16 位长处理机的进程空间大小为 216 ,而 32 位处理机的进程空间大小为 232 。进程至少有 5 种基本状态,它们是:初始态,执行态,等待状态,就绪状态,终止状态。

线程,在网络或多用户环境下,一个服务器通常需要接收大量且不确定数量用户的并发请求,为每一个请求都创建一个进程显然是行不通的,——无论是从系统资源开销方面或是响应用户请求的效率方面来看。因此,操作系统中线程的概念便被引进了。线程,是进程的一部分,一个没有线程的进程可以被看作是单线程的。线程有时又被称为轻权进程或轻量级进程,也是 CPU 调度的一个基本单位。

自己总结一下。进程是操作系统分配计算资源的基本单位,通常一个应用程序就是一个进程。进程拥有自己的进程空间,与其他进程互相独立。线程是CPU调度的基本单位,它是进程的一部分,能够使用系统分配给进程的资源来进行任务处理。一个进程必须拥有至少一个线程来运行。

进程与线程的区别与联系

进程的执行过程是线状的,尽管中间会发生中断或暂停,但该进程所拥有的资源只为该线状执行过程服务。一旦发生进程上下文切换,这些资源都是要被保护起来的。这是进程宏观上的执行过程。而进程又可有单线程进程与多线程进程两种。我们知道,进程有 一个进程控制块 PCB ,相关程序段 和 该程序段对其进行操作的数据结构集 这三部分,单线程进程的执行过程在宏观上是线性的,微观上也只有单一的执行过程;而多线程进程在宏观上的执行过程同样为线性的,但微观上却可以有多个执行操作(线程),如不同代码片段以及相关的数据结构集。线程的改变只代表了 CPU 执行过程的改变,而没有发生进程所拥有的资源变化。出了 CPU 之外,计算机内的软硬件资源的分配与线程无关,线程只能共享它所属进程的资源。与进程控制表和 PCB 相似,每个线程也有自己的线程控制表 TCB ,而这个 TCB 中所保存的线程状态信息则要比 PCB 表少得多,这些信息主要是相关指针用堆栈(系统栈和用户栈),寄存器中的状态数据。进程拥有一个完整的虚
4000
拟地址空间,不依赖于线程而独立存在;反之,线程是进程的一部分,没有自己的地址空间,与进程内的其他线程一起共享分配给该进程的所有资源。

线程可以有效地提高系统的执行效率,但并不是在所有计算机系统中都是适用的,如某些很少做进程调度和切换的实时系统。使用线程的好处是有多个任务需要处理机处理时,减少处理机的切换时间;而且,线程的创建和结束所需要的系统开销也比进程的创建和结束要小得多。最适用使用线程的系统是多处理机系统和网络系统或分布式系统。

自己总结一下,进程的执行过程是线性的,如果发生进程间的上下文切换,进程的资源必须被妥善保护起来。一个进程中的各线程之间的切换本质上来说是执行顺序的改变,并不改变除CPU以外的计算资源,因此线程的切换要比进程的切换快得多。进程有自己独立的进程空间,线程并没有。

多线程运行原理

线程本质上不是同时执行的,CPU的运行时间非常快,它能够非常迅速的在各个线程之间切换,轮流执行各线程。由于速度很快,看起来各线程就是轮流执行的了。

Java中实现线程的两种方式

目前Java中有两种实现线程的方式,一种是继承java.lang.Thread类,一种是实现java.lang.Runnable接口。本质上都是在写run方法。

继承Thread方法实现的例子

下面给出继承方法实现的最简单的例子:

package com.mrbcy.bigdata.basic.thread.testthread;

public class MyThreadWithExtends extends Thread{

@Override
public void run() {
// TODO Auto-generated method stub
super.run();
System.out.println("线程run方法被调用");
}

public static void main(String[] args) {
Thread thread = new MyThreadWithExtends();
thread.start();
}
}


Thread.start和Thread.run的区别

似乎start()方法和run()方法都能够执行run方法里面的业务逻辑,那么我们是否可以通过类似
thread.run()
这样的代码来启动线程呢?下面来看一个例子。

首先,我们用start()方法来启动两个线程。代码如下:

package com.mrbcy.bigdata.basic.thread.testthread;

public class MyThreadWithExtends extends Thread{
private String threadName;

public MyThreadWithExtends(String threadName) {
super();
this.threadName = threadName;
}

@Override
public void run() {
// TODO Auto-generated method stub
super.run();
for(int i = 0; i < 10; i ++){
System.out.println("线程"+threadName+" run方法被调用");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

public static void main(String[] args) {
Thread thread1 = new MyThreadWithExtends("a");
Thread thread2 = new MyThreadWithExtends("b");
thread1.start();
thread2.start();
}
}


下面是代码的输出:

线程a run方法被调用
线程b run方法被调用
线程a run方法被调用
线程b run方法被调用
线程b run方法被调用
线程a run方法被调用
线程b run方法被调用
线程a run方法被调用
线程b run方法被调用
线程a run方法被调用
线程b run方法被调用
线程a run方法被调用
线程b run方法被调用
线程a run方法被调用
线程a run方法被调用
线程b run方法被调用
线程b run方法被调用
线程a run方法被调用
线程a run方法被调用
线程b run方法被调用


可以看出a b线程的输出是交错的,也就是说我们可以认为它们是“并行”执行的。

然后把启动a b线程的代码换成下面这样:

public static void main(String[] args) {
Thread thread1 = new MyThreadWithExtends("a");
Thread thread2 = new MyThreadWithExtends("b");
//thread1.start();
//thread2.start();

thread1.run();
thread2.run();
}


输出变成了下面这样:

线程a run方法被调用
线程a run方法被调用
线程a run方法被调用
线程a run方法被调用
线程a run方法被调用
线程a run方法被调用
线程a run方法被调用
线程a run方法被调用
线程a run方法被调用
线程a run方法被调用
线程b run方法被调用
线程b run方法被调用
线程b run方法被调用
线程b run方法被调用
线程b run方法被调用
线程b run方法被调用
线程b run方法被调用
线程b run方法被调用
线程b run方法被调用
线程b run方法被调用


可以看出此时代码是严格顺序执行的,这样我们就失去了使用多线程的意义。实际上,此时的两段代码都是在主线程中执行的,只是调用了它们的run方法而已。

实现Runable接口实现的例子

下面给出继承Runable接口实现线程的例子最简单的代码:

package com.mrbcy.bigdata.basic.thread.testthread;

public class MyThreadWithRunnable implements Runnable{

@Override
public void run() {
System.out.println("Runnable的run方法执行了");
}

public static void main(String[] args) {
new Thread(new MyThreadWithRunnable()).start();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: