多线程知识汇总(一)

一、线程的状态

   New、Runnable、Teminated、TimeWaiting、Waiting、Blocked
   Runnable又有 ready 和 running两个子状态

二、线程的几个基本方法

   Thread.sleep(1000); 线程休眠,从Runnable进入TimeWaiting;1000ms后唤醒,又变回Runnable。
   Thread.yield();让出一下cpu,然后继续抢。从running变成ready。
   thread.join(); 从Runnable进入Waiting,thread结束后,被唤醒进入Runnalbe

三、Synchronized

   1.可保证多线程时,共享变量的可见性、原子性和有序性。
   2.原理是保证同时只有一个线程进入临界区。
      依赖 monitorenter和monitorexit实现。monitor依赖操作系统的mutex lock 来实现互斥锁,获取到mutex则持有锁,mutex涉及到内核态与用户态的切换,增加系统开销。
   3.在对象头用两位表示锁标志,详细如下图:

mark word.jpg

4.synchronized(o) 锁的是 o 对象,即moniter为o.this;作用在非static方法同理..
   synchronized(static o ) 锁的是class,即moniter为o.class,相当于一个全局锁;作用在static方法同理..
5.synchronized是可重入的对象锁。
   锁对象并非引用;可重入是为了避免死锁,例:子类调用父类同步方法如果不可重入,则会死锁。
6.程序中如果出现异常,锁将被释放。
   即执行monitorexit,释放mutex。
7.锁升级,对照3中的图
    =>无锁 锁标记 0 01
    =>偏向锁:第一个访问锁的线程,未加锁,在偏向锁位记为1,同时记录线程ID。锁标志 1 01
    =>轻量级锁:有线程征用,升级为自旋锁。默认自旋10次。锁标志 00
    =>重量级锁:轻量级锁自旋10次后仍未获取到锁,则升级为重量级锁。锁标志10
8.自旋锁占用cpu,但是不访问操作系统,属于用户态。
   重量级锁不占cpu,但是涉及到用户态和内核态的切换。
ps:加锁代码,执行时间长,线程数多的时候,系统锁更好。执行时间短,线程数少,用自旋锁。

四、wait()、notify()/notifyAll()

1.是本地方法,无法被重写。
2.配合synchronized使用,获取到锁后,才用这三个方法。
3.wait()会让出cpu;
   notify()和notifyAll()虽然会唤醒wait()的线程,但不会让出cpu,会继续执行。所以一般用notify唤醒wait的线程时,都要主动退出临界区,以便被唤醒的线程进入临界区。
4.多线程判断状态时,用notify和wait时,判断一定要用while()而不是if(),因为wait被唤醒线程应该重新判断,而不是直接向下执行。如图:

public synchronized void put(T t) throw Execption {
        while(lists.size() == MAX) { //用while而不是用if。被notify唤醒后还要判断,才可进行下面业务逻辑
            this.wait(); 
        }
        //Todo 业务逻辑
    }

5.依赖锁的monitor来实现的,所以只有在同步代码块或者同步方法中才能调用,否则抛异常。
6.调用wait(),线程会释放锁,线程会进入WaitSet等待队列,被notify()唤醒的时候是随机唤醒WaitSet中的一个线程。

五、锁粗化和锁消除

1.锁粗化
    锁的请求、同步与释放本身会带来性能损耗。所以对同一个锁的高频、不间断的请求、同步与释放会消耗一定系统资源。所以把多次频繁的锁请求合并成一次,就会降低资源的消耗。
    注意:锁粗化有可能把原本不需要加锁的代码放到锁中执行,所以如果代码执行的时间很长,就会大大提高锁的持有时间,这样锁粗化就不合理了。
2.锁消除
    锁消除是指虚拟机即时编译器在运行时,检测到一些加锁的代码不可能存在共享数据竞争的锁进行消除。
    想让锁消除生效,需要开启逃逸分析。

-server -XX:+DoEscapeAnalysis -XX:+EliminateLocks

    -server 表示以server模式启动,只有server模型才会进行锁消除优化;+DoEscapeAnalysis表示开启逃逸分析;+EliminateLocks表示开启锁消除。

六、happens-before原则

1.程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作;
2.锁定规则:一个unLock操作先行发生于后面对同一个锁额lock操作;
3.volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作;
4.传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C;
5.线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作;
6.线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生;
7.线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行;
8.对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始;

七、volatile关键字

1.汇编层实际是通过lock指令实现的
2.保证线程可见性,本质上时是使用了cpu的MESI,即缓存一致性协议。
   写时强制将修改值刷如内存
   同时把其他线程的该volatile的缓存行置为无效
   其他线程使用该volatile值时,发现缓存无效,则直接内存中获取
3.禁止指令重排序,使用内存屏障
   内存屏障有四种 StoreStore、LoadLoad、StoreLoad、LoadStore四种屏障。
4.volatile的内存屏障策略非常严格保守,如下:
   写操作前加入StoreStore,保证之前写操作对其他线程可见。
   写操作后加入StoreLoad,保证写操作对其他线程可见。
   读操作前加入LoadLoad,保证读操作完成后,才可进行后续读。
   读操作后加入LoadStore,保证读操作完成后,才可进行后续写。
5.单例的双重检查写法,注意要用volatile。否则可能会有指令重排问题。因为new不是原子操作的。

 public class Singleton {
    private Singleton(){}
    private static volatile Singleton singleton; //这里要用volatile
    public static  Singleton getInstance(){
        if (singleton == null){
            synchronized (Singleton.class){
                if (singleton == null){
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

八、CAS 和 Atomic

1.compare and swap 比较替换
2.cas(OldVal,Expected,NewVal)

  if(O == E) O=N;
  else try again

3.cas是cpu原语支持的,是原子操作。
4.ABA问题,解决办法,加个版本号。 AtomicStampedReference可以解决。
5.简单了解 Unsafe类
6.简单了解LongAddr,采用分段锁

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,198评论 6 514
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,334评论 3 398
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,643评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,495评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,502评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,156评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,743评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,659评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,200评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,282评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,424评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,107评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,789评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,264评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,390评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,798评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,435评论 2 359

推荐阅读更多精彩内容

  • Java多线程学习 [-] 一扩展javalangThread类 二实现javalangRunnable接口 三T...
    影驰阅读 2,964评论 1 18
  • 本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 首先讲...
    李欣阳阅读 2,458评论 1 15
  • 一、进程和线程 进程 进程就是一个执行中的程序实例,每个进程都有自己独立的一块内存空间,一个进程中可以有多个线程。...
    阿敏其人阅读 2,612评论 0 13
  • 1.解决信号量丢失和假唤醒 public class MyWaitNotify3{ MonitorObject m...
    Q罗阅读 881评论 0 1
  • 35. 并行(parallellism )和并发(concurrency)有什么区别? 并行:同一时刻有多条指令在...
    CgySHFF阅读 431评论 0 0