线程
在Java中,鼓励多线程编程。进程可以满足并发编程,但是效率不高(创建、销毁、调度时间都比较长,这些都消耗在申请资源上了),而线程就不一样。
线程也叫"轻量级进程",创建、销毁、调度都更快。但是它不能独立存在,而是依附于进程(也就是说进程包含线程),一个进程可以一个或多个线程,前面谈到的进程调度都是基于一个进程只有一个线程的情况下,实际上一个进程有多个线程,每个线程都是可以独立调度的,每个线程也有状态、优先级、上下文、记账信息,每个PCB对应到每个线程上,pid是与进程共用同一份的(内存指针、文件描述符表也是)。
总的来说就是:进程包含线程-》一个进程由多个PCB共同表示-》每个PCB就用来表示一个线程-》每个线程有自己的状态、优先级、上下文、记账信息-》每个线程都可以独立的去CPU上调度执行-》这些PCB共用了同样的内存指针、文件描述符表-》创建线程/PCB不需要重新申请资源-》创建销毁效率更高
总结线程特点:
- 每个线程都可以独立的去CPU上调度执行。
- 同一个进程的多线程之间共用同一份内存空间、文件资源......(所以创建线程就不需要重新申请资源,直接复用进程的资源,使得线程速度更快)
- 然而不是线程越多效率越快,当增加到一定数目时,效率无法提升,反而会调度线程太多,使调赴花销更大 。
- 当两个线程同时访问变量时也会出现问题,线程不安全问题。
- 如果某个线程出现异常:一个线程抛出异常,如果没有妥善处理就容易把整个进程崩溃。
进程与线程的区别(重点)
- 进程包含线程;
- 进程和线程都是实现并发并发编程,但是线程比进程更高效、轻量;
- 同一个进程的线程之间共用一份资源(内存+硬盘),省去资源的开销;
- 进程和进程之间是独立的,线程和线程(同一个进程中)可能会相互影响;
- 进程时资源分配的基本单位,线城市调度执行的基本单位;
多线程
Java如何进行多线程编程的?
线程是操作系统的概念,操作系统提供一些API可以操作线程,Java针对API进程封装。Thread类就是操作系统提供的API进行封装和抽象。
1.多线程程序
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread t = new MyThread();
t.start();
while (true){
sleep(1000);
System.out.println("hello main");
}
}
}
class MyThread extends Thread{
@Override
public void run() {
while (true) {
try {
sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("hello thread");
}
}
}
上面这个程序运行就可以看到,这两个线程是"同时执行"的,看到的结果是两边的日志都在交替打印,---》可以得到的是每个线程都是独立执行的逻辑(独立的执行流)
start可以创建一个新的线程,如果将start改成run,此时代码就不会创建新线程,只有主线程,这个主线程只能执行完run,才能往后继续执行。

上面是使用idea观察多线程,也可以通过 jconsole (jdk-》bin-》jconsole.exe)来观察进程里的多线程,如果不知道jdk安装在哪个路径下可以通过idea寻找。

然后再双击 jconsole.exe 出现以下页面,,如果没有出现以下页面那就是没有以管理员身份打开

2.创建线程
1.继承Thread,重写run
1)继承Thread来创建一个线程类;2)创建线程类的实例;3)调用start方法启动线程
继承Thread类,需要重写它里面的run方法,想要看到"同时"的效果,就需要使用sleep,之前在学C语言中的sleep,现在Thread类中有自己的sleep方法,使用Thread中的sleep会出现异常,需要解决异常可以抛出异常或者try/catch异常,如果是重写父类的run方法,父类的run方法没有throws,子类方法也就不能有,所以只能try/catch异常。main方法中就可以使用throws也可以使用try/catch。操作系统对于多个线程的调度顺序是不确定的/"随机的",运行结果取决于操作系统对线程调度的模块(调度器)


2.实现Runnable,重写run
1)实现Runable接口;2)创建Thread类实例,调用Thread的构造方法是将Runable对象作为target参数;3)调用start方法;
使用Runnable 的写法,和直接继承Thread 之间的区别就是解耦合(两个线程之间的关联程度)

3. 继承Thread,重写run(使用匿名内部类)
使用匿名内部类创建Thread子类对象
4.实现Runnable,重写run(使用匿名内部类)
使用匿名内部类创建Runnable子类对象
5.基于 lambda 表达式(推荐使用)
lambda表达式中直接重写run方法中的内容,不用再继承实现哪个类或者哪个接口。

3.Thread 的其他构造方法
Thread()//创建线程对象
Thread(Runnable target)//使用Runnable对象创建线程对象
Thread(String name)//创建线程对象,并命名
Thread(Runnable target, String name) //使用Runnable对象创建线程对象,并命名
4.Thread 常见的属性
ID//身份标识 getId()//获取id
名称 getName()//获得线程名称
状态 getState()//获得当前状态
优先级 getPriority()
是否后台线程 isDaemon()
是否存活 isAlive()
是否被中断 isInterrupted()
后台线程:后台进程不结束不影响整个进程的结束。
前台线程:如果前台线程没有执行结束,此时整个进程是一定不会结束的。
Thread 对象往往比系统中的线程更长,线程没了Thread 对象还在。