开始时间:2021-01-05
进程和线程
4.1、什么是进程?什么是线程?
进程是一个应用程序(1个进程是一个软件) .线程是一个进程中的执行场景/执行单元-
一个进程可以启动多个线程。
4.2、对于java程序来说,当在Dos命令窗口中输入;
java Helloworld回车之后-
会先启动JVM,而JVM就是一个进程。JVM再启动一个主线程调用main方法。
同时再启动一个垃圾回收线程负责看护,回收垃圾。最起码,现在的java程序中至少有两个线程并发,
一个是垃圾回收线程,一个是执行main方法的主线程。
4.3、进程和线程是什么关系?举个例子
阿里巴巴:进程
马云:阿里巴巴的一个线程童文红:阿里巴巴的一个线程京东:进程
强东:京东的一个线程妹妹:京东的一个线程
进程可以看做是现实生活当中的公司.线程可以看做是公司当中的某个员工。
注意:
进程A和进程B的内存独立不共享。
线程A和线程B呢?
在java语言中:
线程A和线程B,堆内存和方法区内存共享。但是栈内存独立,一个线程一个栈。
Java虚拟机中,堆和方法区只有一个,栈可以有多个,多个栈就是多线程。
启动10个线程,就会有10个栈空间,互不干扰->多线程并发(提高效率)
火车站:进程
每个售票窗口:线程
使用了多线程后,main方法结束,可能程序也没有结束。只是主线程结束。
主栈空了,其他栈可能还在压栈弹栈
判断
package BUPT20210106;
public class ThreadTest01 {
public static void main(String[] args) {
System.out.println("main begin!");
m1();
System.out.println("main over!");
}
private static void m1() {
System.out.println("m1 begin!");
m2();
System.out.println("m1 over!");
}
private static void m2() {
System.out.println("m2 begin!");
System.out.println("m2 over!");
}
}
只有一个main线程
多线程启用示例1
编写一个类,直接继承Thread,重写run方法
package BUPT20210106;
/*
实现线程的方法1
直接继承Java.lang.Thread,重写run方法
*/
public class ThreadTest02 {
public static void main(String[] args) {
//新建一个线程对象
MyThread myThread = new MyThread();
//myThread.run();//不启动线程,不会分配新的分支栈,跟调用普通程序没两样
//启动线程
//start的作用。启动线程,在JVM中开辟一个新的栈空间
//开启栈空间后,start方法就结束了,启动的线程会自动调用run方法
//分支和主同时工作,不过还是有个先后,每次输出有个多少的差异,不会同时结束
//因为控制台只有一个,某个线程会先抢到执行权
myThread.start();
for (int i = 0; i < 100; i++) {
System.out.println("主线程->" + i);
}
}
}
class MyThread extends Thread {
public void run() {
//分支线程中
for (int i = 0; i < 100; i++) {
System.out.println("分支线程->" + i);
}
}
}
多线程启动示例2
编写一个类,实现Java.lang.Runnable接口,实现run方法
package BUPT20210106;
public class ThreadTest03 {
public static void main(String[] args) {
Thread thread = new Thread(new Myrunnable());
thread.start();
for (int i = 0; i < 120; i++) {
System.out.println("主线程->" + i);
}
}
}
//可运行类,还不是一个线程
class Myrunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("分支线程->" + i);
}
}
}
使用匿名内部类
package BUPT20210106;
/*
匿名内部类
*/
public class ThreadTest04 {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("分支线程->" + i);
}
}
});
t.start();
for (int i = 0; i < 100; i++) {
System.out.println("主线程->" + i);
}
}
}
线程生命周期
获取线程的名字
package BUPT20210106;
/*
获取当前线程对象
*/
public class ThreadTest05 {
public static void main(String[] args) {
MyThread1 myThread = new MyThread1();
//修改线程的名字,如果不修改 默认为 Thread-0
myThread.setName("ttt");
//获取线程的名字
String name = myThread.getName();
System.out.println(name);
myThread.start();
for (int i = 0; i < 100; i++) {
System.out.println("主线程" + i);
}
}
}
class MyThread1 extends Thread {
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("分支线程" + i);
}
}
}
package BUPT20210106;
/*
获取当前线程对象
*/
public class ThreadTest05 {
//也在主线程里面
public void doSome() {
//不行
//this.getName();
//super.getName();
//可以获取当前线程名字
System.out.println("doSome"+Thread.currentThread().getName());
}
public static void main(String[] args) {
ThreadTest05 tt = new ThreadTest05();
tt.doSome();
Thread currentThread = Thread.currentThread();
System.out.println(currentThread.getName());
MyThread1 myThread = new MyThread1();
//修改线程的名字,如果不修改 默认为 Thread-0
myThread.setName("ttt");
//获取线程的名字
myThread.start();
for (int i = 0; i < 200; i++) {
System.out.println(currentThread.getName() + "->" + i);
}
}
}
class MyThread1 extends Thread {
public void run() {
for (int i = 0; i < 100; i++) {
Thread currentThread = Thread.currentThread();
System.out.println(currentThread.getName() + "->" + i);
//super和this在这里都可以,但是对于其他情况不一定适用的,只是因为这个地方写在了类里面
System.out.println(this.getName() + "--" + i);
System.out.println(super.getName() + "-" + i);
}
}
}
线程的sleep方法
可以作为定时器,每次遇到Thread.sleep,就会进入阻塞阶段
间隔特定时间,执行特定代码
package BUPT20210106;
public class ThreadTest06 {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "->" + i);
Thread.sleep(1000 * 2);
}
}
}
sleep面试题
该程序是否能让分支线程进入睡眠?
package BUPT20210106;
public class ThreadTest07 {
public static void main(String[] args) throws InterruptedException {
MyThread3 myThread3 = new MyThread3();
myThread3.setName("t3");
myThread3.start();
//调用sleep方法,但这个程序不会进入休眠状态
//sleep是静态方法,执行的时候还是会转为Thread.sleep
//让当前线程进入休眠,即main方法进入睡眠
//3秒后才输出hello world,myThread3并没有睡
myThread3.sleep(1000*3);
System.out.println("hello world!");
}
}
class MyThread3 extends Thread {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "->" + i);
}
}
}
中止睡眠
package BUPT20210106;
/*
唤醒正在睡眠的线程
这并不是终止线程的执行,
*/
public class ThreadTest08 {
public static void main(String[] args) {
Thread thread = new Thread(new Myrunnable2());
thread.setName("t");
thread.start();
//希望五秒后,t线程醒来
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//终止t线程的睡眠,干扰,通过报Java异常处理机制进行中断
thread.interrupt();
}
}
class Myrunnable2 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "->begin");
//这里只能try catch不能抛异常
//因为子类重写不能抛出比父类更多的异常
try {
//睡一天
Thread.sleep(1000 * 60 * 60 * 24);
} catch (InterruptedException e) {
//打印异常信息
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "->end");
}
}
输出
t->begin
java.lang.InterruptedException: sleep interrupted
at java.base/java.lang.Thread.sleep(Native Method)
at BUPT20210106.Myrunnable2.run(ThreadTest08.java:33)
at java.base/java.lang.Thread.run(Thread.java:832)
t->end
强制终止线程
package BUPT20210106;
/*
强行终止线程
*/
public class ThreadTest09 {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable3());
thread.setName("t");
thread.start();
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//5秒后强行终止t线程
thread.stop();//过时用法,示例用一用,容易丢数据
}
}
class MyRunnable3 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "->" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
合理终止线程执行
通过修改标记来执行
package BUPT20210107;
public class ThreadTest10 {
public static void main(String[] args) {
MyRunnable4 myRunnable4 = new MyRunnable4();
Thread t = new Thread(myRunnable4);
t.setName("tt");
t.start();
//模拟5秒
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//终止t的执行
myRunnable4.run = false;
}
}
class MyRunnable4 implements Runnable {
//做一个布尔标记
boolean run = true;
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if (run) {
System.out.println(Thread.currentThread().getName() + "->" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
//终止当前线程
return;
}
}
}
}
线程调度模型
常见的线程调度模型有哪些?
- 抢占式调度模型:
哪个线程的优先级比较高,抢到的CPU时间片的概宰就高一些/多一些。Java采用的就是抢占式调度模型。 - 均分式调度模型:
平均分配CPU时间片。每个线程占有的cPu时间片时间长度一样。平均分配,一切平等。
线程调度方法
1.2、java中提供了哪些方法是和线程调度有关系的呢?
- 实例方法:
void setPriority(int newPriority)设置线程的优先级int getPriority(获取线程优先级
最低优先级1
默认优先级是5最高优先级10
优先级比较高的获取cPu时间片可能会多一些。(但也不完全是,大概率是多的.) - 静态方法:
static void yield让位方法
暂停当前正在执行的线程对象,并执行其他线程
yield()方法不是阻塞方法。让当前线程让位,让给其它线程使用。yield()方法的执行会让当前线程从"运行状态"回到"就绪状态". 回到就绪后还可以再去参与抢占过程,和阻塞不一样的
线程的优先级
package BUPT20210107;
/*
关于线程的优先级,优先级较高的,抢CPU时间片相对多一些
*/
public class ThreadTest11 {
public static void main(String[] args) {
System.out.println("最高优先级" + Thread.MAX_PRIORITY);//最高为10
System.out.println("最低优先级" + Thread.MIN_PRIORITY);//最低为1
System.out.println("默认优先级" + Thread.NORM_PRIORITY);//默认为5
Thread currentThread = new Thread();
//默认为5
System.out.println(currentThread.getPriority());
}
}
人为可以设置优先级
package BUPT20210107;
/*
关于线程的优先级,优先级较高的,抢CPU时间片相对多一些
*/
public class ThreadTest11 {
public static void main(String[] args) {
//当前线程为主线程
Thread.currentThread().setPriority(1);
Thread currentThread = Thread.currentThread();
Thread t = new Thread(new MyRunnable5());
//t线程设置为10
t.setPriority(10);
t.setName("t");
t.start();
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "->" + i);
}
}
}
class MyRunnable5 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "->" + i);
}
}
}
观察结果可以发现设置了优先级更高的t抢得更快
线程让位
package BUPT20210107;
/*
当前线程暂停,回到就绪状态,让给其他线程
静态方法 Thread.yield()
*/
public class ThreadTest12 {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable6());
t.setName("t");
t.setPriority(10);
t.start();
for (int i = 1; i <= 1000; i++) {
System.out.println(Thread.currentThread().getName() + "->" + i);
}
}
}
class MyRunnable6 implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 1000; i++) {
if (i % 100 == 0) {
Thread.yield();//100的倍数,当前线程暂停一下,让给主线程
}
System.out.println(Thread.currentThread().getName() + "->" + i);
}
}
}
线程合并
package BUPT20210106;
/*
线程合并
*/
public class ThreadTest13 {
public static void main(String[] args) throws InterruptedException {
System.out.println("main begin!");
Thread thread = new Thread(new MyRunnable7());
thread.setName("t");
thread.start();
//栈之间进行协调
thread.join();//Thread合并到当前线程中,当前线程受阻塞,Thread线程执行直到结束
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "->" + i);
}
System.out.println("main over!");
}
}
class MyRunnable7 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "->" + i);
}
}
}
2021-01-07