什么时候用多线程?多线程须要加锁吗?线程数多少最合理?

爱生活,爱编码,微信搜一搜【架构技术专栏】关注这个喜欢分享的地方。 本文 架构技术专栏 已收录,有各类视频、资料以及技术文章。java

1、何时应该使用多线程?

今天看到一个问题,忽然有感而发,想聊下这个话题。数据库

不知道你们有没有想过这个问题,就是何时我该使用多线程呢?使用多线程就必定会提高系统性能吗?编程

一、实际上是否应该使用多线程在很大程度上取决于应用程序的类型。安全

  • 计算密集型(如纯数学运算) 的, 并受CPU 功能的制约, 则只有多CPU(或者多个内核) 机器可以从更多的线程中受益, 单CPU下, 多线程不会带来任何性能上的提高, 反而有可能因为线程切换等额外开销而致使性能降低微信

  • IO密集型的,当个人应用必须等待缓慢的资源(如网络链接或者数据库链接上返回数据)时,那么多线程会让系统的CPU充分的利用起来,当一个线程阻塞挂起时,另外一个线程能够继续使用CPU资源。网络

  • 其实,就是多线程不会增长CPU的处理能,而是可以更加充分地利用CPU资源。多线程

因为同一进程的多个线程是共享同一片内存资源的,在带来方便的同时也必然会增长其复杂性,如何保证多线程访问数据的一致性问题等。而多线程属于编程中容易翻车的地方。而且多线程编程问题的测试定位也是比较难的。整体来讲,好的多线程是写出来,将多线程问题寄但愿于测试中发现, 无疑是极度不可靠的。SO,努力的学习吧。架构

Java API 与多线程息息相关的的几大关键字:volatile、synchronized、 wait、 notify. 理解了这几个关键字,就能够编写多线程的代码了。性能

2、何时须要加锁?

在多线程场合下,最重要的就是保障数据的一致性问题,而保障数据一致性问题,就须要借助于锁了。学习

其实咱们在多线程的场景下应该搞清楚一个问题,就是到底什么须要保护?并非全部的的数据都须要加锁保护,只有那些涉及到被多线程访问的共享的数据才须要加锁保护。

锁的本质其实就是确保在同一时刻,只有一个线程在访问共享数据,那么此时该共享数据就能获得有效的保护。

举例说明下,好比咱们想构造一个多线程下安全的单向链表:

<img src="/Users/luqiang/Downloads/公众号图片/链表插入新图.jpg" alt="链表插入新图" style="zoom:50%;" />

假如如今有两个线程在操做这个链表,一个写线程插入一个新元素7,另外一个读线程遍历链表数据,若是不使用任何锁,那就有可能出现下面的执行顺序:

步骤 写线程 读线程
0 修改链表元素2的next指针指向7这个元素 ... ...
1 ... ... 遍历链表,到7的时候发现next 为null,遍历结束
2 修改元素7的next 指针指向3 ... ...

经过上面的例子咱们能够明显看到在多线程下操做这个链表,有可能会致使读线程读到的数据不完整,只有从链表头部到元素7的位置的数据。因而可知,不加入任何保护措施的多线程保护,势必会致使数据的混乱。为了不数据一致性问题,咱们就须要将操做该队列的代码放入同步块内(锁的对象也就是这个链表实例),来确保同一时刻只有一个线程能够访问该链表。

如何加锁?

这里简单的说下,通常咱们都是使用synchronized(若是没有特殊需求建议直接使用这个关键字,jdk新版本它真的很快),记住synchronized 锁的就是对象头。

简单的说下,主要有下面几种用法:

  • synchronized 放在方法上,锁的是当前synchronized 方法的对象实例

  • synchronized在static 方法上,锁的是synchronized 方法的class 类对象,注意这里class 其实也是一个对象。

  • synchronized(this)在代码块中,锁的是代码块括号内的对象,这里this指的就是调用这个方法的类实例对象

3、 多线程中易犯的错误

一、锁范围过大

共享资源访问完成后, 后续的代码没有放在synchronized同步代码块以外。 会致使当前线程长期无效的占用该锁, 而其它争用该锁的线程只能等待, 最终致使性能受到极大影响。

public void test()
 {
 		synchronized(lock){
 		... ... //正在访问共享资源
 		... ... //作其它耗时操做,但这些耗时操做与共享资源无关
 	}
 }

面对上面这种写法,会致使此线程长期占有此锁,从而致使其余线程只能等待,下面来讨论下解决方法:

1)单CPU场景下,将不须要同步的耗时操做拿到同步块外面,有的状况能够提高性能,有的却不行。

  • CPU密集型的代码 ,不存在磁盘IO/网络IO等低CPU消耗的代码。 这种状况下, CPU 99%都在执行代码。 所以缩小同步块也不会带来任何性能上的提高, 同时缩小同步块也不会带来性能上的降低。

  • IO密集型的代码,在执行不消耗CPU的代码时,其实CPU属于空闲状态的。若是此时让CPU工做起来就能够带来总体上性能的提高。因此在这种状况下,就能够将不须要同步的耗时操做移到同步块外面了。

