线程与进程
- 进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
- 线程:是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
进程与线程的区别:
- 进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。
- 线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。
1. 因为一个进程中的多个线程是并发运行的,那么从微观角度看也是有先后顺序的,哪个线程执行完全取决于CPU 的调度,程序员是干涉不了的。而这也就造成的多线程的随机性。
2. Java 程序的进程里面至少包含两个线程,主进程也就是 main()方法线程,另外一个是垃圾回收机制线程。每当使用 java 命令执行一个类时,实际上都会启动一个 JVM,每一个 JVM 实际上就是在操作系统中启动了一个线程,java 本身具备了垃圾的收集机制,所以在 Java 运行时至少会启动两个线程。
线程的创建-继承方式
Java使用java.lang.Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码。Java使用线程执行体来代表这段程序流。Java中通过继承Thread类来创建并启动多线程的步骤如下:
1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。2. 创建Thread子类的实例,即创建了线程对象3. 调用线程对象的start()方法来启动该线程
自定义线程类:
class MyThread extends Thread{
/*
重写run方法,完成该线程执行的逻辑
*/
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("自定义线程:" + i);
}
}
}
测试类:
public static void main(String[] args){
//创建自定义线程对象
MyThread mt = new MyThread();
//开启新线程
mt.statrt();
//在主方法中执行for循环
for(int i =0 ; i<200; i++){
System.out.println("main主线程:"+i);
}
}
线程的执行流程
线程内存图
当执行线程任务结束了,线程自动在栈内存中释放了,但是当所有执行线程都结束了,那么进程就结束了。
run()方法和start()方法
- run()方法,是线程执行的任务方法,每个线程都会调用run()方法执行,我们将线程要执行的任务代码都写在run()方法中就可以被线程调用执行。
- start()方法,开启线程,线程调用run()方法。start()方法源代码中会调用本地方法start0()来启动线程:private native void start0(),本地方法都是和操作系统交互的,因此可以看出每次开启一个线程的线程都会和操作系统进行交互。
- 注意:一个线程只能被启动一次!!!
线程名字的设置和获取
- Thread类的方法String getName(String name)设置线程的名字
- 通过Thread类的构造方法Thread(String name)也可以设置线程的名字
public class MyThread extends Thread{
public void run(){
System.out.println("线程名字:" +super.getName());
}
}
测试类:
public class Demo{
public static void main(String[] args){
//创建自定义线程对象
MyThread mt = new MyThread();
//设置线程名字
mt.setName("旺财");
//开启新线程
mt.start();
}
}
注意:线程是有默认名字的,如果我们不设置线程的名字,JVM会赋予线程默认名字Thread-0,thread-1....
获取运行main方法线程的名字
- Demo类不是Thread的子类,因此不能使用getName()方法获取。
- Thread类定义了静态方法static Thread currentThread()获取到当前正在执行的线程对象
- main方法也是被线程调用了,也是具有线程名字的。
public static void main(String[] args){
Thread t = Thread.currentThread();
System.out.println(t.getName());
}
public static void main(String[] args){
Thread t = Thread.currentThread();
System.out.println(t.getName());
}
线程的创建-实现方式
实现Runnable接口方式
采用java.lang.Runnable也是非常常见的一种,我们只需要重写run方法即可。
步骤如下:
public class MyRunnable implements Runnable{
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){
//创建自定义类对象,线程任务对象
Runnable mr = new MyRunnable();
//创建线程对象
Thread t = new Thread(mr);
t.start();
for(int i = 0;i<20;i++){
System.out.println("main:"+i);
}
}
Thread和Runnable的区别
如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
实现Runnable接口比继承Thread类所具有的优势:
匿名内部类方式创建线程
使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。使用匿名内部类的方式实现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("wensong:"+i);
// }
// }
//};//这个整体相当于new MyRunnable()
Runnable r = new Runnable(){
public void run(){
for(int i=0;i<20;i++){
System.out.println("wensong:"+i);
}
}
};
new Thread(r).start();
for (int i=0;i<20;i++){
System.out.println("qing:"+i);
}
}
}