文章目录
1. 线程的概念
多线程是所有应用程序中都非常重要的特性,在Java中也是相同,一般情况多线程仅用于网络编程,服务器程序开发,以及常见的UI底层开发,或者系统底层开发,所以一般只要涉及到多线程开发那么程序就会非常复杂,代码逻辑也非常复杂
多线程的作用就是让程序在执行任务期间key同事操作多个任务或多个线程操作一个任务,从而达到提高执行效率
多线程程序:
- 现实生活:一个人跟一堆小姐姐聊天
- 软件:QQ,微信,迅雷,音乐软件
1. 程序
程序是一个静态的概念(未开启时),程序一般是指计算机中的一个可执行文件,例如:QQ,微信,等软件,当双击时会将对应的应用程序加载到内存中,并且开始执行程序并产生进程
,程序
开启后会生成进程
2. 进程
进程
是一个正在运行的程序
,当双击某个程序
时会在内存中加载运行,所以进程
就是在内存运行的程序,进程
是动态的概念,程序
加载过程就是在内存中会产生一个进程
,并具有一定的功能,同时操作系统中允许开启多个进程
,进程的最小执行单元是线程
,进程的执行前提是必须要有一个线程
执行,否则会程序终止
3. 线程
线程
是进程
中最小的执行单元,主要赋值程序的运行,一个进程
中至少要有一个线程执行,如果没有线程则进程
就无法执行,导致进程结束
一个进程中允许启动多个线程(多个功能或任务),线程分为多线程和单线程
- 单线程:进程中只有一个执行的线程,任务的执行必须是顺序执行,当一个任务执行完毕后下一个任务才可以继续执行(依次执行)
- 多线程:进程中有多个执行的线程,任务可以同事执行,并且任务间不会存在互相干扰,互相影响的问题
4. Java程序的运行原理
Java的运行是由命令启动Java虚拟机(JVM),JVM启动后就等于是开起来一个进程
,进程的
开启是由类中的main
方法执行,main
方法是Java程序主线程
,所有逻辑代码都是在main
方法执行的,一般main
方法就是程序入口
Java是多线程还是单线程?
答案是多线程。原因是垃圾回收线程也要先启动,否则很容易会出现内存溢出。现在的垃圾回收线程加上前面的主线程,最低启动了两个线程,所以,JVM的启动其实是多线程的
5. 并发与并行概念
1. 并发
并发是指程序同一时间只能执行一条指令,多线程开启时会快速轮询切换(由CPU决定切换执行),从宏观上看是多个线程同时执行,但是在微观上并不是多个线程同时执行,而是将时间分为若干份,通过CPU切换执行(交替执行)
在同一个时间段内只能有一个线程执行,线程执行的途中其他线程则无法执行,这个是由CPU决定,三个线程不会出现同时执行的情况
2. 并行
并行是指程序同一时间可以执行多条指令,在同一个时限内宏观上看是同时执行,在微观上看也是同时执行,在一个时间片段内可以共同执行,这种效率也是最高
并行执行是在同一个时间片段内多条指令同时执行,同一个时间内三个线程可以同时执行并互不干扰
3. 并发编程和并行编程
并发编程与并行编程完全取决于CPU是否足够强大
如果CPU比较繁忙,资源不足时开启多个线程,那么是由CPU决定执行的线程应该是谁(多个线程抢夺CPU的执行权),谁抢夺到了谁执行,线程会尽可能抢夺执行权
如果CPU资源充足,那么开启多个线程或进程则可以分配不同的CPU内核(核心),此时多个进程中的多个线程则会同时执行,互不干扰,也不会出现抢夺CPU执行权的问题,每个线程都会分配独立的CPU内核运行
单核CPU最多只能并发编程,多核CPU可以实现并行编程
一个物理cpu(计算机主板上实际安装的cpu)可以有多个核心数,简称多核cpu;单核cpu是指一个cpu只有一个处理核心,双核cpu是指一个cpu具有两个处理核心,多核cpu是指一个cpu具有多个核心;计算机主板上可以安装多个物理cpu。
物理cpu内核总数=物理cpu数*每颗物理cpu核心数
逻辑cpu总数=总的物理cpu内核数*超线程数
这个电脑是六核十二线程,即一个物理cpu具有六个核心数属于多核cpu。
真正实现多线程效果的是:start0(),而不是run()方法,,很可惜的是start0()方法是本地方法由native关键字修饰,该方法由JVM调用,方法底层是由C/C++实现。
2. Java中的Thread线程类
Java中的线程启动需要使用Thread
类的方法,此类用于创建并启动线程,其中定义了很多的构造方法与启动线程的方法,用于启动线程和定义线程规则等功能,同时还提供了静态获取方法以及普通获取方法,Thread
类位于java.lang
核心包中,所以不需要导包
1. Thread类构造方法
public Thread()
:创建线程类对象public Thread(String name)
:创建线程类对象,并指定线程名称public Thread(Runnable target)
:创建线程类对象,并指定Runnable实现类public Thread(Runnable target, String name)
:创建线程类对象,并指定Runnable实现类以及指定线程名称public Thread(ThreadGroup group, String name)
:创建线程类对象,并指定线程分组以及指定线程名称public Thread(ThreadGroup group, Runnable target)
:创建线程类对象,并指定线程分组以及指定Runnable实现类public Thread(ThreadGroup group, Runnable target, String name)
:创建线程类对象,并指定线程分组以及指定Runnable实现类和指定线程名称
2. Thread类普通方法
public long getId()
:获取当前线程对象IDpublic final String getName()
:获取当前线程对象名称public final int getPriority()
:获取当前线程优先级别public State getState()
:获取当前线程状态public final ThreadGroup getThreadGroup()
:获取当前线程分组
3. Thread类静态方法
public static native Thread currentThread()
:获取当前正在执行的线程对象
4. Thread类特殊方法
public void run()
:用于编写线程任务的方法(多线程执行的任务方法),继承后必须自定义实现的方法public synchronized void start()
:启动线程方法,当调用start方法时此方法会有JVM虚拟机调用执行run方法中的任务代码
3.线程的创建方式
1. 继承Thread类
创建一个类并继承Thread
类,并重写run
方法,在run
方法中实现自己的线程任务,同时可以创建构造方法,调用父类的构造方法实现设置线程的信息
/**
* 多线程创建方式一:继承Thread类重写run方法
*/
class CreateMethod1 extends Thread{
public CreateMethod1(){}
public CreateMethod1(String name){
super(name);
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("createMethod1:"+i);
}
}
}
public class ThreadTest {
public static void main(String[] args) {
CreateMethod1 createMethod1 = new CreateMethod1("这个女生");
createMethod1.start();
System.out.println(createMethod1.getName());
System.out.println("ID:"+createMethod1.getId());
System.out.println("优先级别:"+createMethod1.getPriority());
System.out.println("当前线程:"+Thread.currentThread());
for (int i = 10; i < 20; i++) {
System.out.println("threadTest:"+i);
}
}
}
2. 实现Runnable接口
创建一个类实现Runnable
接口,并重写run
方法,run
方法就是线程任务方法
//实现Runnable接口重写run方法
class CreateMethod2 implements Runnable{
@Override
public void run() {
Thread thread = Thread.currentThread();
System.out.println("次线程名称:"+thread.getName());
System.out.println("次线程状态:"+thread.getState());
System.out.println("次线程优先级:"+thread.getPriority());
for (int i = 0; i < 10; i++) {
System.out.println("实现Runnable接口:"+i);
}
}
}
public class ThreadTest2 {
public static void main(String[] args) {
CreateMethod2 createMethod2 = new CreateMethod2();
Thread t1 = new Thread(createMethod2, "实现Runnable接口");
t1.start();
for (int i = 40; i < 50; i++) {
System.out.println("主线程:"+i);
}
}
}
3. 实现Callable接口
创建一个类实现Callable
接口,并重写call
方法,call
方法就是线程任务类,启动线程还需要创建一个任务FutureTask
类,再创建Thread
类启动线程
Callable
接口的call
方法具有返回值并且可以声明异常,call
方法的返回值由FutureTask
获取
class CreateMethod3 implements Callable<String> {
@Override
public String call() throws Exception {
Thread thread = Thread.currentThread();
System.out.println("次线程名称:"+thread.getName());
System.out.println("次线程状态:"+thread.getState());
System.out.println("次线程优先级:"+thread.getPriority());
// 任务代码
// 循环10次
for (int i = 1; i <= 10; i++) {
System.out.println("次线程执行..." + i);
}
// 返回值
return "线程执行完毕";
}
}
public class ThreadTest3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 1. 创建Callable实现类
Callable threadTest3 = new CreateMethod3();
// 2. 创建FutureTask类
FutureTask<String> futureTask = new FutureTask<>(threadTest3);
// 3. 创建Thread类
Thread thread = new Thread(futureTask,"创建线程方式3");
// 接收返回值,get()方法用于接收Callable接口的返回值
// 调用此方法时必须时线程已被启动的,如果是未启动则会造成程序阻塞等待
// get方法调用后会等待接收返回值,等待途中其他代码不会执行
thread.start();
String res = futureTask.get();
System.out.println("result = " + res);
for (int i = 50; i < 60; i++) {
System.out.println("主线程:"+i);
}
}
}
4. 多线程创建方式总结
- 继承Thread类
- 优点:实现简单
- 缺点:无返回值,异常不能声明抛出,Java是单继承,一旦继承Thread类则无法继承其他类
- 实现Runnable接口–>常用
- 优点:解决了单继承的问题,实现相对较为简单
- 缺点:无返回值,异常不能声明抛出
- 实现Callable接口
- 优点:解决了单继承问题,有返回值,异常可以声明抛出
- 缺点:实现较为复杂,接收返回值会造成程序阻塞,等待接收返回值,接收返回值后才可以继续执行