目录
进程的概述和多进程的意义
进程概述
什么是进程呢?通过任务管理器我们就可以看到进程的存在。
概念:进程就是正在运行的程序,是系统进行资源分配和调用的独立单位。
每一个进程都有它自己的内存空间和系统资源。
多进程的意义
单进程计算机只能做一件事情。而我们现在的计算机都可以一边玩游戏(游戏进程),一边听音乐(音
乐进程),
所以我们常见的操作系统都是多进程操作系统。比如:Windows,Mac和Linux等,能在同一个时
间段内执行多个任务。
对于单核计算机来讲,游戏进程和音乐进程是同时运行的吗?不是。
因为CPU在某个时间点上只能做一件事情,计算机是在游戏进程和音乐进程间做着频繁切换,且切换速度很快,
所以,我们感觉游戏和音乐在同时进行,其实并不是同时执行的。多进程的作用不是提高执行速
度,而是提高CPU的使用率。
线程的概述和多线程的意义
什么是线程
在一个进程内部又可以执行多个任务,而这每一个任务我们就可以看成是一个线程。是程序使用
CPU的基本单位。所以,进程是拥有资源的基本单位, 线程是CPU调度的基本单位。
多线程有什么意义呢?
多线程的作用不是提高执行速度,而是为了提高应用程序的使用率。
那么怎么理解这个问题呢?
我们程序在运行的使用,都是在抢CPU的时间片(执行权),如果是多线程的程序,那么在抢到
CPU的执行权的概率应该比较单线程程序抢到的概率要大.那么也就是说,CPU在多线程程序
中执行的时间要比单线程多,所以就提高了程序的使用率.但是即使是多线程程序,那么他们
中的哪个线程能抢占到CPU的资源呢,这个是不确定的,所以多线程具有随机性.
并发:多个任务交替执行,不是真正意义上的同时执行,只是交替的速度很快你感觉在同时执行。
这就是Java中多个线程并发执行。
并行:真正意义上的同时执行,多个任务同时执行
一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行
不同的任务。
JVM运行原理以及JVM启动的线程探讨
Java程序运行原理
Java.exe 命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。
该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。
所以 main方法运行在主线程中。
JVM的启动是多线程的吗: JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。
实现多线程
方式一 继承Thread类
1.定义一个类,继承 Thread
2.重写 Thread 类的run方法
3.创建你定义的这个类对象,调用start()方法启动线程
public class MyThread extends Thread {
//run方法里面的代码将来你让你创建的线程一旦启动,就会过来执行。
@Override
public void run() {
//这里面的代码就是有线程过来执行的代码,一般我在子线程里面做一些耗时的操作
for (int i = 0; i < 100; i++) {
System.out.println("子线程执行的代码:" + i);
}
}
}
MyThread th = new MyThread();
//th.run(); //这样不是开启了线程,你这是创建了一个对象,调用了run方法,没有新的线程开启。
th.start(); //正确开启线程的方法
// th.start(); 同一个线程对象不要重复调用 start()
MyThread th2 = new MyThread();
th2.start();
Thread类的基本获取和设置方法
public final String getName()//获取线程名称
public final void setName(String name)//设置线程名称
public static Thread currentThread()//获取当前执行的线程
System.out.println("主线程中代码AAA");
//currentThread();获取当前执行的线程对象
Thread thObj = Thread.currentThread();
thObj.setName("主线程");
System.out.println(thObj.getName());
//在这个结点处开启一个子线程
MyThread th1 = new MyThread();
th1.setName("范冰冰");
线程的执行
假如我们的计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,
线程只有得到 CPU时间片,也就是使用权,才可以执行指令。那么Java是如何对线程进行调用的
呢?
抢占式调度模型 优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。
Java使用的是抢占式调度模型。
设置和获取线程优先级
public final int getPriority() //获取线程的优先级
public final void setPriority(int newPriority)//设置线程的优先级
线程的优先级的大小仅仅表示这个线程被CPU执行的概率增大了.但是我们都知道多线程具有随机
性
th1.setPriority(Thread.MAX_PRIORITY);
int priority = th1.getPriority();
System.out.println("th1的优先级:" + priority);
th1.start();
//在这个结点处开启一个子线程
MyThread th2 = new MyThread();
th2.setName("刘亦菲");
th2.setPriority(Thread.MIN_PRIORITY);
int priority1 = th2.getPriority();
System.out.println("th2的优先级:" + priority1);
th2.start();
线程休眠
public static void sleep(long millis) 线程休眠
有异常要处理
public class MyTest {
public static void main(String[] args) throws InterruptedException {
MyThread th = new MyThread();
th.start();
//让当前线程休眠
Thread.sleep(1000 * 3);
System.out.println("主线程的代码");
System.out.println("主线程的代码");
System.out.println("主线程的代码");
}
}
class MyThread extends Thread {
@Override
public void run() {
try {
//让当前线程进行休眠
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100; i++) {
System.out.println("子线程执行的代码:" + i);
}
}
}
加入线程
public final void join()
等待该线程执行完毕了以后,其他线程才能再次执行
public class MyTest {
public static void main(String[] args) throws InterruptedException {
MyThread th1 = new MyThread("刘备");
MyThread th2 = new MyThread("关羽");
MyThread th3 = new MyThread("张飞");
th1.start();
th1.join();
th2.start();
th2.join();
th3.start();
th3.join();
//join()在线程开启之后,调用,可以把多个线程并发执行,变为串行。
}
}
class MyThread extends Thread {
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(this.getName() + "-子线程执行的代码:" + i);
}
}
}
礼让线程
public static void yield(): 暂停当前正在执行的线程对象,并执行其他线程。
效果好像不太明显.
public class MyTest {
public static void main(String[] args) throws InterruptedException {
MyThread th1 = new MyThread("刘备");
MyThread th2 = new MyThread("关羽");
th1.start();
th2.start();
}
}
class MyThread extends Thread {
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(this.getName() + "-子线程执行的代码:" + i);
Thread.yield(); //线程礼让
}
}
}
守护线程
public final void setDaemon(boolean on)
setDaemon(true),设置线程为守护线程。
thread.setDaemon(true)必须在thread.start()之前设置。
public class MyTest {
public static void main(String[] args) throws InterruptedException {
Thread main = Thread.currentThread();
main.setName("刘备");
for (int i = 0; i < 10; i++) {
System.out.println(main.getName() + "i");
}
MyThread th2 = new MyThread("关羽");
MyThread th3 = new MyThread("张飞");
//设置守护线程
th2.setDaemon(true);
th2.start();
//设置守护线程
th3.setDaemon(true);
th3.start();
}
}
class MyThread extends Thread {
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(this.getName() + "-子线程执行的代码:" + i);
}
}
}
刘备i
刘备i
刘备i
刘备i
刘备i
刘备i
刘备i
刘备i
刘备i
刘备i
关羽-子线程执行的代码:0
张飞-子线程执行的代码:0
张飞-子线程执行的代码:1
张飞-子线程执行的代码:2
张飞-子线程执行的代码:3
张飞-子线程执行的代码:4
中断线程
public final void stop(): 停止线程的运行
public class MyTest {
public static void main(String[] args) throws InterruptedException {
MyThread th = new MyThread("线程A");
th.start();
Thread.sleep(5);
th.stop(); //强制停止线程 主线程睡5秒 再停止
}
}
class MyThread extends Thread {
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(this.getName() + "-子线程执行的代码:" + i);
}
}
}
public void interrupt(): 中断线程(这个翻译不太好),查看API可得当线程调用wait(),sleep(long
time)方法的时候处于阻塞状态,可以通过这个方法清除阻塞
public class MyTest {
public static void main(String[] args) throws InterruptedException {
MyThread th = new MyThread("线程A");
th.start();
th.interrupt(); //清除线程阻塞的状态 停止支线的睡眠
}
}
public class MyThread extends Thread {
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 1000; i++) {
System.out.println(this.getName() + "-子线程执行的代码:" + i);
}
}
}
方式二 实现Runnable接口
这种方式扩展性强 实现一个接口 还可以再去继承其他类
1.定义一个类,实现 Runnable 接口
2.重写run方法
3.创建定义的这个类的对象
4.new Thread 把这个类的对象作为参数,传过来
5. 调用start()方法启动
MyRunnable myRunnable = new MyRunnable();
Thread th = new Thread(myRunnable);
th.setName("线程A");
th.start();
class MyRunnable implements Runnable {
//void run()
// 使用实现接口 Runnable 的对象创建一个线程时,启动该线程将导致在独立执行的线程中调用对象的 run 方法。
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "-" + i);
}
}
}
方式三 实现 Callable 接口
相较于实现 Runnable 接口的方式,方法可以有返回值,并且可以抛出异常。
执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。 FutureTask 是 Future
接口的实现类
1.创建一个类实现Callable 接口
2.创建一个FutureTask类将Callable接口的子类对象作为参数传进去
3.创建Thread类,将FutureTask对象作为参数传进去
4.开启线程
public class MyTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建线程的第三种方式
MyCallable myCallable = new MyCallable();
//可使用 FutureTask 包装 Callable 或 Runnable 对象。因为 FutureTask 实现了 Runnable,所以可将 FutureTask 提交给 Executor 执行。
FutureTask<Integer> futureTask = new FutureTask<>(myCallable);
Thread th = new Thread(futureTask);
th.start();
//Thread.sleep(100);
Integer integer = futureTask.get();
System.out.println(integer);
}
}
public class MyCallable implements Callable<Integer> {
//call方法也是线程将来执行的方法
@Override
public Integer call() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
return 100;
}
}