多线程相对于其他Java知识点有一定的学习门槛,理解起来难度更大。在日常工作中,如果使用不当,会出现数据混乱、执行效率低(不如单线程运行)或死锁程序挂起等问题,因此掌握和理解多线程非常重要。本文从基本概念入手,由浅入深,讲解最新的并发模型,讲解线程的相关知识。概念化在这一节中,我将带您了解多线程中的几个基本概念。并发和并行意味着两个线程同时做事。并发就是一次做一件事,同时做另一件事,有调度。单核CPU不可能实现并行(微观上)。临界区临界区用于表示可被多个线程使用的公共资源或共享数据。但是每次,只有一个线程可以使用它。一旦临界区资源被占用,其他线程要想使用这个资源就必须等待。阻塞和非阻塞阻塞和非阻塞通常用来描述多线程之间的交互。例如,如果一个线程占用了一个临界区资源,那么所有其他需要这个资源的线程都必须在这个临界区等待,而等待会导致该线程挂起。这种情况称为阻塞。这时候,如果占用资源的线程一直不愿意释放资源,那么其他所有阻塞在这个临界区的线程都无法工作。阻塞意味着线程在操作系统级别被挂起。Blocking一般性能较差,调度需要80000个时钟周期左右。非阻塞允许多个线程同时进入临界区。死锁死锁是进程死锁的缩写,指的是多个进程在循环中等待被别人占用的资源而死锁的情况。Livelock假设有两个线程1和2,都需要资源A/B,假设线程1占用资源A,线程2占用资源B;由于两个线程都需要同时拥有这两个资源,它们才能工作,为了避免死锁,1号线程释放了A的资源占有锁,2号线程释放了B的资源占有锁;此时AB处于空闲状态,两个线程同时抢到锁,又出现了上面的情况。锁。简单打个比方,电梯遇人,一进一出,对面占道,两个人同时向一个方向让路,如此反复来回,还是堵死了路。如果在线申请遇到活锁问题,恭喜你中奖了。此类问题更难排除故障。Hunger饥饿是指一个或多个线程由于各种原因得不到需要的资源,导致无法执行。线程的生命周期在线程的生命周期中,会经历几种状态:创建、可运行和不可运行。创建状态当使用new操作符创建一个新的线程对象时,线程处于创建状态。处于创建状态的线程只是一个空的线程对象,系统不会为其分配资源。可运行态执行线程的start()方法会为该线程分配必要的系统资源,安排其运行,并调用线程体-run()方法,使该线程处于可运行态(Runnable)。这个状态是没有运行,因为线程可能实际上并没有运行。不可运行状态当以下事件发生时,处于运行状态的线程将进入不可运行状态:sleep()方法被调用;线程调用wait()方法等待特定条件的满足;线程输入/输出被阻塞;返回为可运行状态;处于睡眠状态的线程经过指定时间后;如果线程正在等待某个条件,则另一个对象必须通过notify()或notifyAll()方法将条件变化通知等待线程;如果线程因为输入输出被阻塞,等待输入输出完成。线程优先级线程优先级和设置线程优先级是为了方便系统在多线程环境下对线程进行调度,优先级高的线程会先执行。线程的优先级设置遵循以下原则:线程创建时,子线程继承父线程的优先级;线程创建后,可以通过调用setPriority()方法改变优先级;线程的优先级是1-10之间的正整数。线程调度策略线程调度器选择具有最高优先级的线程运行。但是,如果出现以下情况,线程的运行将被终止:在线程体内调用yield()方法,放弃占用CPU的权利;在线程体中调用sleep()方法,使线程进入休眠状态;由于I/O操作而阻塞的线程;另一个更高优先级的线程出现;在支持时间片的系统上,该线程的时间片用完了。单线程创建方式单线程创建方式比较简单,一般只有两种方式:继承Thread类,实现Runnable接口;这两个方法比较常用,Demo中没有,但是对于新手来说,需要注意的问题是:是否继承Thread类还是实现Runable接口,业务逻辑写在run方法,线程启动时执行start()方法;开启新线程不影响主线程的代码执行顺序,不会阻塞主线程的执行;新线程无法保证与主线程的代码执行顺序;对于多线程程序,从微观上看,一次只有一个线程在工作,多线程的目的是让CPU保持忙碌;查看Thread的源码可以看到,Thread类实现了Runnable接口,所以两者本质上是一个;PS:工作中也可以借鉴这种代码结构,为上层调用提供更多的选择,作为服务商的核心业务为什么要使用线程池进行常态化维护通过上面的介绍,可以开发多线程程序,何必引入线程池。主要原因是上述单线程方式存在以下问题:线程工作周期:线程创建所需时间为T1,线程执行所需时间为T2,线程销毁所需时间为T3,往往是T1+T3大于T2,所以频繁创建线程会额外消耗太多时间;如果有任务过来,再创建线程效率会很低。如果可以直接从池中获取可用线程,效率会提高。因此,线程池省去了执行任务前创建线程的过程,节省了时间,提高了效率;线程池可以管理和控制线程,因为线程是稀缺资源。会消耗系统资源,降低系统的稳定性。线程池可用于统一分配、调优和监控;线程池提供了一个队列来存储和缓冲等待执行的任务。以上原因大致总结一下,所以可以得出结论,在日常工作中,如果要开发多线程程序,尽量使用线程池来创建和管理线程。从调用API的角度来看,通过线程池创建的线程有两种。一种是原生的线程池,另一种是通过Java提供的并发包创建的。后者比较简单,后者其实就是一个原生的线程池。线程池的创建方法进行了简化和封装,方便调用者使用,但道理是一样的。所以理解原生线程池的原理是非常重要的。ThreadPoolExecutor通过ThreadPoolExecutor创建线程池。API如下:publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,TimeUnitunit,BlockingQueue
