环保局 网站建设,网站开发与设计实验报告,路由器做网站服务器吗,网站建设与网站设计概述 多线程技术#xff1a;基于软件或者硬件实现多个线程并发执行的技术 线程可以理解为轻量级进程#xff0c;切换开销远远小于进程 在多核CPU的计算机下#xff0c;使用多线程可以更好的利用计算机资源从而提高计算机利用率和效率来应对现如今的高并发网络环境 并发编程…概述 多线程技术基于软件或者硬件实现多个线程并发执行的技术 线程可以理解为轻量级进程切换开销远远小于进程 在多核CPU的计算机下使用多线程可以更好的利用计算机资源从而提高计算机利用率和效率来应对现如今的高并发网络环境 并发编程核心三要素 原子性 原子即一个不可再被分割的颗粒。在Java中原子性指的是一个或多个操作要么全部执行成功要么全部执行失败有序性 程序执行的顺序按照代码的先后顺序执行。处理器可能会对指令进行重排序可见性 当多个线程访问内存中同一个变量时如果其中一个线程对其作了修改其他线程能立即获取到最新的值并行 VS 并发 并行在同一时刻有多个指令在多个CPU上同时执行 并发在同一时刻有多个指令在单个CPU上交替执行 同步 VS 异步 阻塞 VS 非阻塞 同步和异步指的对于消息结果的获取是客户端(调用者)主动获取[同步]还是由服务端(提供者)主动通知客户端[异步];阻塞和非阻塞指的一个是客户端(调用者)等待消息处理时的本身状态是挂起[阻塞]还是继续处理其他事[非阻塞]; 同步阻塞:客户端主动发起获取结果,而同时一直在等待应答结果同步非阻塞:客户端主动发起获取结果,而同时不断轮询查看发起的请求是否有应答结果异步阻塞:客户端发出请求后,服务端会主动通知,而客户端同时一直在等待通知异步非阻塞:客户端发出请求后,服务端会主动通知,而客户端在获取服务端通知之前已经去处理其他事情进程 VS 线程 进程是系统运行的基本单位 独立性: 进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位动态性: 进程的实质是程序的一次执行过程进程是动态产生动态消亡的并发性: 任何进程都可以同其他进程一起并发执行线程是进程中的单个顺序控制流是一条执行路径 单线程: 一个进程如果只有一条执行路径则称为单线程程序多线程: 一个进程如果有多条执行路径则称为多线程程序悲观锁 VS 乐观锁 悲观锁 每次操作都会加锁,会造成线程阻塞乐观锁 每次操作不加锁而是假设没有冲突而去完成某项操作,若因为冲突导致失败就重试,直到成功为止,不会造成阻塞 线程安全 线程安全问题指的是内存的安全,在每个进程的内存空间中都会有一块共享区域称为堆内存,进程内的所有线程都可以访问到该区域这就是造成线程安全问题的潜在原因。所以在堆内存中的数据由于可以被任何线程访问到,在没有限制的情况下存储在内存的数据(主要是全局变量和静态变量)可能被别的线程修改。因此在多线程场景下,我们需要通过相关限制实现线程安全 上下文切换线程在执行过程中都会有自己的运行条件和状态这些运行条件和状态我们就称之为线程上下文这些信息例如程序计数器、虚拟机栈、本地方法栈等信息。当出现以下几种情况时线程就会从占用CPU状态中退出: 线程主动让出CPU例如调用wait或者sleep等方法线程的CPU 时间片用完 而退出CPU占用状态 (因为操作系统为了避免某些线程独占CPU导致其他线程饥饿的情况就设定的例如时间分片算法)线程调用了阻塞类型的系统中断例如IO请求等线程被终止或者结束运行上述的前三种情况都会发生上下文切换为了保证线程被切换在恢复时能够继续执行所以上下文切换都需要保存线程当前执行的信息并恢复下一个要执行线程的现场。所以会占用CPU和内存资源频繁的进行上下文切换就会导致整体效率低下死锁指多个进程在运行过程中因争夺资源而造成的一种僵局当进程处于这种僵持状态时若无外力作用它们都将无法再向前推进。死锁的必要条件包括 互斥条件进程要求对所分配的资源进行排它性控制即在一段时间内某资源仅为一进程所占用请求和保持条件当进程因请求资源而阻塞时对已获得的资源保持不放不剥夺条件进程已获得的资源在未使用完之前不能剥夺只能在使用完时由自己释放环路等待条件在发生死锁时必然存在一个进程--资源的环形链 线程分类
普通线程即前台线程或者用户线程;即应用程序运行的线程守护线程即服务线程或者后台线程;优先级较低,作用是为其他线程的运行提供便利服务 JRE判断程序是否执行结束的标准是所有的前台执行线程执行完毕,而不管后台线程守护线程的状态守护线程最典型的应用就是GC除虚拟机提供的如GC守护线程外开发者也可以设置线程为守护线程 线程生命周期(线程状态) 根据sun官网可知,线程生命周期在java中有以下几种状态:初始(NEW) 可运行(RUNNABLE)运行(Running)阻塞(BLOCKED)和终止(TERMINATED)
初始(NEW): 如果创建Thread类的实例但在调用start()方法之前线程处于初始(NEW)状态。可运行(RUNNABLE): 调用start()方法后线程处于runnable状态但线程调度程序尚未选择它作为正在运行的线程运行(RUNNING): 如果线程调度程序已选择它则线程处于运行状态阻塞(BLOCKED): 此时线程仍处于活动状态但当前没有资格运行的状态终止(TERMINATED): 当run()方法退出时线程处于终止或死亡状态 线程间通信方式
很多业务场景下某个任务都是通过多个线程一起完成,此时就需要线程之间进行通信一起完成该任务,线程通信就是当多个线程共同操作共享的资源时互相告知自己的状态以避免资源争夺;线程通信主要可以分为三种方式分别为共享内存、消息传递和管道流。
共享内存线程之间共享程序的公共状态线程之间通过读-写内存中的公共状态来隐式通信 volatile关键字,该方式是最简单的一种实现方式volatile修饰的变量是共享内存,保证内存可见性,让多个线程共享一个标志位,当标志位更改的时候就执行不同的线程消息传递线程之间没有公共的状态线程之间必须通过明确的发送信息来显示的进行通信 Object类的 wait/notify方法,执行wait(方法,该线程释放锁资源,然后执行notify() 方法,唤醒其他的线程condition类的 await/signal方法,condition通过lock对象创建,await操作会立刻释放掉锁,进入阻塞状态,signal会唤醒等待队列中的头节点(失败就依次唤醒)管道流: 管道的输入和输出实际上使用的是一个循环缓冲数组来实现的 创建线程
通过继承Thread,重写其中的run方法,通过调用start方法启动线程时通过实现Runnable接口,重写其中的run方法,并将自定义类的实例作为一个参数传入Thread获取线程实例,再调用start方法启动线程通过实现callable接口的,重写其中的call方法。新建Callable的实例,将Callable传入FutureTask得到FutureTask实例,再将FutureTask实例传入Thread获得Thread实例,调用Thread的start方法启动线程,并通过FutureTask的get方法来获取返回值通过线程池提前为我们准备好线程,我们需要的时候直接取用就可以了,而不需要自己创建 线程Thread常用API
start()在使用 new 关键字创建一个线程后New 状态,只有在 start() 方法执行后才表示这个线程可运行了Runnable 状态至于何时真正运行还要看线程调度器的调度;在线程死亡后,不要再次调用 start() 方法。只能对新建状态的线程调用且只能调用一次 start() 方法否则将抛出 IllegalThreadStateException 异常run()启动线程是 start() 方法而不是 run() 方法。如果直接调用 run() 方法这个线程中的代码会被立即执行多个线程就无法并发执行了join()等待该线程完成的方法其他线程将进入等待状态Waiting 状态通常由使用线程的程序线程调用如将一个大问题分割为许多小问题要等待所有的小问题处理后再进行下一步操作sleep()主动放弃占用的处理器资源该线程进入阻塞状态Blocked 状态指定的睡眠时间超时后线程进入就绪状态Runnable等待线程调度器的调用。yield()主动放弃占用的处理器资源线程直接进入就绪状态Runnable等待线程调度器的调用。可能出现当线程使用yield方法放弃执行后线程调度器又将该线程调度执行interrupt()没有任何强制线程终止的方法这个方法只是请求线程终止这个方法并没有实际的用途还有 isInterrupted() 方法检查线程是否被中断setDaemon()设置守护进程该方法必须在 start() 方法之前调用判断一个线程是不是守护线程可以使用 isDaemon() 方法判断setPriority()设置线程的优先级理论上讲线程优先级高更容易被执行。每个线程默认的优先级和父线程如 main 线程、普通优先级的优先级相同线程优先级区间为 1~10三个静态变量MIN_PRIORITY 1、NORM_PRIORITY 5、MAX_PRIORITY 10。使用 getPriority() 方法可以查看线程的优先级isAlive()检查线程是否处于活动状态如果线程处于就绪、运行、阻塞状态方法返回 true如果线程处于新建和死亡状态方法返回 false 线程常见问题
sleep VS wait
sleep不会释放锁只是单纯休眠一会,在给定时间后就会苏醒而wait则会释放锁若wait没有设定时间只能通过notify或者notifyAll唤醒wait常用于线程之间的通信或者交互而sleep单纯让线程让出执行权。sleep是Thread的方法因为sleep要做的仅仅是让线程休眠所以不涉及任何锁释放等逻辑放在Thread上最合适而wait是Object的方法调用wait时会释放锁并让对象进入WAITING状态会涉及到资源释放等问题所以我们需要将wait放在Object 类上
run VS start
run: 仅仅是方法若直接通过对象调用run方法那么该方法和普通方法没有任何差别它仅仅是一个名字为run的普通方法start: 开启线程方法线程就会从用户态转内核态创建线程并在获取CPU时间片的时候开始运行然后运行run方法
Thread,Runnable,Callable区别 Thread 和 Runnable 区别 Thread 是类而Runnable是接口如果有其他类需要继承的话肯定是用Runnable接口了Thread本身是实现了Runnable接口的类。我们知道“一个类只能有一个父类但是却能实现多个接口”因此Runnable具有更好的扩展性Callable和Runnable区别 相同点 都是接口实现,具有良好的扩展性不同点 Callable可以在任务结束的时候提供一个返回值,Runnable无法提供这个功能Callable的call方法可以抛出异常,而Runnable的run方法不能抛出异常