目录
1.什么是多线程的上下文切换
2.死锁与活锁的区别
3.什么是线程饥饿
4.new Thread()创建对象的缺点(也可以说是线程池的优点)
5.多线程中的原子操作
6.Executors框架
7.Callable接口的优点(相比于Runnable)
8.FutureTask是什么
9.同步容器与并发容器
10.线程互斥
不同线程切换使用CPU时发生的数据切换就是上下文切换。由于多线程执行任务过程中会使用计算机上的CPU资源,当线程数大于为程序分配的CPU数量时,为了让各个线程都有执行的机会,需要轮换使用CPU。
1.死锁
多个线程执行过程中,若需要对方占用的共享数据时,同时等待对方释放数据而出现的阻塞现象
2.活锁
程序执行中没有阻塞,然而由于不满足某些条件,导致一直重复尝试、失败的过程叫做活锁。
3.区别
1.程序状态的区别
处于活锁的程序在不停的改变执行状态,出现尝试执行、失败的重复过程,而死锁的程序表现出等待状态。
2.程序是否阻塞
处于活锁的程序并没有阻塞,可能出现尝试执行后成功的现象,死锁的程序若无外力将一直阻塞下去。
1.饥饿
线程由于无法获取执行中需要的资源,导致一直无法执行的状态
2.为什么会出现线程饥饿
优先级问题:线程中优先级高的永远先执行,优先级低的线程无法使用CPU资源
1.消耗资源
使用new Thread()方法创建线程比较耗时、耗资源
2.缺乏管理
使用new Thread()方法创建的线程缺乏管理,也称为野线程。且可以无限制创建,导致线程之间出现过多相互竞争和频繁切换,占用过多系统资源。
3.不利于扩展
不便实现定期执行、定时执行等的操作
原子操作(atomic operation):不可被中断的一个或一系列操作
作用:用于在多线程环境下保证数据的一致性
实现方式:在处理器中,使用基于对缓存加锁或总线加锁的方式实现多处理器之间的原子操作。在Java中,通过锁和CAS(Compare And Swap)实现原子操作。
是一个根据一组执行策略进行调度执行异步任务的框架。利用Executors框架可以方便的创建一个线程池,以便管理线程,避免出现无限制创建野线程导致的内存溢出问题。
1.call()方法可以有返回值;Runnable方法无法获取返回值
2.call()可以抛出异常,被外面的操作捕获,获取异常的信息;Runnable方法无法抛出返回的异常
3.Callable是支持泛型的;Runnable不支持泛型
1.FutureTask是Java并发程序中用来表示一个可以取消的异步运算
2.含有启动、取消运算、查询运算是否完成、取回运算结果等方法;使用取回运算结果方法时:只有运算完成后结果才能取回
同步容器:通过synchronized方法实现同步的容器,比如Vector、Hashtable等,源码中为这个容器的方法加上了关键字synchronized
并发容器:使用与同步容器不同的加锁方式,提供更高并发性和吞吐量。比如ConcurrentHashMap中采用的分段锁,能够允许多个读线程或者写线程并发访问或修改Map
单个线程访问共享资源时的排他性。多个线程操作共享数据时,任何时刻最多只允许一个线程使用,其他线程必须等待,直到占用资源的线程释放资源。