本博客系列是学习并发编程过程当中的记录总结。因为文章比较多,写的时间也比较散,因此我整理了个目录贴(传送门),方便查阅。html
并发编程系列博客传送门java
多线程是Java编程中一块很是重要的内容,其中涉及到不少概念。这些概念咱们平时常常挂在嘴上,可是真的要让你介绍下这些概念,你可能还真的讲不清楚。这篇博客就总结下多线程编程中常常用到的概念,理解这些概念能帮助咱们更好地掌握多线程编程。linux
进程和线程是最常提到的概念了。在linux中,线程与进程最大的区别就是是否共享同一块地址空间,并且共享同一块地址空间的那一组线程将显现相同的PID号。下面介绍下二者的概念:程序员
谈到线程和进程,又势必会涉及到线程号和进程号的概念。下面列举了各个ID的概念。面试
下面贴上一张图来解释下这两个概念:算法
上图中的咖啡就能够当作是CPU,上面的只有一个咖啡机,至关于只有一个CPU。想喝咖啡的人只有等前面的人制做完咖啡才能制做本身的开发,也就是同一时间只能有一我的在制做咖啡,这是一种并发模式。下面的图中有两个咖啡机,至关于有两个CPU,同一时刻能够有两我的同时制做咖啡,是一种并行模式。数据库
咱们发现并行编程中,很重要的一个特色是系统具备多核CPU。要是系统是单核的,也就谈不上什么并行编程了。编程
这个概念多是在多线程编程中说起最多的一个概念了。在面试过程当中,我试着问过几个面试者,可是几乎没人能将这个概念解释的很好的。安全
关于这个概念,我以为好多人都有一个误区,包括我本身一开始也是这样的。我一开始认为线程安全讲的是某个共享变量线程安全,其实咱们所说的线程安全是指某段代码或者是某个方法是线程安全的。线程安全的准肯定义应该是这样的:微信
若是线程的随机调度顺序不影响某段代码的最后执行结果,那么咱们认为这段代码是线程安全的。
为了保证代码的线程安全,Java中推出了不少好用的工具类或者关键字,好比volatile、synchronized、ThreadLocal、锁、并发集合、线程池和CAS机制等。这些工具并非在每一个场景下都能知足咱们多线程编程的需求,并非在每一个场景下都有很高的效率,须要咱们程序员根据具体的场景来选择最适合的技术,这也许就是咱们程序员存在的价值所在。(我一直以为若是有一个技术能很好的解决大多数场景下的问题,那么这个领域确定是能够作成机器自动化的。那么对于这个领域就不太须要有多少人参与了。)
线程1占用了锁A,等待锁B,线程2占用了锁B,等待锁A,这种状况下就形成了死锁。在死锁状态下,相关的代码将不能再提供服务。
private void deadLock() { Thread t1 = new Thread(new Runnable() { @Override public void run() { synchronized (lock1) { try { Thread.currentThread().sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock2) { System.out.println("1"); } } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { synchronized (lock2) { synchronized (lock1) { System.out.println("2"); } } } }); t1.start(); t2.start(); }
这段代码只是演示死锁的场景,在现实中你可能不会写出这样的代码。可是,在一些更为复杂的场景中,你可能会遇到这样的问题,好比t1拿到锁以后,由于一些异常状况没有释放锁(死循环)。又或者是t1拿到一个数据库锁,释放锁的时候抛出了异常,没释放掉。
若是你怀疑代码中有线程出现了死锁,你能够dump线程,而后查看线程状态有没有Blocked的线程(java.lang.Thread.State: BLOCKED)
"Thread-2" prio=5 tid=7fc0458d1000 nid=0x116c1c000 waiting for monitor entry [116c1b000] java.lang.Thread.State: BLOCKED (on object monitor) at com.ifeve.book.forkjoin.DeadLockDemo$2.run(DeadLockDemo.java:42) - waiting to lock <7fb2f3ec0> (a java.lang.String) - locked <7fb2f3ef8> (a java.lang.String) at java.lang.Thread.run(Thread.java:695) "Thread-1" prio=5 tid=7fc0430f6800 nid=0x116b19000 waiting for monitor entry [116b18000] java.lang.Thread.State: BLOCKED (on object monitor) at com.ifeve.book.forkjoin.DeadLockDemo$1.run(DeadLockDemo.java:31) - waiting to lock <7fb2f3ef8> (a java.lang.String) - locked <7fb2f3ec0> (a java.lang.String) at java.lang.Thread.run(Thread.java:695)
避免死锁的几个方式:
饥饿是指某一个或者多个线程由于种种缘由没法得到所须要的资源,致使一直没法执行。好比它的线程优先级可能过低,而高优先级的线程不断抢占它须要的资源,致使低优先级线程没法工做。
在天然界中,母鸟给雏鸟喂食时很容易出现这种状况:因为雏鸟不少,食物有限,雏鸟之间的食物竞争可能很是厉害,常常抢不到食物的雏鸟有可能会被饿死。线程的饥饿很是相似这种状况。
此外,某一个线程一直占着关键资源不放,致使其余须要这个资源的线程没法正常执行,这种状况也是饥饿的一种。与死锁相比,饥饿仍是有可能在将来一段时间内解决的(好比,高优先级的线程已经完成任务,再也不疯狂执行)。
活锁是一种很是有趣的状况。不知道你们是否遇到过这么一种场景,当你要坐电梯下楼时,电梯到了,门开了,这时你正准备出去。但很不巧的是,门外一我的挡着你的去路,他想进来。因而,你很礼貌地靠左走,避让对方。同时,对方也很是礼貌地靠右走,但愿避让你。结果,大家俩就又撞上了。因而乎,大家都意识到了问题,但愿尽快避让对方,你当即向右边走,同时,他当即向左边走。结果,又撞上了!不过介于人类的智能,我相信这个动做重复两三次后,你应该能够顺利解决这个问题。由于这个时候,你们都会本能地对视,进行交流,保证这种状况再也不发生。
但若是这种状况发生在两个线程之间可能就不会那么幸运了。若是线程的智力不够,且都秉承着“谦让”的原则,主动将资源释放给他人使用,那么就会致使资源不断地在两个线程间跳动,而没有一个线程能够同时拿到全部资源正常执行。这种状况就是活锁。
这边讨论的同步和异步指的是同步方法和异步方法。
同步方法是指调用这个方法后,调用方必须等到这个方法执行完成以后才能继续往下执行。
异步方法是指调用这个方法后会立马返回,调用方能立马往下继续执行。被调用的异步方法实际上是由另外的线程进行执行的,若是这个异步方法有返回值的话能够经过某种通知的方式告知调用方。
实现异步方法的方式:
涉及读写共享资源的代码片断叫“临界区”。
好比下面代码中,1处和2处就是一个代码临界区。
private static class BankAccount{ String accountName; double balance; public BankAccount(String accountName,double balance){ this.accountName = accountName; this.balance = balance; } public synchronized double deposit(double amount){ balance = balance + amount; //1 return balance; } public synchronized double withdraw(double amount){ balance = balance - amount; //2 return balance; } }
线程在CPU上运行以前须要CPU给这个线程分配时间片,当时间片运行完以后这个线程就会让出CPU资源给其余的线程运行。可是线程在将CPU资源让出以前会保存当前的任务状态以便下次得到CPU资源以后能够继续往下执行。因此线程从保存当前执行状态到再加载的过程称为一次上下文切换。
减小上下文切换的措施
使用并发编程的目的是让程序运行的更快(更大限度的使用CPU资源,让程序运行更快),可是在进行并发编程的过程也会遇到一些挑战。
PS:多线程并发编程可让咱们最大限度的使用系统的CPU资源,以达到让程序运行更快的目的(不是全部状况下多线程都更快)。可是一个硬币具备两面性,引入多线程编程会给咱们带来其余的问题,好比说线程的上下文切换问题、共享变量的线程安全问题、线程间通讯问题、线程死锁问题和硬件资源对多线程的影响等问题。其实研究多线程并发编程就是在研究这对矛盾体,怎么在享受多线程并发编程给咱们带来便利的同时又能避开多线程带来的坑。JDK中给咱们提供不少多线程相关的类