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()的条件是否成立,
这样就不会在多线程情况下一瞬间的条件不成立让线程继续往下走

2. Notification

调用m的notify方法的线程t必须持有m的monitor,
否则抛出IllegalMonitorStateException

1) notify(): 随机唤醒m的wait set中的其中一个线程t1, 并将t1从wait set中移除

2) notifyAll(): 唤醒所有m的wait set中的所有线程并移除

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


没有正确同步的程序


由于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;  

时间: 2024-10-24 17:37:30

Java 线程与锁的相关文章

Java线程:锁

一.锁的原理 Java中每个对象都有一个内置锁,当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行的代码类的当前实例(this实例)有关的锁.获得一个对象的锁也称为获取锁.锁定对象.在对象上锁定或在对象上同步. 当程序运行到synchronized同步方法或代码块时该对象锁才起作用.一个对象只有一个锁.所以一个线程获得该所,就没有其他线程获得,直到第一个线程释放(或返回)锁.这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放.

Java线程中锁的问题

Java线程中锁的问题: 同步代码块的锁是自己定义的类:object obj = new object 同步方法的锁是this 静态同步方法的锁是类名.class

Java线程与锁

Java线程与锁 本篇是 <深入理解Java虚拟机>的最后一章, 在此涉及到了线程安全, 但并不是如何从代码层次来实现线程安全, 而是虚拟机本身对线程安全做出了哪些努力, 在安全与性能之间又采取了哪些优化措施. 那么一步步来梳理这些概念. 三种线程概念--内核线程.轻量级进程.用户线程 参考 内核线程.轻量级进程.用户线程三种线程概念解惑(线程≠轻量级进程) Linux下的进程类别(内核线程.轻量级进程和用户进程)以及其创建方式--Linux进程的管理与调度(四) 内核线程(Kernel-Le

【Java线程】锁机制:synchronized、Lock、Condition

http://www.infoq.com/cn/articles/java-memory-model-5  深入理解Java内存模型(五)——锁 http://www.ibm.com/developerworks/cn/java/j-jtp10264/  Java 理论与实践: JDK 5.0 中更灵活.更具可伸缩性的锁定机制 http://blog.csdn.net/ghsau/article/details/7481142 1.synchronized 把代码块声明为 synchronize

java线程 公平锁 ReentrantLock(boolean fair)

一.公平锁 1.为什么有公平锁 CPU在调度线程的时候是在等待队列里随机挑选一个线程,由于这种随机性所以是无法保证线程先到先得的(synchronized控制的锁就是这种非公平锁).但这样就会产生饥饿现象,即有些线程(优先级较低的线程)可能永远也无法获取cpu的执行权,优先级高的线程会不断的强制它的资源.那么如何解决饥饿问题呢,这就需要公平锁了. 产生饥饿的另一个原因是:某个线程占据资源不释放,那其他需要该资源的线程只能处于无限等待中.在这里我们主要解决第一种饥饿问题. 2.什么是公平锁 公平锁

java 线程 Lock 锁使用Condition实现线程的等待(await)与通知(signal)

一.Condition 类 在前面我们学习与synchronized锁配合的线程等待(Object.wait)与线程通知(Object.notify),那么对于JDK1.5 的 java.util.concurrent.locks.ReentrantLock 锁,JDK也为我们提供了与此功能相应的类java.util.concurrent.locks.Condition.Condition与重入锁是通过lock.newCondition()方法产生一个与当前重入锁绑定的Condtion实例,我们

java线程详解

Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程.比如在Windows系统中,一个运行的exe就是一个进程. 线程是指进程中的一个执行流程,一个进程中可以运行多个线程.比如java.exe进程中可以运行很多线程.线程总是属于某个进程,进程中的多个线程共享进程的内存. “同时”执行是人的感觉,在线程之间实际上轮换执行. 二.Jav

Java线程详解(三)

Java线程:新特征-有返回值的线程 在Java5之前,线程是没有返回值的,常常为了"有"返回值,破费周折,而且代码很不好写.或者干脆绕过这道坎,走别的路了. 现在Java终于有可返回值的任务(也可以叫做线程)了. 可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口. 执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了. 下面是个很简单的例子: import jav

Java线程详解----借鉴

Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程.比如在Windows系统中,一个运行的exe就是一个进程. 线程是指进程中的一个执行流程,一个进程中可以运行多个线程.比如java.exe进程中可以运行很多线程.线程总是属于某个进程,进程中的多个线程共享进程的内存. “同时”执行是人的感觉,在线程之间实际上轮换执行. 二.Jav