JavaEE-线程&多线程编程(1)

通过并发编程模式,将复杂的任务分成多个部分分别交给不同的cpu运行能有效提高cpu利用率,否则会出现”一核有难,八方围观“的情况,多进程的编程模式就可以实现并发编辑模式,因为进程可以被调度到不同cpu上运行,此时就可以把cpu的核心很好的利用起来。但有个很严重的问题:在服务器开发时,经常会有同一时间来了很多客户端的情况,服务器如果只能利用一个cpu那效率会非常低,典型的解决办法是:每个客户端连接上服务器了都创建一个进程,客户端断开再销毁线程,如果有客户端频繁的来来去去,服务器就需要频繁的创建销毁进程,这个操作的消耗是非常大的,因此引入了线程(轻量级工程)来解决这个问题,相比进程,线程的创建销毁开销更小

一、认识线程(Thread)

1、线程的概念

一个线程就是一个”执行流“,每个线程之间都可以按照顺序执行自己的代码,多个线程之间同时执行着多份代码。

可以理解为,线程是进程的一部分,一个进程至少包含一个线程或多个线程,必须存在的线程称为主线程,上篇文章提到PCB是描述进程的结构体的,严格来讲,PCB描述的是一个线程的,多个PCB连在一起描述一个进程,当进程中只包含一个线程时也可以说该线程的PCB是描述该进程的。

每一组线程的内存指针和文件描述符表是相同的,但每一组线程有自己的属性。

同一个进程的若干线程是可以共用文件资源和内存资源的,比如线程1new的对象线程2是可以访问的,但每个线程都是独立在cpu上调度执行。因此很重要的两条:

1、进程是系统分配资源的基本单位。 2、线程是系统调度执行的基本单位。

那么为什么说线程相比进程更加轻量级呢?为什么说线程创建销毁的开销比进程小?

因为一个进程包含多个线程,当进程创建销毁时要重新进行资源分配,而线程创建销毁时相当于资源已经有了,省去了分配资源那一步。

多线程编程会出现一个线程报错,如果不能很好的处理捕获,会导致进程直接退出,其他线程也不能运行的问题,相比来讲多进程编程独立性更好,进程之间互不影响,实际开发中按照具体情况择优选择。

到这了总结一下,高频的面试题:说说进程与线程之间的区别。重点!!

1、定义:进程是资源分配的基本单位,而线程系统调度执行的基本单位。

2、资源分配:每个进程拥有独立的地址空间和系统资源,而线程共享进程的资源。

3、调度:进程切换时需要保存和恢复整个进程的上下文信息,开销较大;线程切换时只需保存和切换线程的上下文信息,开销较小。

4、安全性:进程拥有独立的地址空间,当单个进程崩溃时不会影响到其他进程,安全性较高,而线程共享资源,当一个线程崩溃可能会影响到其他线程。

二、代码实现多线程

线程本身是操作系统提供的,操作系统提供了api来操控线程,JVM就对线程的api进行了封装,此处使用THread类表示线程。

1、代码解读

class MyThread extends Thread{//此处的Thread来自于标准库
    @Override
//run方法描述了该线程要执行什么任务
    public void run() {//这里重写父类的run方法
        System.out.println("第一种方法,创建新的类继承Thread后重写run方法");
    }
}
public class demo1 extends Thread{

    public static void main(String[] args) {
        Thread t = new MyThread();//实例化对象
        t.start();
        System.out.println("主线程运行");
    }
}

上述代码中有两段线程,一个是main方法中的主线程,一个是t线程,此时的运行结果:

修改一下代码,使这两个线程一直运行,看看效果

class MyThread extends Thread{
    @Override
    public void run() {
        while(true){ //写死循环让线程一直运行
            System.out.println("t线程");
            try {
                sleep(3000);//sleep方法为让线程进入堵塞状态,时间为3000ms也就是3s,时间结束后继续执行
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
       
    }
}
public class demo1 extends Thread{

    public static void main(String[] args)  {
        Thread t = new MyThread();
        t.start();
        while(true){
            System.out.println("主线程运行");
            try {
                sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        
    }
}

根据运行结果可以看出,线程的运行顺序是无序的,在操作系统内部也成为抢占式执行。任何一个代码在执行一个代码的过程中都可能被其他线程抢占它的资源,cpu就给别的线程执行了。
重点!

run方法只是描述了该线程该去干什么,重点在于t.start()干了什么,调用了操作系统提供的”创建线程“api,在内核中创建对应的pcb,并且把pcb加入到链表中,进一步系统调度到这个线程后就会执行上述run方法中的逻辑。

run不是被start调用的,run是在被创建出的线程里调用的。像这种创建好方法交给系统来带调用的函数称为回调函数。java数据结构中的比较器也是类似于回调函数的效果。

2、创建线程的代码写法

上面给大家介绍了第一种写法:创建Thread子类,重写run方法;那么给大家介绍第二种方法:

(2)通过runnable接口创建线程

首先创建一个子类实现runnable接口,runnable的作用是描述了一个任务,这个任务和具体的执行机制无关。

第二步,在子类中实现run方法,也就是想要该线程做什么任务

第三步,在主线程实例化Thread对象时将子类的对象传入构造方法后,t.start

class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println("第二种方法:创建子类实现runnable接口");
    }
}
public class demo2 {
    public static void main(String[] args) {
        Thread t = new Thread(new MyThread());
        t.start();
        System.out.println("主线程运行");
    }
}

对于第一种写法,是Thread来记录需要做的任务,第二种方法是通过runnable来记录任务,Thread只负责执行。第二种写法将任务与本体分开,这种写法本质上是为了代码的解耦合。

(3)针对Thread创建匿名内部类的方法

创建方法:在实例化Thread对象时直接在其后面创建匿名内部类,在类里重写run方法

public class demo3 {
    public static void main(String[] args) {
        Thread t = new Thread(){
            @Override
            public void run() {
                System.out.println("第三种,通过匿名内部类的方法");
            }
        };
        t.start();
        System.out.println("主线程运行");
    }
}

(4)针对runnable创建匿名内部类

Thread t = new Thread(new Runnable() {
            @Override
            public void run() {

            }
        });

(5)通过lambda表达式

这种方法是最推荐也是最常用的一种方法:使用lambda表达式代替刚才run的位置

public class demo4 {
    public static void main(String[] args) {
        Thread t = new Thread(() ->{
            
        });
    }
}

是这个样子,要注意lambda的写法是否正确。

总结:以上五种写法,本质上都是要把线程执行的任务表示出来,通过Thread的start来创建启动系统中的线程。

下篇文章将进一步解读线程。

感谢观看

道阻且长,行则将至

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值