Java 线程与锁
Synchronization
synchronized语法可以获取锁, 当其他线程持有锁的时候该线程想要获取锁将会进入等待状态, 直到没有其他线程持有该锁
显示使用 synchronized (lock) 将会获取lock对象的锁
没有显示指定锁对象将会获取当前类的class对象的锁
Wait and Notification
每个对象m都有一个wait set, 调用该对象的wait方法的线程会被加入到wait set
1. Wait
调用某个对象m的wait方法会使调用线程t进入等待状态, 他有如下几个派生方法
wait(): 无限期等待, 直到被wait对象被notify或notifyAll
wait(long timeout): 等待timeout毫秒, 或被notify
wait(long timeout, int nanos): 等待 1000000*timeout+nanos 纳秒
调用lock.wait()必须持有lock的monitor(锁), 否则会抛出IllegalMonitorStateException, 例如
private final static Object lock = new Object(); public static void main(String[] args) throws InterruptedException { synchronized (lock) { // 该线程必须持有锁
while (condition not hold) {
lock.wait();
}
// logic code
} }
线程t会在如下情况从对象的wait set移除:
1) 在t被选为从wait set中移除的线程的时候, 调用m的notify方法
2) 调用m的notifyAll方法
3) t被中断
4) wait的时间到达指定值
*: 一般来说wait都是放在一个循环里, 这个循环会一直判断wait()的条件是否成立, 这是因为如果有evil线程将wait唤醒, 有可能你继续执行的条件还未成立, 这样就可以继续进入wait状态
调用wait()会释放锁, 当被notify()的时候, 会重新获得锁
2. Notification
调用m的notify方法的线程t必须持有m的monitor, 否则抛出IllegalMonitorStateException
1) notify(): 随机唤醒m的wait set中的其中一个线程t1, 并将t1从wait set中移除
2) notifyAll(): 唤醒所有m的wait set中的所有线程并移除
调用notify()并不会释放锁, 所以, 即使调用了notify(), 另一个正在wait()的线程也需要等到notify()线程中的同步块执行完毕释放锁以后才能继续执行, 例如
synchronized (lock) {
monitor.notify()
foo()
}
则必须等到foo()执行完毕, await()线程重新获得锁以后才能继续执行
3. Interruptions
调用线程t.interrupt()会将t的 interruption status 设置为 true
如果 t 在 m 的 wait set 里, 调用 t.interrupt() 会将 t 从 m 的 wait set 里移除, 然后wait()方法会抛出InterruptedException, 并将 interruption status 清除.
4. Waits, Notification, Interruption的交互
如果一个线程处于waiting状态, 但是同时被notify和interrupt 它要么:
a. 如果notify在前, 则正常唤醒, 并调用线程的interrupt, interrupt标志位置为true
b. 如果interrupt在前, 则t抛出InterruptedException, 然后另一个线程u将被唤醒并从wait set移除
Sleep and Yield
yeild 会让当前执行线程暂停, 并让同优先级的线程执行.
sleep 指定某个线程的暂停一定的时间
yeild和sleep都不需要获得锁, 也不能被唤醒
调用 sleep 和 yield 不会刷新寄存器缓存到共享内存中, 也不会让寄存器缓存重载.
while (!this.done)
Thread.sleep(1000);
所以如果 this.done 如果不是volatile的, 则有可能使得循环内一直使用相同的this.done值, 这样循环可能永远停不下来, 即使别的线程修改了this.done的值
Memory Model
1. 没有正确同步的程序
由于Java编译器和微处理器会对程序进行优化, 如果不进行正确的同步操作, 可能会出现很诡异的行为.
例如, 以下程序使用本地变量 r1, r2, 并共享变量 A B, 最初 A == B == 0
Thread 1 | Thread 2 |
---|---|
1: r2 = A; |
3: r1 = B; |
2: B = 1; |
4: A = 2; |
可能会觉得 r2 == 2, r1 == 1 并不可能. 从直觉上来看, 指令1先执行, r2则看不到指令4中A的变化, 指令3先执行, r1则看不到指令2中B的变化.
然而, 由于编译器的优化, 很有可能会打乱执行顺序, 造成最后的结果是r2 == 2, r1 == 1, 如下
Thread 1 | Thread 2 |
---|---|
B = 1; |
r1 = B; |
r2 = A; |
A = 2;
|
像这种程序在多线程中是不正确的:
1. 在一个线程中有写共享变量操作
2. 在另一个线程中读这个共享变量
3. 读和写的顺序没有被同步控制
这种情况叫做数据竞争(data race), 如果代码里出现数据竞争, 则有可能出现反直觉的结果.
另一个例子如下, 最初 p == q 并且 p.x == 0. 这个程序也是不正确的, 因为它在不同的线程中读写共享变量, 并且不适用同步控制
Thread 1 | Thread 2 |
---|---|
r1 = p; |
r6 = p; |
r2 = r1.x; |
r6.x = 3; |
r3 = q; |
|
r4 = r3.x; |
|
r5 = r1.x; |
这种情况下, 编译器可能将r2的值复用优化给r5, 变为如下
Thread 1 | Thread 2 |
---|---|
r1 = p; |
r6 = p; |
r2 = r1.x; |
r6.x = 3; |
r3 = q; |
|
r4 = r3.x; |
|
r5 = r2; |
2. 共享变量
可以被不同的线程共享的内存叫共享内存或堆内存, 所有的实例变量, 静态变量, 数组元素都存在对内存. 在这章里变量包括上述所有类型的变量. 下面提到的变量都是指这些变量
本地变量, 方法参数, 异常处理器参数不会被线程共享, 也不会受内存模型的影响.
如果不同线程对同一个共享变量的访问(读写)中含有写操作, 则这些访问是冲突的.
3. Actions
线程间行为(inter-thread action)指的是由一个线程执行, 但是可以被其他线程检测到或直接影响到的行为. 包括如下几种:
a. 读非volatile(非本地)变量
b. 写非volatile变量
c. 同步行为.
1) Volatile读
2) Volatile写
3) Lock 锁行为
4) UnLock 解锁行为
一个 Action 由 < t, k, v, u > 元组构成
a. t - 执行action的线程
b. k - action类型
c. v - action 中包含的变量或者monitor
对于Lock action, v 是被锁住的monitor; 对于 unlock action, v 是被解锁的monitor
如果action是读, 则v是被读得变量
如果action是写, 则v是被写的变量
d. u - 任意唯一action标识符
4. Synchronization Order
每次执行都有一个同步顺序, 同步顺序是一次执行中所有同步行为的总顺序. 对于同一个线程t来说, t内的同步行为的同步顺序跟t的程序顺序是一致的
同步行为包括 synchronized-with的关系, 如下:
a. 对monitor m 的unlock行为和lock行为是同步的
b. 对volatile变量v的写操作和任何线程对他的读操作是同步的
c. 线程的start操作和线程start时的第一个action同步
d. 变量的默认值写操作和线程中的第一个action同步
e. 线程T1的最后一个action和线程T2中用来检查T1是否终止的操作同步. 例如: T2
may accomplish this by calling T1
.isAlive()
or T1
.join()
.
5. Happens-before Order
参考这个. http://www.iteye.com/topic/260515/ 写的不错
Java 线程与锁的更多相关文章
- Java线程:锁
一.锁的原理 Java中每个对象都有一个内置锁,当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行的代码类的当前实例(this实例)有关的锁.获得一个对象的锁也称为获取锁.锁 ...
- Java线程与锁
概要:线程的实现方法. 线程调度.线程状态及转换.线程安全(5种分类.3种实现方法.锁优化技术) 进程是OS进行资源分配的基本单位,线程是CPU调度的基本单位. 1.线程的实现方法 可参阅 我是一个进 ...
- java 线程 Lock 锁使用Condition实现线程的等待(await)与通知(signal)
一.Condition 类 在前面我们学习与synchronized锁配合的线程等待(Object.wait)与线程通知(Object.notify),那么对于JDK1.5 的 java.util.c ...
- java线程 公平锁 ReentrantLock(boolean fair)
一.公平锁 1.为什么有公平锁 CPU在调度线程的时候是在等待队列里随机挑选一个线程,由于这种随机性所以是无法保证线程先到先得的(synchronized控制的锁就是这种非公平锁).但这样就会产生饥饿 ...
- 【Java线程】锁机制:synchronized、Lock、Condition
http://www.infoq.com/cn/articles/java-memory-model-5 深入理解Java内存模型(五)——锁 http://www.ibm.com/develope ...
- Java线程同步锁
把synchronized当作函数修饰符时,示例代码如下: Public synchronized void method(){ //-. } 这也就是同步方法,那这时synchronized锁定的是 ...
- 【java线程】锁机制
转载于:https://blog.csdn.net/vking_wang/article/details/9952063 http://www.infoq.com/cn/articles/java-m ...
- 【Java线程】锁机制:synchronized、Lock、Condition(转)
原文地址 1.synchronized 把代码块声明为 synchronized,有两个重要后果,通常是指该代码具有 原子性(atomicity)和 可见性(visibility). 1.1 原子性 ...
- Java线程八锁
package com.atguigu.juc1205; import java.util.concurrent.TimeUnit; class Phone//Phone.java ---> P ...
随机推荐
- 在Win10下搭建web服务器,使用本机IP不能访问,但是使用localhos或127.0.0.1可以正常访问的解决办法
最近在在Win10下搭建web服务器,发现通过windows自带的浏览器win10 edge浏览器使用本机IP不能放问,但是使用localhos或127.0.0.1可以正常访问, 后来无意发现,使用w ...
- InnoDB的锁机制浅析(二)—探索InnoDB中的锁(Record锁/Gap锁/Next-key锁/插入意向锁)
Record锁/Gap锁/Next-key锁/插入意向锁 文章总共分为五个部分: InnoDB的锁机制浅析(一)-基本概念/兼容矩阵 InnoDB的锁机制浅析(二)-探索InnoDB中的锁(Recor ...
- ABP-Zero模块
一.介绍 二.启动模版 三.功能 1,租户管理 2,版本管理 3,用户管理 4,角色管理 5,组织单位管理 6,权限管理 7,语言管理 8,Identity Server集成 一.介绍 1,Zero模 ...
- android 设置为系统应用
韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha 将一个应用apk放到手机的 /系统/应用 这个目录下, 就会是 系统应用.
- Manthan, Codefest 18 (Div 1 + Div 2) (A~E)
目录 Codeforces 1037 A.Packets B.Reach Median C.Equalize D.Valid BFS E.Trips(正难则反) Codeforces 1037 比赛链 ...
- Xtreme8.0 - Kabloom 动态规划
Xtreme8.0 - Kabloom 题目连接: https://www.hackerrank.com/contests/ieeextreme-challenges/challenges/kablo ...
- windows servier2008+virtualenv下部署Flask (IIS+wfastcgi)
由于业务只有一台windows server2008的服务器,一般的nginx+uwsgi的部署方式不行,以下记录部署过程,本文参考自:https://www.cnblogs.com/xiaolecn ...
- HTML解析利器HtmlAgilityPack
一个.NET下的HTML解析类库HtmlAgilityPack.HtmlAgilityPack是一个支持用XPath来解析HTML的类库,在花了一点时间学习了解HtmlAgilityPack的API和 ...
- 使用CefSharp在.Net程序中嵌入Chrome浏览器(六)——调试
chrome强大的调试功能令许多开发者爱不释手,在使用cef的时候,我们也可以继承这强大的开发者工具. 集成调试: 我们可以使用如下函数直接使用集成在chrome里的开发者工具 _chrome.Sho ...
- MongoDB中的变更通知
MongoDb 3.6中引入了一个新特性change stream,简单的来说就是变更通知,它提供了一个接口允许应用实时获取数据库变更,这个在ETL.数据同步.数据迁移.消息通知等方面非常有用. 使用 ...