重温《并发编程实战》---性能和可伸缩性

来源:互联网 发布:紫微排盘详批软件 编辑:程序博客网 时间:2024/05/18 13:12

1.资源:资源的含义很广,举几个例子:CPU时钟周期,内存,网络带宽,I/O带宽,数据库请求,磁盘空间等。

 

 

2.使用多线程会引入一些开销,包括:

a.)线程之间的协调(加锁,触发信号以及内存同步)

b.)增加的上下文切换

c.)线程的创建和销毁

d.)线程的调度

 

3.可伸缩性指的是:当增加计算资源的时候(内存、CPU、存储容量、I/O带宽),程序的吞吐量或者处理能力能相应的增加。

 

 

4.线程引入的开销:

a.)上下文切换+线程调度:

a.1)当操作系统把某个正在运行的线程调度出来,从而使其他线程能够使用cpu的时候,发生了:线程调度+1次上下文切换。

上下文切换需要一定的开销。

而在线程调度中也需要JVM+操作系统的合作,然而我们知道应用程序、JVM、操作系统都共用一组相同的CPU,当你的线程调度频繁发生,JVM+操作系统的开销就很大,占用了很多CPU资源导致应用程序可用的CPU资源就减少了。

 

以上是上下文切换和线程调度的操作系统和JVM的开销

 

a.2)当一个新的线程被切换进来的时候,它所需要的数据可能不在当前处理器的缓存中,因此上下文切换还可能导致一些缓存缺失,因而线程会在首次调用运行时更加缓慢。

 

当线程阻塞时,JVM通常会将这个线程挂起,并允许它被交换出去。如果频繁的发生阻塞,CPU密集型的程序就会越多的发生上下文切换,从而增加调度开销,因此降低吞吐量。

 

 

b.)无竞争同步和有竞争同步:

b.1)无竞争的同步开销一般很小,而且JVM能通过优化来去掉一些不会发生竞争的锁,从而减少不必要的同步开销。例如:

1)Synchronized(new Object()){} 这个新对象在其他线程中不会发生同步,因此JVM会去掉这个锁操作。

2)method(){

List<xx> s = new Vector<xx>();

s.add(1);

s.add(1);

s.add(1);

s.add(1);

}

此时JVM会找到不会发布到堆的本地对象引用(通过逸出分析来完成),s其实是线程的本地变量,在执行过程中本来会发生4次锁获取+锁释放,JVM通过优化操作,会去掉这些锁获取操作。

3.)锁粒度粗化优化:将临近的同步代码块用同一个锁合并起来。

 

不需要担心非竞争同步带来的开销,我们应该将重点放在发生锁竞争的地方。

 

 

b.2)竞争的同步:竞争的同步通常需要操作系统的介入,从而增加开销。

当在锁上发上竞争的时候,竞争失败的线程肯定会阻塞。JVM在实现阻塞行为的时候,可以采用自旋等待或者通过操作系统挂起被阻塞的线程。等待之间较短的时候比较适合自选等待,等待时间比较长的时候,适合线程挂起,大部分JVM会在等待锁的时候将线程挂起。

当线程无法获取某个锁或者在某个条件等待或者在I/O操作上阻塞的时候,需要被挂起,这包含额外的上下文切换和额所有必要的操作系统和缓存操作。

线程在持有锁的时候也存在一定的开销:当它释放锁的时候,必须告诉操作系用恢复运行阻塞的线程。

原创粉丝点击