多线程详解
Posted Neil_Wesley
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程详解相关的知识,希望对你有一定的参考价值。
[多线程详解(一)](http://www.neilx.com)
一、概念准备
1、进程
(1)直译:正在进行中的程序
(2)解释:执行一个程序时,会在内存中为程序开辟空间,这个空间就是一个进程。
(3)注意:一个进程中不可能没有线程,只有有了线程才能执行; 进程只开辟空间,并不执行,执行的是线程.
2、进程
(1)定义:就是进程中一个负责执行的控制单元(执行路径)
(3)注意:一个进程中可以有多个执行路径,称为多线程;一个进程中至少有一个线程
3、任务
开启多个线程,是为了同时运行多个内容,这个内容就是任务
二、多线程的初步认识
jvm(java虚拟机)中的多线程解析
jvm启动时,就启动了多个线程:
其中至少有两个线程:执行main函数的主线程和负责垃圾回收的线程
解释:在执行main中任务的时候,会开启一条线程,当运行垃圾越来越多,又会有垃圾回收任务,所以垃圾回收线程开启。其中,main方法任务的代码都定义主函数代码中。
三、垃圾回收线程和主函数线程示例
1、垃圾回收线程
class person extends Object
{
public void finalize()
{
System.out.println("hahaha!");
}
}
public class demo1{
public static void main(String[] args){
person p1=new person();
person p2=new person();
//调用gc方法,运行垃圾回收期
System.gc();
System.out.println("hehehe!");
}
}
运行结果:
![垃圾回收线程输出结果](https://img-blog.csdn.net/20160615152724948)解释:垃圾回收机制是虚拟机调用Object类的finalize方法。当垃圾回收器已经确定不存在对某对象的引用时,垃圾回收器自动调用fianlize方法,子类重写finalize方法,执行回收动作。代码如上
System.gc();是垃圾回收程序,调用后,不定时的执行垃圾回收
小插曲:System.gc()与finalize()的辨析:
(1)gc()只能清除在堆上分配的内存(纯java语言的所有对象都在堆上使用new分配内存),而不能清除栈上分配的内存(当使用JNI技术时,可能会在栈上分配内存,例如java调用c程序,而该c程序使用malloc分配内存时)。因此,如果某些对象被分配了栈上的内存区域,那gc就管不着了,对这样的对象进行内存回收就要靠finalize().
举个例子来说,当java调用非java方法时(这种方法可能是c或是c++的),在非java代码内部也许调用了c的malloc()函数来分配内存,而且除非调用那个了free(),否则不会释放内存(因为free()是c的函数),这个时候要进行释放内存的工作,gc是不起作用的,因而需要在finalize()内部的一个固有方法调用它(free()).
(2)finalize的工作原理应该是这样的:一旦垃圾收集器准备好释放对象占用的存储空间,它首先调用finalize(),而且只有在下一次垃圾收集过程中,才会真正回收对象的内存.所以如果使用finalize(),就可以在垃圾收集期间进行一些重要的清除或清扫工作.
(3)那么什么时候调用finalize()呢?
- 《1》所有对象被gc()时自动调用,比如运行System.gc()的时候.
- 《2》程序退出时为每个对象调用一次finalize方法
- 《3》显式的调用finalize方法
(4)除此以外,正常情况下,当某个对象被系统收集为无用信息的时候,finalize()将被自动调用,但是jvm不保证finalize()一定被调用,也就是说,finalize()的调用是不确定的,这也就是为什么sun不提倡使用finalize()的原因. 简单来讲,finalize()是在对象被GC回收前会调用的方法,而System.gc()强制GC开始回收工作纠正,不是强制,是建议,具体执行要看GC的意思简单地说,调用了 System.gc() 之后,java 在内存回收过程中就会调用那些要被回收的对象的 finalize() 方法。
2、主线程的运行示例
class person extends Object
{
int i;
private String name;
person(String name)
{
this.name=name;
}
public void show()
{
for(i=0;i<=10;i++)
{
System.out.println(name+"x="+i);
}
}
}
public class demo1{
public static void main(String[] args){
person p1=new person("zhangsan");
person p2=new person("lisi");
p1.show();
p2.show();
System.out.println("hehehe!");
}
}
执行流程:p1.show执行—结束后——出栈——p2.show执行——执行完毕—出栈
此时,在执行p1.show时,必须执行完张三后,李四才可以执行,如果想让张三执行的同时,李四也执行呢?——使用多线程
四、创建线程
从上面的示例中可以看出,张三show、李四show是在一条主线程上运行,所以输出的结果中是按照一定的顺序打印输出的。这种方法,程序运行的效率较低,如果我们可以让两个对象“同时”运行的话,那么效率会得到极大的提升。
注意:此处说的同时,是依赖于多核处理器运行情况下的同时。我们都知道,单核下所谓的多线程是依赖于cpu高速处理下的程序往返切换而造成的“伪多线程”“伪同时”在说明线程创建方法之前,我们不妨首先思考一下上述的主线程是如何创建的?
在上面的示例中,我们并没有刻意的添加代码,但是主线程就别创建了,由此,我们可以知道是java虚拟机默认为我们创建了主线程。那么java虚拟机又是如何创建的呢?其实是java虚拟机以来操作系统的一些功能,为我们创建的。说道这里大家可能有些晕了,下面我们来梳理一下思路(学习嘛,就是先把书本学厚,再学薄的过程)
jvm→→→操作系统→→→创建线程所以在java中我们可以利用对象,通过虚拟机连接底层的操作系统进行线程的创建
方法一:继承Thread类,实现线程的创建
-
1、定义一个类,继承Thread类
-
2、覆盖Thread类中的run方法
示例:
//继承Thread
class person extends Thread
{
int i;
private String name;
person(String name)
{
this.name=name;
}
//调用run方法
public void run()
{
show();
}
public void show()
{
for(i=0;i<=10;i++)
{
System.out.println(name+"x=“+i+”…name=“+getName());
}
}
}
public class demo1{
public static void main(String[] args){
person p1=new person("zhangsan");
person p2=new person("lisi”);
//开启多线程并调用
p1.start();
p2.start();
}
}
可能会有人比较奇怪,既然继承Thread类后就已经创建了线程。run()又是什么鬼???Thread类中为什么会有run()方法呢??请听我慢慢道来
这就需要我们用到之前的关于进程、线程、任务的概念了。
//继承Thread
class person extends Thread
{ int i;
private String name;
person(String name)
{
this.name=name;
}
//调用run方法
public void run()
{
show();
}
public void show()
{
for(i=0;i<=10;i++)
{
System.out.println(name+"x=“+i+”…name=“+getName());
}
}
}
public class demo1{
public static void main(String[] args){
person p1=new person("zhangsan");
person p2=new person("lisi”);
//未开启多线程
p1.run();
p2.run();
}
}
运行结果:
结果是显而易见的,由于未使用start方法所以并没有实现多线程的效果。这也是创建多线程失败的一个原因。
六、显示线程的名字
当我们创建线程成功后,如何直观的看出正在运行的是哪一个线程呢?这就需要我们直接打印输出线程的名字————使用getName()方法
class person extends Thread
{
int i;
private String name;
person(String name)
{
this.name=name;
}
public void run()
{
show();
}
public void show()
{
for(i=0;i<=10;i++)
{
System.out.println(name+"x="+i+"...name="+getName());
}
}
}
public class demo1{
public static void main(String[] args){
person p1=new person("zhangsan");
person p2=new person("lisi”);
//开启线程
p1.start();
p2.start();
}
}
运行结果:
此时,我们会发现打印输出的结果中有线程的名字:Thread-0、Thread-1
下面我们修改一下代码,不开启线程,看看会有什么发生??????
class person extends Thread
{
int i;
private String name;
person(String name)
{
this.name=name;
}
public void run()
{
show();
}
public void show()
{
for(i=0;i<=10;i++)
{
System.out.println(name+"x="+i+"...name="+getName());
}
}
}
public class demo1{
public static void main(String[] args){
person p1=new person("zhangsan");
person p2=new person("lisi”);
//注意此时没有开启线程,仅调用run函数,没有使用线程
p1.run();
p2.run();
}
}
运行结果:
显而易见:输出的结果中,仍然有Thread-0、Thread-1(即线程的名字)
基于上述情况,让我们重新认识一下getName()方法吧!!!!
由上述可知,我们没有启动多线程,但是运行结果却打印输出了线程的名字。其实,在线程创建时,线程就拥有了自己的名字。也就是说此时得到的是对象线程的名字,而不是正在运行的线程名字 这样做的好处是,一旦产生就拥有唯一标识自己身份的ID,便于以后的管理和使用,减少错误情况的产生。就如同我们出生后,往往会尽快的上户口,如果在打预防针的时候,你还没有上户口,那么就会出出现等等问题。
那么如何获得正在运行的线程名字呢?
七、显示正在运行线程的名字
使用Thread类下的currentThread()方法
示例如下
class person extends Thread
{
int i;
private String name;
person(String name)
{
this.name=name;
}
public void run()
{
show();
}
public void show()
{
for(i=0;i<=10;i++)
{
System.out.println(name+"x="+i+"...name="+Thread.currentThread().getName());
}
}
}
public class demo1{
public static void main(String[] args){
person p1=new person("zhangsan");
person p2=new person("lisi");
p1.start();
p2.start();
System.out.println("出来吧,name="+Thread.currentThread().getName());
}
}
运行结果:
人类的欲望总是无休止的,现在我们已经输出了正在运行的线程名字,但是为了提高阅读的直观性,我们能不能将线程的名字与对象的名字结合起来呢?比如给一个线程取名字叫张三,一个叫李四
如何给线程取名字呢?
class person extends Thread
{
int i;
private String name;
person(String name)
{
//给线程命名,使用super
super(name);
this.name=name;
}
public void run()
{
show();
}
public void show()
{
for(i=0;i<=10;i++)
{
System.out.println(name+"x="+i+"...name="+Thread.currentThread().getName());
}
}
}
public class demo1{
public static void main(String[] args){
person p1=new person("zhangsan");
person p2=new person("lisi");
p1.start();
p2.start();
System.out.println("出来吧,name="+Thread.currentThread().getName());
}
}
运行结果:
未完待续,这只是创建线程的第一种方法,第二种方法及其他介绍,请阅读“多线程(二)”
以上是关于多线程详解的主要内容,如果未能解决你的问题,请参考以下文章