并发、并行
并发:指两个或多个事件在同一个时间段内发生。
并行:指两个或多个事件在同一时刻发生(同时发生)。
线程与进程
进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程。进程也是程序的一次执行过程,是系统运行程序的基本单位。系统运行一个程序即是一个进程从创建、运行到消亡的过程。
线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程,一个进程中也可以有多个线程,这个应用程序也称之为多线程程序。
进程
我们也可以在电脑上同时ctrl+alt+delete进入任务管理器,查看进程
线程
比如我们电脑上安装的电脑管家,当点击运行电脑管家的时候,进入内存,占有一块独立的内存空间,但是我们知道电脑管家有很多的功能,比如说病毒查杀、清理垃圾、电脑加速,每点击一个功能执行,就是一个线程。
线程调度
分时调度
所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。
抢占式调度
优先让优先级高的线程是用cpu,如果线程的优先级相同,那么会随机选择一个(线程随机性),java使用的就是抢占式调度,一句话说大概就是,大家一起抢夺CPU,岁强大谁执行。
我们可以自己设置线程的优先级—>
在电脑上同时ctrl+alt+delete进入任务管理器,查看详细信息,选择一个右击–>设置优先级
CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于一个CPU的一个核而言,某个时刻,只能执行一个线程,而CPU在多个线程间切换速度相对我们的感觉快得多,看上去就是在同一个时刻运行,提高程序的运行效率,让CPU的使用率更高。
线程的创建
继承Thread类
步骤:
- 定义Thread子类,并重写父类Thread的run()方法,该run()方法中写线程需要执行什么任务,也称为线程的执行体。
- 创建Thread子类的实例,即创建了线程对象。
- 调用线程对象的start()方法启动该线程
package com.yjj.test;
import com.yjj.threads.MyThread;
public class MyTest {
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.setName("myThread");
myThread.start();
for(int i=0;i<5;i++){
System.out.println("main主线程执行");
}
}
}
package com.yjj.threads;
public class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<5;i++){
//输出当前运行的线程的名字
System.out.println(Thread.currentThread().getName()+"正在执行");
}
}
}
实现Runnable接口
步骤:
- 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
- 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
- 调用线程对象的start()方法来启动线程。
package com.yjj.test;
import com.yjj.threads.MRunnable;
public class MyTest {
public static void main(String[] args) {
//创建Runnable实现类的实例
MRunnable myThread=new MRunnable();
//MRunnable实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
Thread th=new Thread(myThread);
th.setName("myThread");
th.start();
for(int i=0;i<5;i++){
System.out.println("main主线程执行");
}
}
}
package com.yjj.threads;
//实现 Runnable接口
public class MRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<5;i++){
//输出当前运行的线程的名字
System.out.println(Thread.currentThread().getName()+"正在执行");
}
}
}
Thread和Runnable的区别
如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
实现Runnable接口比继承Thread类所具有的优势:
- 适合多个相同的程序代码的线程去共享同一个资源。
- 可以避免java中的单继承的局限性。
- 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
- 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
注意
- 创建线程,调用start()方法,线程进入就绪状态,并没有直接进入运行状态,只有获得了CPU时间片才进入运行状态
- 在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM其实就是在操作系统中启动了一个进程。