一、主线程
主线程:执行主方法的线程(main)
JVM执行main(),main()会进入到栈内存,JVM会找操作系统开辟一条main方法通向CPU的执行路径
CPu就可以通过这个路径来执行main方法,而这个路径就叫做主(main)线程。
单线程程序:Java程序只有一个线程
Java程序属于抢占式调度,哪个线程的优先级高,就优先调度;同级随机调度。
二、如何多线程程序
创建多线程程序有两种方法。
1、一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。
创建多线程程序的第一种方式:创建Thread类的子类
java.lang.Thread类:是描述线程的类,需继承Thread类。
实现步骤:
1、创建一个Thread类的子类
2、在Thread类的子类中重写Thread类的run(),设置线程任务(开启线程要做什么)
3、创建Thread类的子类对象
4、调用Thread类的start(),开启新线程,执行run()
void start() 使该线程开始执行;JVM调用该线程的run()
Thread子类代码如下:
package test.day22Process;
/*
Java程序属于抢占式调度,哪个线程的优先级高,就优先调度;同级随机调度。
创建多线程程序的第一种方式:创建Thread类的子类
java.lang.Thread类:是描述线程的类,需继承Thread类
实现步骤:
1、创建一个Thread类的子类
2、在Thread类的子类中重写Thread类的run(),设置线程任务(开启线程要做什么)
3、创建Thread类的子类对象
4、调用Thread类的start(),开启新线程,执行run()
void start() 使该线程开始执行;JVM调用该线程的run()
*/
public class Demo02Thread extends Thread {
//设置线程的任务
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("run" + i);
}
}
}
测试代码如下:
package test.day22Process;
public class Main02 {
public static void main(String[] args) {
//创建Thread子类对象
Demo02Thread th = new Demo02Thread();
//开启多线程程序
th.start();
for (int i = 0; i < 20; i++) {
System.out.println("main" + i);
}
}
}
在 Thread.class 文件中有这样一行代码:
private native void start0();
start()方法是一个native方法,它将通知底层操作系统,最终由操作系统启动一个新线程,操作系统将执行run()方法。
多线程内存图解如下:
多线程执行时,在栈内存中,其实每一个执行线程都有一片自己所属的栈内存空间。进行方法的压栈和弹栈。当执行线程的任务结束了,线程自动在栈内存中释放了。但是当所有的执行线程都结束了,那么进程就结束了。
2、另一种是实现Runnable接口方式
实现步骤:
1、创建Runnable接口的实现类
2、在实现类中重写Runnable接口的run(),设置线程任务
3、创建一个Runnable接口的实现类对象
4、创建Thread类对象,构造方法传递Runnable接口的实现类对象
优点:
1、避免了单继承的局限性
由于一个子类只能有唯一的一个父类,一旦继承了Thread类就无法继承其他的父类。但是,使用Runnable接口,可以实现其余的接口。
2、增强了程序的扩展性,降低了程序的耦合性(解耦)
实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离。实现类中,重写了run():用来设置线程任务。创建Thread类对象,调用start():用来开启新的线程(实现多线程)。
5、调用Thread类的start(),开启多线程
Runnable接口的实现类代码如下:
package test.day22Process;
/*
创建多线程程序的第二种方法:实现Runnable接口
实现步骤
1、创建Runnable接口的实现类
2、在实现类中重写Runnable接口的run(),设置线程任务
3、创建一个Runnable接口的实现类对象
4、创建Thread类对象,构造方法传递Runnable接口的实现类对象
5、调用Thread类的start(),开启多线程
*/
public class Demo03Runnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "==>" + i);
}
}
}
测试代码如下:
package test.day22Process;
public class Demo03Main {
public static void main(String[] args) {
Demo03Runnable r = new Demo03Runnable();
new Thread(r).start();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "==>" + i);
}
}
}
三、Thread类
构造方法:
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():返回对当前正在执行的线程对象的引用。
四、Thread类和Runnable的区别
如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
总结:实现Runnable接口比继承Thread类所具有的优势:
1.适合多个相同的程序代码的线程去共享同一个资源。
2.可以避免java中的单继承的局限性。
3.增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
4.线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
扩充:
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM其实在就是在操作系统中启动了一个进程。