- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章浅析 Java多线程由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
什么是进程 。
当一个程序进入内存中运行起来它就变为一个进程。因此,进程就是一个处于运行状态的程序。同时进程具有独立功能,进程是操作系统进行资源分配和调度的独立单位.
什么是线程 。
线程是进程的组成部分。通常情况下,一个进程可拥有多个线程,而一个线程只能拥有一个父进程.
线程可以拥有自己的堆栈、自己的程序计数器及自己的局部变量,但是线程不能拥有系统资源,它与其父进程的其他线程共享进程中的全部资源,这其中包括进程的代码段、数据段、堆空间以及一些进程级的资源(例如,打开的文件等).
线程是进程的执行单元,是CPU调度和分派的基本单位,当进程被初始化之后,主线程就会被创建。同时如果有需要,还可以在程序执行过程中创建出其他线程,这些线程之间也是相互独立的,并且在同一进程中并发执行。因此一个进程中可以包含多个线程,但是至少要包含一个线程,即主线程.
Java中的线程 。
Java 中使用Thread类表示一个线程。所有的线程对象都必须是Thread或其子类的对象。Thread 类中的 run 方法是该线程的执行代码。让我们来看一个实例:
1
2
3
4
5
6
7
8
|
public
class
Ticket
extends
Thread{
// 重写run方法
public
void
run() {
for
(
int
i =
0
; i <
20
; i++) {
System.out.println(getName() +
": "
+ i);
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
|
public
class
TestThread {
public
static
void
main(String[] args) {
// 1.创建线程
Thread thread1 =
new
Ticket();
Thread thread2 =
new
Ticket();
// 2.启动线程
thread1.start();
thread2.start();
}
}
|
运行结果如下:
通过上面的代码和运行结果,我们可以得到
线程运行的几个特点 。
1.同一进程下不同线程的调度不由程序控制。线程的执行是抢占式的,运行的顺序和线程的启动顺序是无关的,当前运行的线程随时都可能被挂起,然后其他进程抢占运行.
2.线程独享自己的堆栈程序计数器和局部变量。两个进程的局部变量互不干扰,各自的执行顺序也是互不干扰.
3.两个线程并发执行。两个线程同时向前推进,并没有说执行完一个后再执行另一个.
start()方法和run()方法 。
启动一个线程必须调用Thread 类的 start()方法,使该线程处于就绪状态,这样该线程就可以被处理器调度.
run()方法是一个线程所关联的执行代码,无论是派生自 Thread类的线程类,还是实现Runnable接口的类,都必须实现run()方法,run()方法里是我们需要线程所执行的代码.
实现多线程必须调用Thread 类的 start()方法来启动线程,使线程处于就绪状态随时供CPU调度。如果直接调用run()方法的话,只是调用了Thread类的一个普通方法,会立即执行该方法中的代码,并没有实现多线程技术.
Java中多线程的实现方法 。
在Java中有三种方法实现多线程.
第一种方法:使用Thread类或者使用一个派生自Thread 类的类构建一个线程.
第二种方法:实现Runnable 接口来构建一个线程。(推荐使用) 。
第三种方法:实现Callable 接口来构建一个线程。(有返回值) 。
第一种方法 。
使用Thread类或者使用一个派生自Thread 类的类构建一个线程.
1
2
3
4
5
6
7
8
|
public
class
Ticket
extends
Thread{
// 重写run方法
public
void
run() {
for
(
int
i =
0
; i <
20
; i++) {
System.out.println(getName() +
": "
+ i);
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
|
public
class
TestThread {
public
static
void
main(String[] args) {
// 1.创建线程
Thread thread1 =
new
Ticket();
Thread thread2 =
new
Ticket();
// 2.启动线程
thread1.start();
thread2.start();
}
}
|
看上面的代码,我们创建了一个Ticket类,它继承了Thread类,重写了Thread类的run方法。然后我们用Ticket类创建了两个线程,并且启动了它们。但我们不推荐使用这种方法,因为一个类继承了Thread类,那它就没有办法继承其他类了,这对较为复杂的程序开发是不利的.
第二种方法 。
实现Runnable 接口来构建一个线程.
1
2
3
4
5
6
7
8
|
public
class
Ticket
implements
Runnable{
// 重写run方法
public
void
run() {
for
(
int
i =
0
; i <
20
; i++) {
System.out.println(Thread.currentThread().getName() +
": "
+ i);
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public
class
TestThread {
public
static
void
main(String[] args) {
// 1.创建线程
Ticket t1 =
new
Ticket();
Ticket t2 =
new
Ticket();
Thread thread1 =
new
Thread(t1,
"买票1号"
);
Thread thread2 =
new
Thread(t2,
"买票2号"
);
// 2.启动线程
thread1.start();
thread2.start();
}
}
|
我们创建了一个Ticket类,实现了Runnable接口,在该类中实现了run方法。在启动线程前,我们要创建一个线程对象,不同的是我们要将一个实现了Runnable接口的类的对象作为Thread类构造方法的参数传入,以构建线程对象。构造方法Thread的第二个参数用来指定该线程的名字,通过Thread.currentThread().getName()可获取当前线程的名字.
在真实的项目开发中,推荐使用实现Runnable接口的方法进行多线程编程。因为这样既可以实现一个线程的功能,又可以更好地复用其他类的属性和方法.
第三种方法 。
实现Callable 接口来构建一个线程.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public
class
TestThread {
public
static
void
main(String[] args) {
// 1.创建Callable的实例
Callable<String> callable =
new
Callable<String>() {
@Override
public
String call()
throws
Exception {
Thread.sleep(
7000
);
return
"我结束了"
;
}
};
// 2.通过FutureTask接口的实例包装Callable的实例
FutureTask<String> futureTask =
new
FutureTask<String>(callable);
// 3.创建线程并启动
new
Thread(futureTask).start();
// 4.获得结果并打印
try
{
System.out.println(futureTask.get());
}
catch
(Exception e) {
e.printStackTrace();
}
}
}
|
首先我们用匿名内部类创建了一个实现Callable接口的类的对象,然后通过FutureTask 的实例包装了Callable的实例,这样我们就可以通过一个Thread 对象在新线程中执行call()方法,同时又可以通过get方法获取到call()的返回值。然后创建线程并启动它,最后在线程执行完执行完call()方法后得到返回值并打印.
我们来看一下Callable的源码:
1
2
3
4
5
6
7
8
9
|
public
interface
Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call()
throws
Exception;
}
|
从Callable 的定义可以看出,Callable接口是一个泛型接口,它定义的call()方法类似于Runnable 的run()方法,是线程所关联的执行代码。但是与run()方法不同的是,call()方法具有返回值,并且泛型接口的参数V指定了call()方法的返回值类型。同时,如果call()方法得不到返回值将会抛出一个异常,而在Runnable的run()方法中不能抛出异常.
如何获得call()方法的返回值 。
通过Future接口来获取。Future接口定义了一组对 Runnable 或者Callable 任务的执行结果进行取消、查询、获取、设置的操作。其中get方法用于获取call()的返回值,它会发生阻塞,直到call()返回结果.
这样的线程调用与直接同步调用函数有什么差异 。
在上面的例子中,通过future.get()获取 call()的返回值时,由于call方法中会 sleep 7s,所以在执行future.get()的时候主线程会被阻塞而什么都不做,等待call()执行完并得到返回值。但是这与直接调用函数获取返回值还是有本质区别的.
因为call()方法是运行在其他线程里的,在这个过程中主线程并没有被阻塞,还是可以做其他事情的,除非执行future.get()去获取 call()的返回值时主线程才会被阻塞。所以当调用了Thread.start()方法启动 Callable 线程后主线程可以执行别的工作,当需要call()的返回值时再去调用future.get()获取,此时call()方法可能早已执行完毕,这样就可以既确保耗时操作在工作线程中完成而不阻挡主线程,又可以得到线程执行结果的返回值。而直接调用函数获取返回值是一个同步操作,该函数本身就是运行在主线程中,所以一旦函数中有耗时操作,必然会阻挡主线程.
以上就是浅析 Java多线程的详细内容,更多关于Java多线程的资料请关注我其它相关文章! 。
原文链接:https://www.cnblogs.com/ruoli-0/p/13726616.html 。
最后此篇关于浅析 Java多线程的文章就讲到这里了,如果你想了解更多关于浅析 Java多线程的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
很多朋友或许都有这个疑问,一个网站域名和网站的排名有关系吗?今天本文就从三个方面分析网站的域名与网站的排名有没有关系,希望对大家有一定的帮助。 1、全拼双拼域名 首先,我们要知道在这一点百
什么是进程 当一个程序进入内存中运行起来它就变为一个进程。因此,进程就是一个处于运行状态的程序。同时进程具有独立功能,进程是操作系统进行资源分配和调度的独立单位。 什么是线程 线程是进
最近几年,互联网络竞争异常激烈,各个企业为了增加业绩,都在网络销售中下足了功夫。要确定网站发展的方向,必须给自己的网站制定好一个发展目标,有了目标才能更好的发展。不管
一.基础知识准备: 1.层的原则: (1)每一层以接口方式供上层调用。 (2)上层只能调用下层。 (3)依赖分为松散交互和严格交互两种。 2.业务逻辑分类: (1)应
编程时一门技术,更是一门艺术 简单工厂模式利用面向对象方式通过继承、封装、多态把程序的耦合度降低,设计模式使得程序更加灵活,容易修改,易于复用。 下面是服务器计算器代码:
对于策略模式的理解:当一个业务有多种需求时候,在某个时候需要使用不同的方式来计算结果。这时候不同的方式可以理解为不同的策略来解决同样的问题。 例如:商场收银系统计算价格,1:正常计算 2:商品打折计
随着 Kubernetes 在企业中应用的越来越广泛和普及,越来越多的公司在生产环境中运维多个集群。本文主要讲述一些关于多集群 Kubernetes 的思考,包括为什么选择多集群,多集群的好处以
Kubelet 出于对节点的保护,允许在节点资源不足的情况下,开启对节点上 Pod 进行驱逐的功能。最近对 Kubelet 的驱逐机制有所研究,发现其中有很多值得学习的地方,总结下来
以下分析不针对任何快递公司,纯属实说。 申通快递在快递行业中速度与费用都属于中等的水平,在国内也分布有很多投递点,一般地区都可以投递到;顺丰在国内是速度最快的快递公司之一,一般来说隔天就能够到,其
概述 流(streams)是PHP4.3版本引入的一个特性,主要是为了统一文件、sockets以及其他类似资源的工作方法。PHP4.3距今已经有很长时间了,但是很多程序员似乎都不能正确使用PHP中
Pre 很早在看 Jesse 的 Asp.net Core快速入门 的课程的时候就了解到了在Asp .net core中,如果添加的Json配置被更改了,是支持自动重载配置的,作为一名有着严重&q
之前对closure一知半解,在网上也找不到一篇文章能把它说清楚,今天好像第一次对它有点清晰的了解 了,写个BLOG记念一下 lua的函数是一种 First-Class Value 的东西, 到底
1、什么是默认方法,为什么要有默认方法 简单说,就是接口可以有实现方法,而且不需要实现类去实现其方法。只需在方法名前面加个default关键字即可。 为什么要有这个特性?首先,之前的接口是个双
信息数据传输的安全一直都是个很重要的话题,从刚开始当程序员时错以为MD5、SHA1这些哈希算法就是加密算法,到后来慢慢接触对称加密、非对称加密这些概念,再到对接各种大开发平台接口的时候看到他们通
前言 2021年,vanilla-extract 作为黑马登顶了 css-in-js 满意度榜首(虽然使用率仅为1%),号称是一个类型安全、高度兼容 TS 场景的库,国内相关讨论还很少,稍微看
(一)响应式数据 1. 简单例子 从最简单的数据绑定开始,在 Vue 2.0 中,我们这样将一个数据绑定到模板的指定位置: 在组件创建参数的 data 构造函数中返回一个用来绑定的数据对象,其
我是一名优秀的程序员,十分优秀!