多线程原理
昨天的时候我们已经写过一版多线程的代码,很多同学对原理不是很清楚,那么我们今天先画个多线程执行时序图
来体现一下多线程程序的执行流程。
代码如下:
自定义线程类:
public
class
MyThread
extends
Thread
{
/*
*
利用继承中的特点
*
将线程名称传递 进行设置
*/
public
MyThread
(
String
name
){
super
(
name
);
}
/*
*
重写
run
方法
*
定义线程要执行的代码
*/
public
void run
(){
for
(
int i
=
0
;
i
<
20
;
i
++
) {
//getName()
方法 来自父亲
北京市昌平区建材城西路金燕龙办公楼一层 电话:400-618-9090
//getName()
方法 来自父亲
System
.
out
.
println
(
getName
()
+
i
);
}
}
}
测试类:
public class
Demo
{
public static
void
main
(
String
[]
args
) {
System
.
out
.
println
(
"
这里是
main
线程
"
);
MyThread mt
=
new
MyThread
(
"
小强
"
);
mt
.
start
();
//
开启了一个新的线程
for
(
int
i
=
0
;
i
<
20
;
i
++
) {
System
.
out
.
println
(
"
旺财
:"
+
i
);
}
}
}
流程图:
北京市昌平区建材城西路金燕龙办公楼一层 电话:400-618-9090
程序启动运行
main
时候,
java
虚拟机启动一个进程,主线程
main
在
main()
调用时候被创建。随着调用
mt
的对象的
start
方法,另外一个新的线程也启动了,这样,整个应用就在多线程下运行。
通过这张图我们可以很清晰的看到多线程的执行流程,那么为什么可以完成并发执行呢?我们再来讲一讲原理。
多线程执行时,到底在内存中是如何运行的呢?以上个程序为例,进行图解说明:
多线程执行时,在栈内存中,其实
每一个执行线程都有一片自己所属的栈内存空间
。进行方法的压栈和弹栈。
当执行线程的任务结束了,线程自动在栈内存中释放了。但是当所有的执行线程都结束了,那么进程就结束了。
1.2 Thread
类
在上一天内容中我们已经可以完成最基本的线程开启,那么在我们完成操作过程中用到了
java.lang.Thread
类,
API
中该类中定义了有关线程的一些方法,具体如下:
构造方法:
北京市昌平区建材城西路金燕龙办公楼一层 电话:400-618-9090
public Thread()
:
分配一个新的线程对象。
public Thread(String name)
:
分配一个指定名字的新的线程对象。
public Thread(Runnable target)
:
分配一个带有指定目标新的线程对象。
public Thread(Runnable target,String name)
:
分配一个带有指定目标新的线程对象并指定名字。
常用方法:
public String getName()
:
获取当前线程名称。
public void start()
:
导致此线程开始执行
; Java
虚拟机调用此线程的
run
方法。
public void run()
:
此线程要执行的任务在此处定义代码。
public static void sleep(long millis)
:
使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
public static Thread currentThread()
:
返回对当前正在执行的线程对象的引用。
翻阅
API
后得知创建线程的方式总共有两种,一种是继承
Thread
类方式,一种是实现
Runnable
接口方式
1.3
创建线程方式二
采用
java.lang.Runnable
也是非常常见的一种,我们只需要重写
run
方法即可。
步骤如下:
1.
定义
Runnable
接口的实现类,并重写该接口的
run()
方法,该
run()
方法的方法体同样是该线程的线程执行体。
2.
创建
Runnable
实现类的实例,并以此实例作为
Thread
的
target
来创建
Thread
对象,该
Thread
对象才是真正
的线程对象。
3.
调用线程对象的
start()
方法来启动线程。
代码如下:
public class
MyRunnable
implements
Runnable
{
@Override
public
void
run
() {
for
(
int
i
=
0
;
i
<
20
;
i
++
) {
System
.
out
.
println
(
Thread
.
currentThread
().
getName
()
+
" "
+
i
);
}
}
}
public class
Demo
{
public static
void
main
(
String
[]
args
) {
//
创建自定义类对象 线程任务对象
MyRunnable mr
=
new
MyRunnable
();
//
创建线程对象
Thread t
=
new
Thread
(
mr
,
"
小强
"
);
t
.
start
();
for
(
int
i
=
0
;
i
<
20
;
i
++
) {
System
.
out
.
println
(
"
旺财
"
+
i
);
}
}
}
北京市昌平区建材城西路金燕龙办公楼一层 电话:400-618-9090
通过实现
Runnable
接口,使得该类有了多线程类的特征。
run()
方法是多线程程序的一个执行目标。所有的多线程
代码都在
run
方法里面。
Thread
类实际上也是实现了
Runnable
接口的类。
在启动的多线程的时候,需要先通过
Thread
类的构造方法
Thread(Runnable target)
构造出对象,然后调用
Thread
对象的
start()
方法来运行多线程代码。
实际上所有的多线程代码都是通过运行
Thread
的
start()
方法来运行的。因此,不管是继承
Thread
类还是实现
Runnable
接口来实现多线程,最终还是通过
Thread
的对象的
API
来控制线程的,熟悉
Thread
类的
API
是进行多线程
编程的基础。
tips:Runnable
对象仅仅作为
Thread
对象的
target
,
Runnable
实现类里包含的
run()
方法仅作为线程执行体。
而实际的线程对象依然是
Thread
实例,只是该
Thread
线程负责执行其
target
的
run()
方法。
1.4 Thread
和
Runnable
的区别
如果一个类继承
Thread
,则不适合资源共享。但是如果实现了
Runable
接口的话,则很容易的实现资源共享。
总结:
实现
Runnable
接口比继承
Thread
类所具有的优势:
1.
适合多个相同的程序代码的线程去共享同一个资源。
2.
可以避免
java
中的单继承的局限性。
3.
增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
4.
线程池只能放入实现
Runable
或
Callable
类线程,不能直接放入继承
Thread
的类。
扩充:在
java
中,每次程序运行至少启动
2
个线程。一个是
main
线程,一个是垃圾收集线程。因为每当使用
java
命令执行一个类的时候,实际上都会启动一个
JVM
,每一个
JVM
其实在就是在操作系统中启动了一个进
程。
1.5
匿名内部类方式实现线程的创建
使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。
使用匿名内部类的方式实现
Runnable
接口,重新
Runnable
接口中的
run
方法:
public class
NoNameInnerClassThread
{
public static
void
main
(
String
[]
args
) {
// new Runnable(){
// public void run(){
// for (int i = 0; i < 20; i++) {
// System.out.println("
张宇
:"+i);
// }
// }
// }; //‐‐‐
这个整体 相当于
new MyRunnable()
Runnable r
=
new
Runnable
(){
public
void
run
(){
for
(
int
i
=
0
;
i
<
20
;
i
++
) {
System
.
out
.
println
(
"
张宇
:"
+
i
);
}
}
};
new
Thread
(
r
).
start
();