什么进程?
进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例。程序运行时系统就会创建一个进程,并为它分配资源,然后把该进程放入进程就绪队列,进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。
什么是线程?
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。同样多线程也可以实现并发操作,每个请求分配一个线程来处理。
进程是资源分配的最小单位,而线程是程序执行的最小单位。一个进程可以包含多个线程,线程之间共享进程的资源,但每个线程都有自己的栈空间和寄存器。
用户线程:如果主线程main停止掉,不会影响用户线程,用户线程可以继续运行。
守护线程:为其他线程服务的,如果主线程死亡,守护线程如果没有执行完毕也要跟着一块死,GC垃圾回收线程就是守护线程
run():仅仅是封装被线程执行的代码,直接调用是普通方法
start():首先启动了线程,这时此线程处于就绪(可运行)状态,然后再由jvm去调用该线程的run()方法。
调用 start 方法方可启动线程并使线程进入就绪状态,而 run 方法只是 thread 的一个普通方法调用,还是在主线程里执行。
相同点:
不同点:
创建:又称初始化状态,这个时候Thread才刚刚被new出来,还没有被启动。
可运行:表示已经调用Thread的start方法启动了,随时等待CPU的调度,此状态又被称为就绪状态。
阻塞:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态
等待:获取锁对象后,调用wait()方法,释放锁进入无线等待状态
计时等待:调用sleep(参数)或wait(参数)后线程进入计时状态,睡眠时间到了或wait时间到了,再或者其它线程调用notify并获取到锁之后开始进入可运行状态,避免了无期限的等待。
线程安全:指某个方法在多线程环境被调用,能够正确处理多线程之间的共享变量,是程序功能正确完成。
线程同步
:当一个线程对共享数据进行操作的时候,在没有完成相关操作时,不允许其它的线程来打断它,否则就会破坏数据的完整性,必然会引起错误信息。
线程互斥
:线程互斥是站在共享资源的角度上看问题,例如某个共享资源规定,在某个时刻只能一个线程来访问我,其它线程只能等待,知道占有的资源者释放该资源,线程互斥可以看作是一种特殊的线程同步。
可以使用互斥锁、条件变量、信号量等机制来同步线程。互斥锁用于保护共享资源,条件变量用于线程之间的通信,信号量用于控制并发访问。
互斥条件:进程对所分配到的资源进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
请求和保持条件:进程已经获得了至少一个资源,但又对其他资源发出请求,而该资源已被其他进程占有,此时该进程的请求被阻塞,但又对自己获得的资源保持不放。
不可剥夺条件:进程已获得的资源在未使用完毕之前,不可被其他进程强行剥夺,只能由自己释放。
循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被 链中下一个进程所请求。
预防死锁的方式就是打破四个必要条件中的任意一个即可。
避免线程死锁的方法包括:避免嵌套锁,按照固定的顺序获取锁,使用超时机制,避免资源竞争等。此外,还可以使用死锁检测工具来检测和解决死锁问题。
可以采用以下方法来优化多线程程序的性能:避免线程之间的竞争,减少锁的使用,使用无锁数据结构,使用线程池等。此外,还可以使用性能分析工具来找出程序的瓶颈,进行优化。
事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。
如果我们在方法中直接new一个线程来处理,当这个方法被调用频繁时就会创建很多线程,不仅会消耗系统资源,还会降低系统的稳定性,一不小心把系统搞崩了。合理的使用线程池,则可以避免把系统搞崩的窘境,这说一下线程池的好处:降低资源消耗、提高响应速度和提高线程的可管理性。
Executors和ThreaPoolExecutor创建线程池的区别
Executors 各个方法的弊端:
ThreadPoolExecutor创建线程池方式只有一种,就是走它的构造函数,参数自己指定。
核心线程数(corePoolSize )
: 线程数定义了最小可以同时运行的线程数量;最大线程数(maximumPoolSize )
:线程池中允许存在的工作线程的最大数量;允许线程空闲时间(keepAliveTime)
:线程池中的线程数量大于核心线程数的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 空闲时间才会被回收销毁;时间单位(unit )
:参数的时间单位;队列(workQueue)
:当新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,任务就会被存放在队列中;线程工厂(threadFactory)
:为线程池提供创建新线程的线程工厂;拒绝策略(handler )
:线程池任务队列超过最大线程数之后的拒绝策略,默认直接抛出异常。乐观锁:每个去拿数据的时候都认为别人不会修改,所以不会都不会上锁,但是在更新的时候会判断一下在此期间有没有去更新这个数据。所以乐观锁使用了多读的场合,这样可以提高吞吐量,像数据库提供的类似write_condition机制,都是用的乐观锁。
悲观锁:总是认为出现问题,每次去拿数据的时候都会认为有人会修改,所以每次在拿数据的时候都会上锁。这样别的对象想拿到数据,那就必须堵塞,直到拿到锁。传统的关系型数据库用到了很多这种锁机制,比如读锁,写锁,在操作之前都会先上锁,再比如Java的同步代码块synchronized/方法用的也是悲观锁。