目录
1.线程的基本概念
线程是进程中的⼀个执⾏单元,负责当前进程中程序的执⾏,⼀个进程中⾄少有⼀个线程。⼀个进
程中是可以有多个线程的,这个应⽤程序也可以称之为多线程程序。
了解操作系统中的进程后,我们知道多进程实现了并发编程,提高了CPU的利用率
想要了解多线程,就必须弄清楚进程的创建过程:
1.创建一个PCB
2.给进程分配资源,赋值到PCB中
3.把PCB插入到链表中
进程销毁:
1.把PCB从链表上删除
2.把PCB中持有的资源释放
在这个过程中, 由于频繁的创建和删除进程,给进程分配空间资源消耗大,比较耗时间,就会很低效,为了解决这个问题于是就有了多线程
进程与线程
1. 线程包含在进程里,一个线程个包含多个线程,也可以包含一个线程.同一份进程中的这些线程共用一份资源,这里的资源包括内存和文件.
2.每个线程都是一个"执行流", 可以单独在CPU上进行调度
3.因为创建线程 销毁线程开销比较小,所以线程被称之为"轻量级进程"
缺点:缺点时线程之间会相互影响,当线程足够多的时候,效率无法继续提升,反而影响其他线程.因
为CPU时有限的,CPU核心数已经吃满了,进程的虚拟内存空间上限.
进程相当于工厂,想要扩大生产,就有两个方案,第一就是在创建一个工厂,第二个方案就是在原来的
厂里的空闲位置在设置一个生产线,每个成产线有独立进行生产,而这个生产线的原材料时共享的,
生产好的产品怎么运走,也是现成的,虽然还是这个工厂,但是生产线多了一个,生产效率还是提高了
不少!
区别:
1.进程包含线程
2.线程比进程更轻量,创建更快,销毁也更快
3.同一个进程的多个线程之间共用同一份内存/文件资源,进程与进程之间,则是独立的内存/文件资
4.进程时资源分配的基本单位,线程时调度执行的基本单位
在进程中说到的PCB,其实说的就是操作系统PCB,只不过钱买你没有明确点出,其实每个PCB就是一个线程
2.创建线程
创建线程时,并不是实例一个相关对象就创建了一个线程,想要创建一个线程必须要在该对象的基础上调用start方法,至于这个对象是什么,就要看下文的代码.
线程里有一个run方法,方法里的内容就是这个这个线程要干的事.一个线程最少就有一个线程,就是main线程,称作主线程,如果程序中有多个线程,操作系统调度线程的时候, 是一个随机过程,但不是严格上的数学随机.他们是"抢占式执行",所以线程容易出现各种变数
下面用五种方法创建线程,并证明线程的执行存在随机性
1.继承Thread,重写run方法
class MyThread extends Thread{ //Thread 为标准库对的类,来表示线程
@Override
public void run(){ //重写run方法 run就是线程要执行的工作
while(true){
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class TestDemo {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
while(true){
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2.实现Runnable,重写run方法
class MyRunnable implements Runnable{
@Override
public void run(){
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Demo2 {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();//要完成的工作
Thread t = new Thread(runnable);//提取出来传给Thread
t.start();
while(true){
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3. 使用匿名内部类,实现创建Thread子类的方式
public class Demo5 {
public static void main(String[] args) {
Thread t = new Thread(){
@Override
public void run(){
while(true){
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t.start();
}
}
4.使用内部类,实现Runnable接口的方式
public class Demo4 {
public static void main(String[] args) {
Thread t = new Thread(new Runnable(){
@Override
public void run(){
while(true){
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
}
}
5.lambda表达式
public class Demo3 {
public static void main(String[] args) {
Thread t = new Thread(()->{
while(true){
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
}
lambda本质上是一个"匿名函数", ()函数的形参 { }表示函数体,->表示特殊语法表示,表示它是一个lambda表达式.
3.串行执行与多线成执行速度对比
public class Demo6 {
public static final long COUNT = 1000_000_0000L;
public static void main(String[] args) {
concurrency();
}
//一个线程串行执行
public static void serial(){
long beg = System.currentTimeMillis();
long a = 0;
for(long i = 0;i < COUNT; i++){
a++;
}
a = 0;
for (long i = 0; i < COUNT; i++) {
a++;
}
long end = System.currentTimeMillis();
System.out.println("执行的时间间隔:" + (end - beg) + "ms");
}
//两个线程并发执行
public static void concurrency(){
long beg = System.currentTimeMillis();
Thread t1 = new Thread(() ->{
long a = 0;
for (long i = 0; i < COUNT; i++) {
a++;
}
});
Thread t2 = new Thread(() ->{
long a = 0;
for (long i = 0; i < COUNT; i++) {
a++;
}
});
t1.start();
t2.start();
try {
t1.join();//这里面的join就是阻塞主线程,该线程执行完,主线程才开始继续执行
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("执行的时间间隔:" + (end - beg) + "ms");
}
}
运行上诉代码,一个串行线程执行上述程序,单个线程串行执行的代码平均花的时间是5200左右,两个线程并发执行平均花费的时间是2900ms,这个速度不是单纯的两倍的关系,是因为这里的并发执行,实际上是包含"并发"与"并行".当线程采用"并发"执行时,执行的时间并没有变化,只有当采用"并行"执行",这个花费的时间才会减少,所以不是简单的两倍关系.