重庆 网站备案,如何建立一个永久网站,昆明网络公司网站建设,一键做网站的软件1.认识线程
我们在之前认识了什么是多进程#xff0c;今天我们来了解线程。
一个线程就是一个 执行流. 每个线程之间都可以按照顺讯执行自己的代码. 多个线程之间 同时 执行 着多份代码.
引入进程这个概念#xff0c;主要是为了解决并发编程这样的…1.认识线程
我们在之前认识了什么是多进程今天我们来了解线程。
一个线程就是一个 执行流. 每个线程之间都可以按照顺讯执行自己的代码. 多个线程之间 同时 执行 着多份代码.
引入进程这个概念主要是为了解决并发编程这样的问题。因为cpu进入了多核心的时代要想进一步提高程序的执行速度,就需要充分的利用CPU的多核资源。 其实多进程编程已经可以解决并发编程的问题了它已经可以利用起来cpu多核资源了但是问题是进程太重了消耗资源多、速度慢 创建一个进程开销比较大。 销毁一个进程开销也比较大。 调度一个进程开销还比较大。 说进程重主要就是重在资源分配/回收上。
线程应运而生线程也叫做轻量级进程 解决并发编程问题的前提下让创建销毁调度的速度更快一些 线程为啥更轻把申请资源/释放资源的操作给省下了。
1.1 进程和线程的区别 进程是包含线程的。每个进程至少有一个线程存在即主线程。 进程和进程之间不共享内存空间。同一个进程的线程之间共享同一个内存空间。 进程是系统分配资源的最小单位线程是系统调度的最小单位。 光靠文字可能有点抽象我们举个例子
多进程 多线程
在多进程中启用了两套院子那么启用的成本是比较大的耗费的时间也是比较多的但是在第二套中院子和运输材料的通道都是公用的那么就节省了成本。
在启动一个新的生产线时就不需要重新启动一个院子而是在原来的院子里启用节省了许多的成本。
线程和进程的关系是进程包含线程 一个进程可以包含一个线程,也可以包含多个线程但是不能没有。 对比下来主要的优势在于 只有第一个线程启动的时候开销是比较大的但是后续线程就省事了.不论是启动还是关闭耗费的资源都比启动/关闭一个进程要小。 同一个进程里的多个线程之间,共用了进程的同一份资源(主要指的是内存和文件描述符表)。这样这一部分资源就不需要重新启动或关闭。 操作系统,实际调度的时候,是以线程为单位进行调度的。
之前介绍的,PCB里的状态上下文优先级记账信息都是每个线程有自己的。各自记录各自的但是同一个进程里的PCB之间, pid是一样的内存指针和文件描述符表也是一样的。 那么既然线程这么好可不可以无限制的在一个进程中增加线程呢
并不可以线程如果太多核心数量有限那么不少的开销就会浪费在线程调度上了但是在多进程中就不会出现这样的状况。
线程模型,天然就是资源共享的.多线程争抢同一个资源(同一个变量)非常容易触发的. 进程模型,天然是资源隔离的.不容易触发.进行进程间通信的时候,多个进程访问同一个资源,可能会出问题.
1.2 多线程编程
本身关于线程的操作操作系统提供的API我们只需要学习Java提供的API就好了。
Java操作多线程,最核心的类 Thread 先在src下创建一个包接着再创建一个类 创建好主函数后我们新建一个Thread的对象
Thread t new Thread();
但是我们还需要一个类新建一个Mythread类并且重写run方法
class MyThread extends Thread{Overridepublic void run() {System.out.println(hello world);}
}
然后在main中开始启动一个特殊的方法
t.start;
完整的代码
class MyThread extends Thread{Overridepublic void run() {System.out.println(hello world);}
}
public class ThreadDemo1 {public static void main(String[] args) {Thread t new MyThread();t.start();System.out.println(hello main);}
}
这样一个代码就新启动了一个线程使得打印hello world和打印hello main是以完全不同的方式来完成的。
start这里的工作就是创建了一个新的线程新的线程负责执行重写过后的t.run。
具体的执行方法就是start这个方法会调用操作系统的API通过操作系统内核创建新线程的PCB并且把要执行的指令交给PCB当PCB被调度到CPU上执行的时候也就执行到了线程run方法中的代码了。
通过具体的结果两个线程是同时进行的并且可以看做是一次运行时无序可能先打印world也可能先打印main。
但是运行的时候不一定谁先谁后 操作系统调度线程的时候,抢占式执行具体哪个线程先上,哪个线程后上,不确定,取决于操作系统调度器具体实现策略. 虽然有优先级,但是在应用程序层面上无法修改. 从应用程序(代码)的角度,看到的效果,就好像是线程之间的调度顺序是随机的一样.
内核里本身并非是随机.但是干预因素太多,并且应用程序这一层也无法感知到细节,就只能认为是随机的了。为啥会有线程安全问题?罪魁祸首万恶之源,就是这里的抢占式执行随机调度。 start和run的区别 start是真正创建了一个线程(从系统这里创建的)线程是独立的执行流。 run 只是描述了线程要干的活是啥如果直接再main中调用run此时没有创建新线程全是main线程一个人干活。相当于还是单线程。 可以使用jdk自带的工具jconsole查看当前的java进程中的所有线程. 这里面就可以看到进程。同时进程中还有很多个线程。除了我们使用的其他的都是JVM自带的 1.3 多线程的五种创建方法
1.继承Thread重写run方法
class MyThread extends Thread{Overridepublic void run() {System.out.println(hello world);}
}
public class ThreadDemo1 {public static void main(String[] args) {Thread t new MyThread();t.start();System.out.println(hello main);}
}
也就是上面详细介绍的方法。
2.实现 Runnable 接口
class MyRunnable implements Runnable{Overridepublic void run() {System.out.println(hello thread);}
}
public class ThreadDemo2 {public static void main(String[] args) {Runnable runnable new MyRunnable();Thread t new Thread(runnable);t.start();}
}Runnable 作用是描述一个“要执行的任务”然后把这个任务交给Thread来执行。
好处就是这样写可以解耦合让线程和线程之间干的活要分开。 3.使用匿名内部类,继承 Thread
public class ThreadDemo3 {public static void main(String[] args) {Thread t new Thread(){Overridepublic void run() {System.out.println(hello);}};t.start();}
}这里面创建了一个Thread的子类并且创建了子类的实例让 t 引用指向该实例。 4.使用匿名内部类,实现 Runable
public class ThreadDemo4 {public static void main(String[] args) {Thread t new Thread(new Runnable() {Overridepublic void run() {System.out.println(hello demo4);}});t.start();}
}这个写法和2本质相同只不过是把Runnable任务交给匿名内部类的语法。
此处是创建了一个类实现Runnable同时创建了类的实例并且传给Thread的构造方法。 5.使用 Lambda 表达式推荐
public class ThreadDemo5 {public static void main(String[] args) {Thread t new Thread(() - {System.out.println(hello demo5);});t.start();}
}
使用lambda表达式来描述直接把lambda传给Thread构造方法。