2)多CPU场景下,将耗时的CPU操做拿到同步块外面,老是能够提高性能的

  • CPU密集型的代码,不存在IO操做等不消耗CPU的代码片断。由于当前是多CPU,其余CPU也多是空闲的。因此在缩小同步块的时候,也会让其余线程尽快的执行这段代码从而带来性能上的提高。

  • IO密集型的代码,由于当前PCU都是空闲的状态,因此将耗时的操做放在同步块外面,必定会带来总体上的性能提高。

固然,无论怎么样,缩小锁的同步范围对于系统来讲都是百利而无一害的,所以上面的代码应该改成:

public void test()
 {
 		synchronized(lock){
 		... ... //正在访问共享资源
 	}
 		... ... //作其它耗时操做,但这些耗时操做与共享资源无关
 }

综上所述,一个重点,就是只将访问共享资源的代码放在同步块内,保证快进快出。

二、死锁的问题

死锁要知道的:

  • 死锁,简单地说就是两个线程或多个线程在同时等待被对方持有的锁致使的,死锁会致使线程没法继续执行并被永久挂起。

  • 若是线程发生了死锁,那咱们就能从线程堆栈中明显的看到”Found one Java-level deadlock“,而且线程栈还会给出死锁的分析结果。

  • 死锁这种问题若是发生在关键系统上就可能会致使系统瘫痪,若是想要快速恢复系统,临时惟一的方法就是保留线程栈先重启,而后再尽快的恢复。

  • 死锁这种问题有时候测试是很难被当即发现的,不少时候在测试时可否及时发现这类问题,就全看你的运气和你准备的测试用例了。

  • 避免死锁这类问题,惟一的办法就是改代码。但一个可靠的系统是设计出来的,而不是经过改BUG改出来的,当出现这种问题的时候就须要从系统设计角度去分析了。

  • 有人会认为死锁会致使CPU 100%,其实对也不对。 要看使用的什么类型的锁了,好比synchronized致使的死锁,那就不会致使CPU100%,只会挂起线程。但若是是自旋锁这种才可能会消耗CPU。

三、共用一把锁的问题

就是多个共享变量会共用一把锁,特别是在方法级别上使用synchronized,从而人为致使的锁竞争。

上例子,下面是新手容易犯的错误:

1 public class MyTest
2 {
3 Object shared;
4 synchronized void fun1() {...} //访问共享变量shared
5 synchronized void fun2() {...} //访问共享变量shared
6 synchronized void fun3() {...} //不访问共享变量shared
7 synchronized void fun4() {...} //不访问共享变量shared
8 synchronized void fun5() {...} //不访问共享变量shared
9 }

上面的代码每个方法都被加了synchronized ,明显违背了保护什么锁什么的原则。

3、线程数咱们通常设多少比较合理呢?

其实你们都知道,在大多数场合下多线程都是能够提升系统的性能和吞吐量,但一个系统到底多少个线程才是合理的?

总的来讲,线程数量太多太少其实都不太好,多了会由于线程频繁切换致使开销增大,有时候反而下降了系统性能。少了又会致使CPU资源不能充分的利用起来,性能没有达到瓶颈。

因此,系统到底使用多少线程合适,是要看系统的线程是否能充分的利用了CPU。其实实际状况,是不少时候不消耗CPU,如:磁盘IO、网络IO等。

磁盘IO、网络IO相比CPU的速度,那简直是至关的慢的,在执行IO的这段时间里CPU实际上是空闲的。若是这时其余线程能把这空闲的CPU利用上,就能够达到提示系统性能和吞吐的目的。

其实上面咱们也提到过,也就是两种计算特性:

CPU密集型: 由于每一个CPU都是高计算负载的状况,若是设置过多的线程反而会产生没必要要的上下文切换。因此,通常线程咱们会设置 CPU 核数 + 1就能够了,为啥要加1 呢,即便当计算(CPU)密集型的线程偶尔因为页缺失故障或者其余缘由而暂停时,这个“额外”的线程也能确保 CPU 的时钟周期不会被浪费,其实就是个备份。

IO密集型:由于大量的IO操做,会致使CPU处于空闲状态,因此这时咱们能够多设置些线程。 因此, 线程数 = CPU 核心数 * (1+ IO 耗时/CPU 耗时) 就能够了,但愿能给你点启发。

爱生活,爱编码,微信搜一搜【架构技术专栏】关注这个喜欢分享的地方。 本文 架构技术专栏 已收录,有各类视频、资料以及技术文章。

相关文章
相关标签/搜索