这个其实前面有提到过,FutureTask表示一个异步运算的任务。FutureTask里面能够传入一个Callable的具体实现类,能够对这个异步运算的任务的结果进行等待获取、判断是否已经完成、取消任务等操做。固然,因为FutureTask也是Runnable接口的实现类,因此FutureTask也能够放入线程池中。咱们知道在Java的新版中中引入了AIO模型,相对于以前的NIO,AIO编程提供了异步模型,若是对IO模型有疑惑,点这里:http://my.oschina.net/u/2288283/blog/625932. 在AIO编程过程当中就会用到这个FutureTask,具体的用法我会在从此的博客中介绍一下。html
这是一个比较偏实践的问题,这种问题我以为挺有意义的。能够这么作:java
获取项目的pid,jps或者ps -ef | grep java,这个前面有讲过算法
第一次看到这个题目,以为这是一个很是好的问题。不少人都知道死锁是怎么一回事儿:线程A和线程B相互等待对方持有的锁致使程序无限死循环下去。固然也仅限于此了,问一下怎么写一个死锁的程序就不知道了,这种状况说白了就是不懂什么是死锁,懂一个理论就完事儿了,实践中碰到死锁的问题基本上是看不出来的。sql
真正理解什么是死锁,这个问题其实不难,几个步骤:数据库
(1)两个线程里面分别持有两个Object对象:lock1和lock2。这两个lock做为同步代码块的锁;编程
(2)线程1的run()方法中同步代码块先获取lock1的对象锁,Thread.sleep(xxx),时间不须要太多,50毫秒差很少了,而后接着获取lock2的对象锁。这么作主要是为了防止线程1启动一会儿就连续得到了lock1和lock2两个对象的对象锁缓存
(3)线程2的run()方法中同步代码块先获取lock2的对象锁,接着获取lock1的对象锁,固然这时lock1的对象锁已经被线程1锁,线程2确定是要等待线程1释放lock1的对象锁的安全
下面演示一个死锁的实例:服务器
public class DeadLock { public static final Object lock1=new Object(); public static final Object lock2=new Object(); public static void main(String[] args){ Thread thread1=new Thread(new Runnable() { @Override public void run() { synchronized (lock1){ try { Thread.sleep(1000); synchronized (lock2){ System.out.println("线程1以得到锁1,正在得到锁2"); } } catch (InterruptedException e) { e.printStackTrace(); } } } }); Thread thread2=new Thread(new Runnable() { @Override public void run() { synchronized (lock2){ try { Thread.sleep(1000); synchronized (lock1){ System.out.println("线程2以得到锁2,正在尝试得到锁1"); } } catch (InterruptedException e) { e.printStackTrace(); } } } }); thread1.start(); thread2.start(); } }
若是线程是由于调用了wait()、sleep()或者join()方法而致使的阻塞,能够中断线程,而且经过抛出InterruptedException来唤醒它;若是线程遇到了IO阻塞,无能为力,由于IO是操做系统实现的,Java代码并无办法直接接触到操做系统。多线程
前面有提到过的一个问题,不可变对象保证了对象的内存可见性,对不可变对象的读取不须要进行额外的同步手段,提高了代码执行效率。好比String对象,在不一样的线程中依然能够“保持自我”,多么清高的String阿。。巴拉巴拉
多线程的上下文切换是指CPU控制权由一个已经正在运行的线程切换到另一个就绪并等待获取CPU执行权的线程的过程。
若是你使用的LinkedBlockingQueue,也就是无界队列的话,不要紧,继续添加任务到阻塞队列中等待执行,由于LinkedBlockingQueue能够近乎认为是一个无穷大的队列,能够无限存听任务;若是你使用的是有界队列比方说ArrayBlockingQueue的话,任务首先会被添加到ArrayBlockingQueue中,ArrayBlockingQueue满了,则会使用拒绝策略RejectedExecutionHandler处理满了的任务,默认是AbortPolicy
抢占式。一个线程用完CPU以后,操做系统会根据线程优先级、线程饥饿状况等数据算出一个总的优先级并分配下一个时间片给某个线程执行。
这个问题和上面那个问题是相关的,我就连在一块儿了。因为Java采用抢占式的线程调度算法,所以可能会出现某条线程经常获取到CPU控制权的状况,为了让某些优先级比较低的线程也能获取到CPU控制权,可使用Thread.sleep(0)手动触发一次操做系统分配时间片的操做,这也是平衡CPU控制权的一种操做。
不少synchronized里面的代码只是一些很简单的代码,执行时间很是快,此时等待的线程都加锁多是一种不太值得的操做,由于线程阻塞涉及到用户态和内核态切换的问题。既然synchronized里面的代码执行得很是快,不妨让等待锁的线程不要被阻塞,而是在synchronized的边界作忙循环,这就是自旋。若是作了屡次忙循环发现尚未得到锁,再阻塞,这样多是一种更好的策略。详情见:http://my.oschina.net/u/2288283/blog/638969
Java内存模型定义了一种多线程访问Java内存的规范。Java内存模型要完整讲不是这里几句话能说清楚的,我简单总结一下Java内存模型的几部份内容:
(1)Java内存模型将内存分为了主内存和工做内存。类的状态,也就是类之间共享的变量,是存储在主内存中的,每次Java线程用到这些主内存中的变量的时候,会读一次主内存中的变量,并让这些内存在本身的工做内存中有一份拷贝,运行本身线程代码的时候,用到这些变量,操做的都是本身工做内存中的那一份。在线程代码执行完毕以后,会将最新的值更新到主内存中去
(2)定义了几个原子操做,用于操做主内存和工做内存中的变量
(3)定义了volatile变量的使用规则
(4)happens-before,即先行发生原则,定义了操做A必然先行发生于操做B的一些规则,好比在同一个线程内控制流前面的代码必定先行发生于控制流后面的代码、一个释放锁unlock的动做必定先行发生于后面对于同一个锁进行锁定lock的动做等等,只要符合这些规则,则不须要额外作同步措施,若是某段代码不符合全部的happens-before规则,则这段代码必定是线程非安全的
CAS,全称为Compare and Swap,即比较-替换。假设有三个操做数:内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时,才会将内存值修改成B并返回true,不然什么都不作并返回false。固然CAS必定要volatile变量配合,这样才能保证每次拿到的变量是主内存中最新的那个值,不然旧的预期值A对某条线程来讲,永远是一个不会变的值A,只要某次CAS操做失败,永远都不可能成功。在Java的锁机制内部不少都是借用了CAS锁来实现,固然CAS仍是要靠硬件支持滴。
(1)乐观锁:就像它的名字同样,对于并发间操做产生的线程安全问题持乐观状态,乐观锁认为竞争不老是会发生,所以它不须要持有锁,将比较-替换这两个动做做为一个原子操做尝试去修改内存中的变量,若是失败则表示发生冲突,那么就应该有相应的重试逻辑。
(2)悲观锁:仍是像它的名字同样,对于并发间操做产生的线程安全问题持悲观状态,悲观锁认为竞争老是会发生,所以每次对某资源进行操做时,都会持有一个独占的锁,就像synchronized,无论三七二十一,直接上了锁就操做资源了。
说点题外话,其实乐观锁和悲观锁的问题在数据库中也很常见,好比Mysql的锁问题,InnoDB默认的就是靠MVCC机制实现的乐观锁,悲观锁的话能够参考行锁,表锁等等来理解。
简单说一下AQS,AQS全称为AbstractQueuedSychronizer,翻译过来应该是抽象队列同步器。
若是说java.util.concurrent的基础是CAS的话,那么AQS就是整个Java并发包的核心了,ReentrantLock、CountDownLatch、Semaphore等等都用到了它。AQS实际上以双向队列的形式链接全部的Entry,比方说ReentrantLock,全部等待的线程都被放在一个Entry中并连成双向队列,前面一个线程使用ReentrantLock好了,则双向队列实际上的第一个Entry开始运行。
AQS定义了对双向队列全部的操做,而只开放了tryLock和tryRelease方法给开发者使用,开发者能够根据本身的实现重写tryLock和tryRelease方法,以实现本身的并发功能。
老生常谈的问题了,首先要说的是单例模式的线程安全意味着:某个类的实例在多线程环境下只会被建立一次出来。单例模式有不少种的写法,我总结一下:
(1)饿汉式单例模式的写法:线程安全
(2)懒汉式单例模式的写法:非线程安全
(3)双检锁单例模式的写法:线程安全
不过对于单例的解释这里也只是一个很是粗略的,若是有兴趣的网友能够网上自行查找:如何安全的建立单例模式,可使用volatile呦。。
Semaphore就是一个信号量,它的做用是限制某段代码块的并发数。Semaphore有一个构造函数,能够传入一个int型整数n,表示某段代码最多只有n个线程能够访问,若是超出了n,那么请等待,等到某个线程执行完毕这段代码块,下一个线程再进入。由此能够看出若是Semaphore构造函数中传入的int型整数n=1,至关于变成了一个synchronized了。
这是我以前的一个困惑,不知道你们有没有想过这个问题。某个方法中若是有多条语句,而且都在操做同一个类变量,那么在多线程环境下不加锁,势必会引起线程安全问题,这很好理解,可是size()方法明明只有一条语句,为何还要加锁?
关于这个问题,在慢慢地工做、学习中,有了理解,主要缘由有两点:
(1)同一时间只能有一条线程执行固定类的同步方法,可是对于类的非同步方法,能够多条线程同时访问。因此,这样就有问题了,可能线程A在执行Hashtable的put方法添加数据,线程B则能够正常调用size()方法读取Hashtable中当前元素的个数,那读取到的值可能不是最新的,可能线程A添加了完了数据,可是没有对size++,线程B就已经读取size了,那么对于线程B来讲读取到的size必定是不许确的。而给size()方法加了同步以后,意味着线程B调用size()方法只有在线程A调用put方法完毕以后才能够调用,这样就保证了线程安全性
(2)CPU执行代码,执行的不是Java代码,这点很关键,必定得记住。Java代码最终是被翻译成汇编代码执行的,汇编代码才是真正能够和硬件电路交互的代码。即便你看到Java代码只有一行,甚至你看到Java代码编译以后生成的字节码也只有一行,也不意味着对于底层来讲这句语句的操做只有一个。一句”return count”假设被翻译成了三句汇编语句执行,彻底可能执行完第一句,线程就切换了。
这是一个很是刁钻和狡猾的问题。请记住:线程类的构造方法、静态块是被new这个线程类所在的线程所调用的,而run方法里面的代码才是被线程自身所调用的。
若是说上面的说法让你感到困惑,那么我举个例子,假设Thread2中new了Thread1,main函数中new了Thread2,那么:
(1)Thread2的构造方法、静态块是main线程调用的,Thread2的run()方法是Thread2本身调用的
(2)Thread1的构造方法、静态块是Thread2调用的,Thread1的run()方法是Thread1本身调用的
同步块,这意味着同步块以外的代码是异步执行的,这比同步整个方法更提高代码的效率。请知道一条原则:同步的范围越小越好。
借着这一条,我额外提一点,虽然说同步的范围越少越好,可是在Java虚拟机中仍是存在着一种叫作锁粗化的优化方法,这种方法就是把同步范围变大。这是有用的,比方说StringBuffer,它是一个线程安全的类,天然最经常使用的append()方法是一个同步方法,咱们写代码的时候会反复append字符串,这意味着要进行反复的加锁->解锁,这对性能不利,由于这意味着Java虚拟机在这条线程上要反复地在内核态和用户态之间进行切换,所以Java虚拟机会将屡次append方法调用的代码进行一个锁粗化的操做,将屡次的append的操做扩展到append方法的头尾,变成一个大的同步块,这样就减小了加锁–>解锁的次数,有效地提高了代码执行的效率。
这是我在并发编程网上看到的一个问题,把这个问题放在最后一个,但愿每一个人都能看到而且思考一下,由于这个问题很是好、很是实际、很是专业。关于这个问题,我的见解是:
(1)高并发、任务执行时间短的业务,线程池线程数能够设置为CPU核数+1,减小线程上下文的切换
(2)并发不高、任务执行时间长的业务要区分开看:
a)假如是业务时间长集中在IO操做上,也就是IO密集型的任务,由于IO操做并不占用CPU,因此不要让全部的CPU闲下来,能够加大线程池中的线程数目,让CPU处理更多的业务
b)假如是业务时间长集中在计算操做上,也就是计算密集型任务,这个就没办法了,和(1)同样吧,线程池中的线程数设置得少一些,减小线程上下文的切换
(3)并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于总体架构的设计,看看这些业务里面某些数据是否能作缓存是第一步,增长服务器是第二步,至于线程池的设置,设置参考(2)。最后,业务执行时间长的问题,也可能须要分析一下,看看能不能使用中间件对任务进行拆分和解耦。
=====================================================================
以上的问题都是参考了其余的博文,固然也加入了本身的一些观点,若是问题,欢迎指正。
参考博文:http://www.importnew.com/18459.html