1:关于多线程简单知识就不介绍了,从此篇开始介绍关于多线程的一些深入知识。这一篇主要介绍线程的开启和synchronized的一些使用。
2:线程开启的三种方式:
a:T1 Extends Thread 重写run()方法
new T1().start();
b::T2 Implements Runnable 重写run()方法
new Thread(new T2()).start();
c:通过线程池来启动(严格来说也是上面的两种)
3:synchronized
a:syncyronized锁住的不是方法而是对象(this/xxx.class),锁定方法和非锁定方法可以同时执行。
b:synchronized(object),锁定对象一般用object,不要使用String常量和基本数据类型。
c:synchronized是可重入的,可以再次获取同一个对象的锁,计数加1.
d:synchronized是比较安全的,在加锁代码中如果发生了异常,会自动释放占有的锁,不会导致死锁。
e:一般使用了synchronized,就不需要volatile,synchronized保证原子性和一致性,而volatile只保证一致性,不保证原子性。所以volatile一般加在变量上(对于volatile这一篇不做重点介绍,以后会专门说明)。
f:锁定某对象o,如果o的属性发生改变,不影响锁的使用;但是如果o变成另外一个对象,则锁定的对象发生改变。应该避免将锁定对象的引用变成另外的对象,给要加锁的对象加上final关键字
g:synchronized的底层实现,也叫锁的膨化:
在很早之前的jdk中,synchronized是重量级的,它是直接对操作系统申请资源(内核态);
jdk1.5之后,jdk对synchronized进行了优化。线程在访问synchronized标识的对象的时候,对象头部中markword有两个字节记录这个线程ID,记录是否加了锁,加了什么类型的锁。如果markword 中只有一个线程ID,说明不存在竞争,此时为偏向锁,不会加锁------>当有线程再次访问对象时,则markword 中会再次记录一个线程的ID,此时存在了线程争用,synchronized会升级为自旋锁,自旋锁占用cpu,但不访问操作系统,效率比较高(本地态),一般默认自旋10次-------->10次以后,自旋锁升级为重量级锁,直接访问操作系统,比较笨重。
讨论:
1:synchronized一旦升级后是不能降级的,所以可能导致一个问题:锁膨胀后,此时只有一个进程使用对象,显然再使用重量级锁会导致效率很慢,如果此时能降级成偏向锁就好了。但这个问题目前还没有解决。
2:什么时候使用自旋锁比较好?什么时候使用重量级锁?
执行时间短(加锁代码),线程数少,用自旋锁
执行时间长,线程数多,用系统锁
3:上面提到了内核态和本地态:这二代表两块内部内存,但访问本地态效率比较高,内核态是直接访问操作系统,而且内核态可以访问本地态,但本地态不能访问内核态。
注意:本文仅代表菜鸟博主的个人观点,如果哪里不对或者路过技术大大有更好的想法,欢迎留言告知,分享和交流使我们进步,谢谢。