线程和进程的区别
线程和进程的主要差别就是他们是不同的操作系统资源管理方式,进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其他进程影响,而线程只是进程中的不同执行路径
多线程有什么用
- 发挥多核CPU的优势
随着工业的进步,现在的笔记本、台式机乃至商用的应用服务器至少也都是双核的,4 核、8 核甚至 16 核的也都不少见,如果是单线程的程序,那么在双核 CPU 上就浪费了 50%, 在 4 核 CPU 上就浪费了 75%。单核 CPU 上所谓的"多线程"那是假的多线程,同一时间处理器只会处理一段逻辑,只不过线程之间切换得比较快,看着像多个线程"同时"运行罢了。多核 CPU 上的多线程才是真正的多线程,它能让你的多段逻辑同时工作,多线程,可以真正发挥出多核CPU 的优势来,达到充分利用CPU 的目的。 - 防止阻塞
从程序运行效率上来说,单核CPU不但发布不出多线程的优势,反而会因为在CPU上运行多线程导致上下文的切换,降低程序整体的效率。初次之外单核CPU还会有阻塞的问题,当其中一个线程阻塞时,会导致后边的停止运行,而多线程就可以防止这个问题,多条线程运行时,既是其中一个线程阻塞,也不会影响到其他的线程 - 便于建模
这是另外一个没有这么明显的优点了。假设有一个大的任务 A,单线程编程,那么就要考虑很多,建立整个程序模型比较麻烦。但是如果把这个大的任务 A 分解成几个小任务,任务B、任务 C、任务 D,分别建立程序模型,并通过多线程分别运行这几个任务,那就简单很
多了。
wait和sleep的区别
wait和sleep方法都可以放弃CPU一段时间,不同点在于sleep不会放弃当前线程池的锁,而wait会放弃当前线程器的锁,而wait是属于Object中的方法,而sleep是Thread中的方法。
violatile关键字的作用
- 多线程主要围绕可见性和原子性两个特性而展开,使用 volatile 关键字修饰的变量,保证了其在多线程之间的可见性,即每次读取到 volatile 变量,一定是最新的数据。
- 现实中,为了获取更好的性能 JVM 可能会对指令进行重排序,多线程下可能会出现一些意想不到的问题。使用 volatile 则会对禁止语义重排序,当然这也一定程度上降低了代码执行效率。
Join方法
很多情况下,主线程生成并启动了子程序,需要用到子程序返回的结果,也就是需要主程序在子程序结束之后再结束,这时候就用到join方法。
System.out.println(Thread.currentThread().getName()+"线程运行开始”);
Thread thread1=new Thread();
thread1.setName("线程1");
thread1.join();
System.out.println("这时候Thread1执行完之后才能执行住线程”);
线程池的原理
线程池做的主要工作就是控制运行的线程数量,处理过程中将任务放入队列,然后在线程池创建后启动这些任务,如果线程数量超过了最大数量超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。
为什么要使用线程池
当我们不使用线程池的时候,每一个线程都会创建并运行一个线程,线程少的时候是不会有太大的问题,但是当线程达到一定数量的时候,就会消耗CPU和内存资源,也会造成GC的频繁收集和停顿。所以线程池中的线程复用极大的节省了系统资源,当线程一段时间不再有任务处理的时候它也会自动销毁,并不会长驻内存。
线程池中submit方法和exectue方法有什么区别
- exectue方法没有返回值,如果不需要知道县城返回结果,就是用exectue方法,效率会高。
- submit方法有返回值,返回一个Future对象,而且可以在主线程中通过Future的get方法捕获线程中的异常
什么是死锁,活锁,饥饿,无锁
死锁
死锁是多线程中最差的一种情况,多个线程相互占用对方的资源的锁,而又相互等对方释放锁,此时若无外力干预,这些线程则一直处理阻塞的假死状态,形成死锁。
举个例子,A 同学抢了 B 同学的钢笔,B 同学抢了 A 同学的书,两个人都相互占用对方的东西,都在让对方先还给自己自己再还,这样一直争执下去等待对方还而又得不到解决,老师知道此事后就让他们相互还给对方,这样在外力的干预下他们才解决,当然这只是个例子没有老师他们也能很好解决,计算机不像人如果发现这种情况没有外力干预还是会一直阻塞下去的。
活锁
活锁跟死锁刚刚相反,死锁是大家都拿不到资源都占用着对方的资源。而活锁是拿到资源之后却又互相释放不执行。当多线程中出现了相互谦让,都主动将资源释放给别的线程使用,这样这个资源在多个线程之间跳动而又得不到执行,这就是活锁。
饥饿
我们知道线程中有优先级这个东西,优先级高的线程能够插队并优先执行,这样如果优先级高的线程一直抢占优先级低的线程,导致优先级低的线程一直无法执行,这就被称为饥饿当然还有一种饥饿的情况,一个线程一直占着一个资源不放而导致其他线程得不到执行,与死锁不同的是饥饿在以后一段时间内还是能够得到执行的,如那个占用资源的线程结束了并释放了资源。
无锁
无锁,即没有对资源进行锁定,即所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功。无锁典型的特点就是一个修改操作在一个循环内进行,线程会不断的尝试修改共享资源,如果没有冲突就修改成功并退出否则就会继续下一次循环尝试。所以,如果有多个线程修改同一个值必定会有一个线程能修改成功,而其他修改失败的线程会不断重试直到修改成功。
可以看出,无锁是一种非常良好的设计,它不会出现线程出现的跳跃性问题,锁使用不当肯定会出现系统性能问题,虽然无锁无法全面代替有锁,但无锁在某些场合下是非常高效的。