并发编程三要素
- 原子性
- 一个不可再被分割的颗粒,原子性指的是一个操作要么全部执行成功要么全部执行失败。
- 期间不能被中断,也不存在上下文切换,线程切换会带来原子性问题。
- 有序性
- 程序执行的顺序按照代码的先后顺序执行,因为处理器可能会对指令进行重排序。
- 可见性
- 一个线程A对共享变量的修改,另一个线程B能够立刻看到
常见的锁种类
- 悲观锁
- 当线程去操作数据的时候,总认为别的线程回去修改数据,所以每次它拿数据的时候都会上锁,别的线程去拿数据的时候就会阻塞,比如synchronized、ReentrantLock
- 乐观锁
- 每次去拿数据的时候都认为别人不会修改,更新的时候会判断是别人是否回去更新数据,通过版本来判断。
- 如果数据被修改了就拒绝更新,比如CAS就是乐观锁,但严格来说并不是锁,通过原子性来保证数据的同步
- 比如数据库的乐观锁,通过版本控制来实现,乐观的认为在数据更新期间没有其他线程影响。
-
公平锁
- 指多个线程按照申请的顺序来获取锁,简单来说,如果一个线程组里,能保证每个线程都能拿到锁。
- 比如ReentrantLock(底层是同步队列FIFO:First Input First Output来实现)
-
非公平锁
- 获取锁的方式是锁机获取的,保证不了没哥线程都能拿到锁,也就是存在有线程饿死,一直拿不到锁。
- 比如synchronized、ReentrantLock
-
独享锁(互斥锁)
- 也叫他排它锁/写锁/独占锁/独享锁/该锁每一次只能被一个线程所持有,枷锁后任何线程试图再加锁的线程会被阻塞直到当前线程解锁。例如:线程A对data1加上排他锁,则其他线程不能再对data1加任何类型的锁。
- 获得互斥锁的线程即能读数据又能修改数据
-
共享锁
- 也叫S锁/读锁,你那个查看但无法修改和删除的一种数据锁,加锁后其他用户可以并发读取,查询数据但不能修改、增加、删除数据,该锁可被多个线程所持有,用于资源数据共享。
-
可重入锁
- 也叫递归锁,在外层使用锁之后,在内存仍然可以使用,并且不发生死锁
-
不可重入锁
- 若当前线程执行某个方法已经获取了该锁,那么在方法中尝试再次获取锁时,就会获取不到被阻塞。
- 自旋锁
- 一个线程在获取锁的时候,如果锁已经被其他线程获取,那么该线程将循环等待,然后不断的判断锁匙放能够被成功获取,知道获取到锁才会退出循环,任何时候最多只能又一个执行单元获取该锁。
- 不回发生线程状态切换,一直处于用户态,减少了线程上下文切换的消耗,缺点是循环会消耗CPU。
- 分段锁
- 并不是具体的一种锁,只是一种锁的设计,将数据分段上锁,把锁进一步细化粒度,可以提升并发量,当操作不需要更新整个数组的时候,就仅针对数组中的一项进行加锁操作,比如CurrentHashMap底层就用了分段锁。
- 死锁
- 两个或两个以上的线程在执行过程中,由于竞争资源或由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法让程序进行下去