多线程运行原理
简单来说,多线程就是在程序执行的时候,再创建一个新的线程类,主程序执行的时候,是开辟了一条通往cpu的路径,而在主程序里面,有调用线程类的代码,也就是在执行的过程中,又开辟了一条新的通往cpu的路线。这也就是说,cpu在执行的时候就有了选择的余地,可以执行主线程,也可以执行线程类的内容,因为我们 无法控制cpu执行哪个,这也就是多线程的随机执行的原因。
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类所具有的优势:
- 适合多个相同的程序代码的线程去共享同一个资源。
- 可以避免java中的单继承的局限性。
- 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
- 线程池只能放入实现Runnable或Callable类线程线程,不能直接放入继承Thread的类。
在java中,每次程序运行至少启动两个线程,一个是main线程,一个是垃圾收集线程。因为每当使用了java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM其实就是在操作系统中启动了一个进程。
获取线程的名称
- 使用Thread类中的getName():String getName() 返回该线程的名称。
- 可以先获取到当前正在执行的线程,使用线程中的方法getName()获取线程名称:static Thread currentThread() 返回对当前正在执行线程对象的引用。
代码演示:
获取线程名称方法1
package day27;
import java.sql.SQLOutput;
public class xiancheng01 extends Thread{
@Override
public void run(){
String name = getName();
System.out.println(name);
}
}
获取线程名称方法2
package day27;
public class xiancheng03 extends Thread{
@Override
public void run(){
Thread t = Thread.currentThread();
System.out.println(t);
}
}
Thread类的常用方法sleep
public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)毫秒数结束之后,线程继续执行。
因为sleep方法本身是带有异常的,所以要使用try...catch方法解决。
代码演示:
package day27;
public class SleepDemo01 {
public static void main(String[] args) {
//模拟秒表
for (int i = 0; i < 60; i++) {
System.out.println(i);
//使用Thread类的sleep方法让程序睡眠1秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
实现Runnable接口
创建多线程程序的第二种方式:实现Runnable接口
java.lang.Runnable
Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为run的无参数方法。
java.lang.Thread类的构造方法
Thread(Runnable target) 分配新的Thread对象。
Thread(Runnable target,String name) 分配新的Thread对象。
实现步骤:
- 创建一个Runnable接口的实现类。
- 在实现类中重写Runnable接口的run方法,设置线程任务。
- 创建一个Runnable接口的实现类对象。
- 创建Thread类对象,构造方法中传递Runnable接口的实现类对象。
- 调用Thread类中的start方法,开启新的线程执行run方法。
代码演示:
package day27;
//创建一个Runnable接口的实现类
public class RunnableImpl implements Runnable{
@Override
public void run(){
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
package day27;
public class RunnableDemo02 {
public static void main(String[] args) {
//创建一个Runnable实现类对象
RunnableImpl run = new RunnableImpl();
//创建Thread类对象,构造方法中传递Runnable接口的实现类对象
Thread t = new Thread(run);
//调用start方法,开启新的线程执行run方法
t.start();
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
实现Runnable接口创建多线程程序的好处:
- 避免了单继承的局限性
一个类智能集成一个类,类继承了Tread类就不能继承其他类。
实现了Runnable接口,还可以继承其他的类,实现其他的接口。- 增强了程序的扩展性,降低了程序的耦合性(解耦)
实现Runnable接口的方式,把设置线程任务和开启新线程进行了奋力(解耦)
实现类中,重写了run方法:用来设置线程任务。
创建Thread类对象,调用start方法:用来开启新线程。
匿名内部类的方式实现线程的创建
匿名:没有名字
内部类:卸载其他类内部的类
匿名内部类作用:简化代码
把子类继承父类,重写父类的方法,创建子类对象合一步完成
把实现类实现类接口,重写接口中的方法,创建实现类对象合成一步完成。
匿名内部类的最终产物:子类/实现类对象,而这个类没有名字
格式:
new 父类/接口(){
重复父类/接口中的方法
};
代码演示:
package day27;
public class Demo03 {
public static void main(String[] args) {
//线程的父类是Thread
// new Mythread().start();
new Thread(){
@Override
public void run(){
for (int i = 0; i < 20; i++){
System.out.println(Thread.currentThread().getName()+"-->"+"小明");
}
}
}.start();
//线程的接口Runnable
//Runnable r = new RunnableImpl(); //多态
Runnable r = new RunnableImpl(){
@Override
//重写run方法,设置线程任务
public void run(){
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+"君君");
}
}
};
new Thread(r).start();
}
}
并发与并行
并发:指两个或多个事件在同一个时间段内发生。
并行:指两个或多个事件在同一时刻发生(同时发生)。
线程与进程
进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
简而言之,一个程序运行后至少有一个进程,一个进程中可以包含多个线程。
线程调度
- 分时调度
所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。- 抢占式调度
优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
主线程
执行主方法(main方法)的线程就是主线程
单线程程序:java程序中只有一个线程。
执行从main方法开始,从上到下依次执行。
主线程概念:
JVM执行main方法,main方法会进入到栈内存
JVM会着操作系统开辟一条main方法桐乡cpu的执行路径,cpu就可以通过这个路径来执行main方法,这个路径就叫做main线程,也叫主线程。