目录
一 认识线程
概念
一个线程就是一个"执行流",每个线程之间都可以按照顺序执行自己的代码.多个线程之间"同时"执行着多
为什么要有线程
这里我们需要先需要了解进程
我们的操作系统对下要管理好各种硬件设备,对上要给软件提供稳定的运行环境;
而我们在运行一个软件的时候,就需要进行资源分配,而进程是操作系统进行资源分配的基本单位,此处涉及到的资源包括但不限于:内存,硬盘,CPU等
当进程多了我们就需要进行进程管理
所谓管理,就是分两步
1.描述一个进程:使用结构体/类,把一个进程有哪些信息,表示出来
2.组织这些进程:使用一定的数据结构,把这些结构体/对象,放到一起
一个进程的结构体(PCB)有哪些属性?
这里只挑几个核心的
1.pid:每个进程的唯一身份标识
2.内存指针:当前这个进程使用的那块内存
3.文件描述符表:
硬盘上存储的数据,就是以文件为单位进行整理;
进程每打开一个文件,就会产生一个"文件描述符"来标识这个被打开的文件;
但一个进程可能会打开很多文件,这样就会产生一组文件描述符,把这些文件描述符放到一个顺序表这样的结构里,就构成了文件描述符表
而每当我们创造一个进程的时候,就会消耗相对较多的资源,但我们可能会不能完全利用起来;
而且每次使用一个进程时,就得进行创建,不使用的时候就得进行销毁,这样是非常耗费时间的;
这个时候,我们就发明了线程,线程是一小块一小块的存在于进程里的;
当我们创建第一个线程的时候,操作系统就会申请系统资源去建立一个进程,当后续在创建线程,就不必再申请资源了,创建和销毁的效率就提高了不少
所以线程是操作系统调度运行的基本单位
面试题:进程和线程的区别
1.进程包含线程
2.进程具有自己独立的内存空间和文件描述符表.同一个进程中的多个线程之间,共享同一份地址空间和文件描述符表
3.进程是操作系统资源分配的基本单位,线程是操作系统调度执行的基本单位
4.进程之间具有独立性,一个进程挂了,不会影响到别的进程;同一个进程里的多个线程之间,一个线程挂了,可能会把整个进程带走,影响到其他线程
二 Java创建线程
1.继承Thread重写run;
class MyThread1 extends Thread{
@Override
public void run() {
while(true){
System.out.println("main");
}
}
}
public class ThreadDemo1 {
public static void main(String[] args) {
MyThread1 thread = new MyThread1();
thread.start();
while(true){
System.out.println("main");
}
}
}
2.实现Runnable,重写run
class MyThread implements Runnable{
@Override
public void run() {
while(true){
System.out.println("thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class ThreadDemo1{
public static void main(String[] args) throws InterruptedException {
//实现Runnable的实例
MyThread2 myThread = new MyThread2();
//将Runnable的实例作为参数
Thread thread = new Thread(myThread2);
thread.start();
while(true){
System.out.println("main");
Thread.sleep(1000);
}
}
}
3.继承Thread,匿名内部类
public class ThreadDemo3 {
public static void main(String[] args) {
Thread thread = new Thread(){
@Override
public void run() {
while(true){
System.out.println("thread");
}
}
};
thread.start();
while(true){
System.out.println("main");
}
}
}
4.实现Runnable,匿名内部类
public class ThreadDemo4 {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while(true){
System.out.println("thread");
}
}
});
thread.start();
while (true){
System.out.println("main");
}
}
}
5.lambda表达式
也是最常用的一个
public class ThreadDemo5 {
public static void main(String[] args) {
Thread thread = new Thread(() ->{
while(true){
System.out.println("thread");
}
});
thread.start();
while(true){
System.out.println("main");
}
}
}
6.Callable
Callable的用法非常类似于Runnable,描述一个线程要干啥
Runnable通过run方法描述,返回值为void,
Callable通过call方法描述,返回值为一个泛型;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//Callable
public class ThreadDemo6 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<Integer> callable = new Callable<Integer>() {
int sum = 0;
//计算1+2+3.......+1000
@Override
public Integer call() throws Exception {
for(int i = 0; i <= 1000; i++){
sum += i;
}
return sum;
}
};
//进行打包
FutureTask<Integer> futureTask = new FutureTask<>(callable);
Thread thread = new Thread(futureTask);
thread.start();
//得到返回值
System.out.println(futureTask.get());
}
}
Thread不能直接传Callable,需要用FutureTask进行包装;
FutureTask的get方法在Thread的线程没有进行完时会进行阻塞等待
三 Thread
方法
isDaemon():
true表示是后台线程 false表示是前台线程
前台线程会阻止java进程结束 必须得java进程中所有的前台线程都执行完java进程才能结束;
创建的线程默认是前台的;
可以通过setDaemon设置成后台
常见构造方法
常用属性
start方法: 真正从系统这里,创建一个线程,新的线程将会执行run方法.
run方法: 表示了线程的入口方法是啥(线程启动起来,要执行哪些逻辑)
中断线程(isInterrupted)
public class ThreadDemo7 {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
//currentThread是获取当前线程实例
//isInterrupted就是thread对象里自带的一个标志位
while(!Thread.currentThread().isInterrupted()){
System.out.println("thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
thread.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
thread.interrupt();
}
}
interrupt方法的作用:
1.设置标志位为true
2.如果该线程正在阻塞(比如执行sleep),此时就会把阻塞状态唤醒;通过抛出异常的方式让sleep立即结束
注意:当sleep被唤醒的时候,sleep会自动把isInterrupted标志位给清空(true -> false)
等待一个线程(join)
public class ThreadDemo8 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
System.out.println("thread");
});
thread.start();
thread.join();
System.out.println("main");
}
}
main线程运行到join时会陷入阻塞,等待thread执行完
thread.join()会等到thread执行完
thread.join(time)在time内等待
线程的状态