💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
### 一、简介 > 多线程是程序中有多个线程流在同时调度资源。线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),一个线程可以创建和撤销另一个线程。 ### 二、线程和进程 1.进程是程序运行的实例,每一个进程都有自己的内存空间,包含内容和数据,不同进程间有相互独立的地址空间 2.线程是CPU调度的基本单位,每一个线程都有顺序执行的,线程有共享资源和锁机制。 3.两者区别和联系 (1) 划分尺度:线程更小,所以多线程程序并发性更高; (2) 资源分配:进程是资源分配的基本单位,同一进程内多个线程共享其资源; (3) 地址空间:进程拥有独立的地址空间,同一进程内多个线程共享其资源; (4) 处理器调度:线程是处理器调度的基本单位; (5) 执行:每个线程都有一个程序运行的入口,顺序执行序列和程序的出口,但线程不能单独执行,必须组成进程, 一个进程至少有一个主线程。简而言之,一个程序至少有一个进程,一个进程至少有一个线程 #### 三、线程的创建和启动 线程创建有2中方式,一是实现Runnable接口,实现run()方法,然后创建一个Thread对象,将 而是继承Thread类,实现run方法。两者都需要调用start()方法来启动线程。下面是两者方法的程序实现: 1.继承Thread方式 ~~~ public class Demo1 extends Thread { @Override public void run() { for(int i=0;i<10;i++){ System.out.println(Thread.currentThread().getName()+i); } } } ~~~ 2.实现Runnable接口 ~~~ public class Demo2 implements Runnable { @Override public void run() { for(int i=0;i<10;i++){ System.out.println(Thread.currentThread().getName()+i); } } } ~~~ ### 四、线程的状态 线程运行时也有它的生命周期,线程会要经历开始(等待),运行,挂起(阻塞)和终止四种不同的状态,且四种状态可以由Thread来自由控制,下面给出Thread类控制各个状态的方法 > 1.线程开始 start()/run(); 2.线程挂起和唤醒 resume()/suspend()-已过时 sleep(); 3.线程终止 stop()不建议使用 interupt(); 4.其他与线程状态相关的方法 isAlive():判断线程的状态是否还活着 join():调用某线程的该方法,将当前线程与该线程“合并”,即等待该线程结束,再恢复当前线程的运行; yield():线程的让步,即让出当前线程的资源给其他线程使用。 状态图如下: ![这里写图片描述](https://box.kancloud.cn/2016-04-13_570df38a5a3dd.jpg "") 从图中可看出线程在建立后并不马上执行run方法中的代码,而是处于等待状态。线程处于等待状态时,可以通过Thread类的方法来设置线程不各种属性,如线程的优先级(setPriority)、线程名(setName)和线程的类型(setDaemon)等。 - 当调用start方法后,线程开始执行run方法中的代码。线程进入运行状态。可以通过Thread类的isAlive方法来判断线程是否处于运行状态。 - 当线程处于运行状态时,isAlive返回true, - 当isAlive返回false时,可能线程处于等待状态,也可能处于停止状态。 ***注意:***一但线程开始执行run方法,就会一直到这个run方法执行完成这个线程才退出。但在线程执行的过程中,可以通过两个方法使线程暂时停止执行。这两个方法是suspend和sleep。 在使用suspend挂起线程后,可以通过resume方法唤醒线程。而使用sleep使线程休眠后,只能在设定的时间后使线程处于就绪状态(在线程休眠结束后,线程不一定会马上执行,只是进入了就绪状态,等待着系统进行调度)。 **在使用sleep方法时有两点需要注意:** 1. sleep方法有两个重载形式,其中一个重载形式不仅可以设毫秒,而且还可以设纳秒(1,000,000纳秒等于1毫秒)。但大多数操作系统平台上的Java虚拟机都无法精确到纳秒,因此,如果对sleep设置了纳秒,Java虚拟机将取最接近这个值的毫秒。 1. 在使用sleep方法时必须使用throws或try{…}catch{…}。因为run方法无法使用throws,所以只能使用try{…}catch{…}。当在线程休眠的过程中,使用interrupt方法中断线程时sleep会抛出一个InterruptedException异常。sleep方法的定义如下: publicstaticvoid sleep(long millis) throws InterruptedException publicstaticvoid sleep(long millis, int nanos) throws InterruptedException 下面的举例为线程的合并的实现: #### A线程: ~~~ public class DemoA extends Thread { public DemoA(String name){ super(name); } @Override public void run() { for(int i=0;i<10;i++){ System.out.println(Thread.currentThread().getName()+"-"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } ~~~ #### B线程: ~~~ public class DemoB extends Thread { private Thread a; public DemoB(String name,Thread a){ super(name); this.a=a; } @Override public void run() { for(int i=0;i<10;i++){ System.out.println(Thread.currentThread().getName()+"-"+i); try { Thread.sleep(1000); if(i==4){ a.join(); } } catch (InterruptedException e) { e.printStackTrace(); } } } } ~~~ ### 五、线程的属性 线程的属性包括线程的优先级,守护线程等,明白线程的属性的作用,可以更灵活的设置线程的执行状态。 #### 1.线程的优先级 每一个线程都会对应一个优先级,默认情况下,新创建的线程会继承他父类的优先级。可以利用setPriority方法来修改线程的优先级的高低。修改的范围可以使MIN_PRIORITY和MAX_PRIORITY之间的任意级别 下面为线程优先级设置的实例: ***定义的线程类:*** ~~~ public class Demo3 implements Runnable { @Override public void run() { for(int i=0;i<20;i++){ System.out.println(Thread.currentThread().getName()+"-"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } ~~~ ***线程优先级的设置:*** ~~~ //最小级别 Thread t1=new Thread(new Demo3()); t1.setPriority(Thread.MIN_PRIORITY); t1.start(); //正常 Thread t2 =new Thread(new Demo3()); t2.setPriority(Thread.NORM_PRIORITY); t2.start(); //最大级别 Thread t3 =new Thread(new Demo3()); t3.setPriority(Thread.NORM_PRIORITY); t3.start(); } ~~~ 注意:在使用线程优先级时,应避免常犯的一个错误,假如高优先级的线程处于非活跃状态,低优先级的线程也不可能会执行,而资源调度线程时会在高优先级的线程中选择,这会是低优先级的线程饿死。 ##### 2.守护线程 一般线程要转换为守护线程,可通过setDaemon(true);来设置,守护线程不会去访问固有资源,如文件,数据库等。其作用是为其他线程提供服务,如计时器的例子,守护线程可定时发送 信号给实现计时的线程。 #### 3.未捕获异常的处理器 线程实现run方法时不会抛出可被检测的异常,而抛出的异常不能被检测到会导致线程终止,从而是程序死亡。 java提供了一个未捕获异常的处理器,该处理器为Thread.UncaughTExceptionHandler接口的类。从JSE5.0后,提供了setUncaughTExceptionHandler方法为线程安装处理器.