并发相关知识点整理

并发,即一个代码块同时被多个线程执行,代码块中的变量会被同时的线程修改成不同的值,造成数据错乱,运行结果错误现象产生,如何来避免这一问题产生呢?这就产生了锁机制,通过对代码块加锁,来保证同一时刻只能有一个线程来操作数据,这样就能保证数据的一致性。

一、锁机制

  • 重入锁/不可重入锁
  • 共享锁/互斥锁
  • 乐观锁/悲观锁
  • 自旋锁

重入锁/不可重入锁

在同一个线程中,可重复进入的锁,就可重入锁。如,一个线程中进入了一个带锁的方法,再次进入使用同一个锁的其他方法时,不需要再获取锁,可直接进入。

可重入锁

  • ReentrantLock
  • synchronized

不可重入锁

  • NoReentrantLock

共享锁/互斥锁

当一个线程获取到锁后,其他线程也可以获取此锁,则此锁是共享锁,如读锁;如果其他线程不能获取此锁,则此锁是互斥锁,如写锁

  • ReentrantReadWriteLock

乐观锁/悲观锁

乐观锁认为并发操作一定会修改数据;悲观锁认为并发操作不会修改数据,但在写数据时一般

乐观锁

  • AtomickInteger
  • AtomickXXX

悲观锁

  • ReentrantLock
  • synchronized

自旋锁

一般情况下线程在未获取到锁的情况下会被阻塞,而自旋锁则是内部有一个循环体在轮询锁是否可用。自旋锁可以减少线程上下文切换带来的消耗,但会消耗CPU。

  • AtomicInteger
  • AtomicXXXX

二、死锁

线程都要获取锁才能进行运行,但所有的线程都获取不到,导致出现所有线程都不能运行的情况,这种情况就叫作死锁。

死锁场景

1. 设计不当

线程内有多个锁,一个线程在获取锁A后,需要再次获取锁B才能执行相关代码,而此刻另一线程已经获取到锁B,但它却在等待获取锁A来执行代码,两个线程都要获取对方已经获取到的锁,而又都获取不到,造成死循环,也就是死锁现象。

2. 程序异常

线程在获取锁后执行相关代码,执行完相关代码后释放锁。但执行过程发生了异常,导致线程意外退出,获取到的锁没有被释放,导致其他所有线程都无法获取锁的情况。
解决方案:添加try/catch,在finally块里释放锁

class AClass{
    ReentrantLock lock=new ReentrantLock();
    
    void aMethod() {
        lock.lock();
        try {
            doSomeThing();
        } catch(Throwable e){
            e.printStacktrace();
        } finally {      
            lock.unlock();
        }
    }
}

三、synchronized使用方式

synchronized可根据加锁范围分为以下两种方式,但

  • 锁对象
  • 锁类

锁对象

即针对对象中的方法进行加锁,一个对象的加锁方法被线程进入后,其他线程不能再进入此对象加锁的方法,但可以进入不同对象的同一加锁方法。

  1. synchronized修饰普通方法
public synchronized void test1(){
    ...
}
  1. synchronized代码块,内部使用this或成员变量
public void test1(){
    synchronized(this){
        ...
    }
}

锁类

类锁与对象锁是一致的,但类方法在内存中只有一份。加锁的是整个类方法,当一个线程获取到类方法锁后,其他线程不能再进入此方法。

  1. synchronized修饰静态方法
public synchronized static void test1(){
    ....
}
  1. synchronized代码块,内部使用class
public void test1(){
    synchronized(Test.class){
        ...
    }
}

四、锁机制单例中的应用

单例对并发方面的考虑主要是

  1. 防止创建多个对象。单例要求全局只有一个对象
  2. 防止单例对象中的数据被并发修改造成错误

创建方式

1. 懒汉模式

需要时再创建

class ConnectionManager{

    private static ConnectionManager sInstance = new ConnectionManager();
    
    private ConnectionManager(){
        
    }
    
    /**
     * 此方式每次获取都会加锁,目的是防止创建多个对象,
     * 但创建对象只有一次,其他都是获取,造成性能损耗
     */
    public synchronized static ConnectionManager getInstance(){
        if(sInstance==null){
            sInstance=new ConnectionManager();
        }
        return sInstance;
    }
}

2. 饿汉模式

直接初始化(太饥饿,上来就直接吃)

public class ConnectionManager{

    private static ConnectionManager sInstance = new ConnectionManager();
    
    private ConnectionManager(){
    }
    
    public static ConnectionManager getInstance(){
        return sInstance;
    }
} 

3. 双重检测

a. 为什么要2次null判断?   
    防止两个线程同时进入第一个if模块,当一个线程获取锁后创建一个对象;在它释放锁后,另一个线程获取到锁,又创建一个对象。   
b. 为什么使用volatile修饰?   
    防止指令重排问题。sInstance=new ConnectionManager();这句代码并不是原子操作,它有三条指令:分配内存、初始化内存、赋值变量。如果先赋值后初始化,就可能会出现另一线程在第一个null判断时,发现不为空,直接使用对象的情况,而此时对象可能还没被初始化造成错误。
class ConnectionManager{
    private volatile static ConnectionManager sInstance = null;
    private ConnectionManager(){
        
    }
    
     public static ConnectionManager getInstance(){
        if(sInstance==null){
            synchronized(ConnectionManager.class){
                if(sInstance==null){
                    sInstance=new ConnectionManager();
                }
            }
        }
        return sInstance;
    }
}

4. 静态内部类

class ConnectionManager{

    private ConnectionManager(){
        
    }
    
    public static ConnectionManager getInstance(){
        return Holder.INSTANCE;
    }

    private static class Holder{
        private final static ConnectionManager INSTANCE = new ConnectionManager();
    }
}

5. 枚举

enum ConnectionManager{
    INSTANCE;
    private ConnectionManager(){
        
    }
}  

五、进程通信

  • binder
  • 管道
  • 广播
  • 文件
  • socket

生产者消费者机制

信息号机制

六、线程安全相关类

容器类

  • ConcurentHashMap
  • CopyOnWriteArrayList
  • CopyOnWriteArraySet
  • LinkedBlockingList
  • LinkedBlockingQueue
  • Vector
  • HashTable
  • Collections.synchronizedList(new ArrayList<T>());
  • Collections.synchronizedSet(new HashSet<T>());
  • Collections.synchronizedMap(new HashMap<String,String>());

字符串类

  • StringBuffer

参考

https://zhuanlan.zhihu.com/p/52563959

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

推荐阅读更多精彩内容

  • 面试发现经常有些重复的面试问题,自己也应该学会记录下来,最好自己能做成笔记,在下一次面的时候说得有条不紊,深入具体...
    SunShine_skay阅读 358评论 0 0
  • 简介 整理容器的基本知识 整理关于不同容器线程安全方面的知识 根据以下资料整理 http://www.jiansh...
    千叶鸟阅读 439评论 0 1
  • 导读:记录下java中提供锁的两种方式:synchronized和Lock(juc里面提供的锁) 一、问题的产生和...
    Toyouy阅读 343评论 0 0
  • 一、知识结构分析 多线程之间的关系 pthread是POSIX线程的API NSThread是Cocoa对pthr...
    huoshe2019阅读 489评论 0 4
  • iOS中多线程 首先看一道面试题 iOS中多线程有哪些实现方案? iOS中,多线程一般有三种方案GCD、NSOpe...
    木子奕阅读 662评论 0 1