1. volatitle
volatitle
对共享变量进行同步。在写入volatitle
变量值之后,CPU缓存中的内容会被写回主存,再读取volatitle
变量值时,缓存值为失效状态,然后重新从主存读取已改变过的值。
2. synchronized 关键字
所有的Java对象都有一个与之关联的监视器对象,允许在该监视器上进行加锁和解锁
这里一定要理解清楚,我们加锁的是监视器对象,而不是代码code
-
synchronized
静态方法
监视器对象是所在Java类对应的Class
-
synchronized
实例对象方法
监视器对象是当前对象实例
-
synchronized
代码块
代码块声明中的对象
2.1 synchronized
方法
2.1.1. synchronized
关键字的继承性
synchronized
关键字是不能继承的,父类的 synchronized
方法在子类中并不是synchronized
,子类需要显式地为某个方法加synchronized
,以变成同步方法
2.1.2. synchronized
实例对象方法
synchronized
method(){}的监视器对象是这个实例对象,加锁的对象不是这个方法,而是实例对象
public class Foo
{
/**
* 加锁的监视器对象是class Foo 的 实例对象 Foo foo = new Foo()后的foo
* 而不是这个方法mothodA
*/
public synchronized void mothodA()
{}
public synchronized void mothodB()
{}
}
- 有多个线程去访问
foo.mothodA()
时,同时只有一个线程能访问foo
的mothodA()
方法 - 一个类的实例对象有多个
synchronized
的方法时,只要一个线程访问了其中一个synchronized
方法,其它线程就不能访问这个对象中的任何一个synchronized
方法 - 不同实例对象间的
synchronized
方法调用时互不影响的
Foo foo1 = new Foo();
Foo foo2 = new Foo();
不同的线程分别访问foo1
、foo2
中的synchronized mothodA()
方法是互不影响的,因为它们的监视器对象分别是foo1
、foo2
,同一个监视器对象才会阻塞同步
2.1.3 synchronized
static 静态方法
synchronized static staticMethodA()
的监视器对象是Foo.class
类,所有访问这个类的静态同步方法的线程,都在同一个 Foo.class
上加锁和解锁,所以对所有线程中的类实例对象的同步起作用。
class Foo
{
public synchronized static methodA();
public synchronized static staticMethodA();
}
一个线程里调用了Foo.staticMethodA()
,会对其它线程调用foo.methodA()
造成同步
2.2 synchronized
代码块
- 多个并发线程访问一个对象的
synchronized(this)
同步代码块时,同一时间只有一个线程执行 - 当一个线程访问一个对象的
synchronized(this)
同步代码块时,其它线程仍然可以访问这个对象的非synchronized(this)
代码块,而对对象中其它所有的synchronized(this)
同步代码块的访问都将被阻塞
class Foo
{
public void methodA()
{
/**
* 加锁对象是 实例对象
* this 关键字代表实例对象本身
*/
synchronized (this) {
}
}
}
public class Foo extends Thread
{
private int val;
//全局
private static Object lock = new Object();
public Foo(int v)
{
val = v;
}
@Override
public void run()
{
printVal(val);
}
public void printVal(int v)
{
synchronized (lock)
{
while(true)
System.out.println(v);
}
}
}
3. Object类的wait、notify、notifyAll
/**
* 以以下代码顺序执行的方式就能确保异步执行的过程正确的获取到looper,
* 当前线程里调用 new Worker(),如果looper还未创建调用线程就陷入wait状
* 态,构造函数里启动另一线程,创建looper后会唤醒new Worker()的调用线
* 程,这时new Worker()才执行完,接着执行下面的getLooper()就能正常获取
*
* mAlbumArtWorker = new Worker("album art worker");
* mAlbumArtHandler = new AlbumArtHandler(mAlbumArtWorker.getLooper());
*
*/
public class Worker implements Runnable
{
private final Object mLock = new Object();
private Looper mLooper;
Worker(String name)
{
//调用线程里构造函数启动另一个线程
Thread t = new Thread(null,this,name);
t.setPriority(Thread.MIN_PRIORITY);
t.start();
synchronized (mLock)
{
//如果当前looper对象未创建
while(mLooper == null)
{
try
{
//调用构造函数的线程wait,并释放监视对象mLock持有的锁
mLock.wait();
} catch (InterruptedException ex)
{}
}
}
}
public Looper getLooper()
{
return mLooper;
}
@Override
public void run()
{
//对检视对象mLock加锁
synchronized (mLock)
{
Looper.prepare();
mLooper = Looper.myLooper();
//唤醒监视对象mLock上等待的所有线程,如果调用构造函数的线程在wait状态,将被唤醒
mLock.notifyAll();
}
Looper.loop();
}
public void quit()
{
mLooper.quit();
}
}
4.高级实用工具
4.1 java.util.concurrent.locks
中的API
锁可在对象之间传递,因此使用更灵活
4.2 java.util.concurrent.Semaphore
信号量
可以创建一个new Semaphore(0)
零个许可的信号量作为一个阻塞点,另一个工作线程处理完一定的工作后release()
释放一个许可出来,让前面阻塞的线程继续执行
4.3 java.util.concurrent.CountDownLatch
倒数闸门
4.4 java.util.concurrent.CyclicBarrier
循环屏障
4.5 java.util.concurrent.Exchanger
对象交换器
适用于两个线程需要交换数据的场景。
- 只能有2个线程交换数据
- exchange是交换点,另一线程未到达则本线程在此等待
- 各自线程exchange函数的返回值是另一线程exchange的参数值
public class FillAndEmpty
{
Exchanger exchanger = new Exchanger();
DataBuffer initialEmptyBuffer = ... a made-up type
DataBuffer initialFullBuffer = ...
class FillingLoop implements Runnable
{
public void run()
{
//生成一个empty 的空缓冲区
DataBuffer currentBuffer = initialEmptyBuffer;
try {
while (currentBuffer != null) {
//向空缓冲区填充数据
addToBuffer(currentBuffer);
if (currentBuffer.isFull())
{
/**
* 1.到达交换点,如果另一个线程还没有到exchanger.exchange交换点,则在此阻塞等待
* 2.这里exchanger.exchange 返回的数据,就是另一线程到达exchanger.exchange(dataA)时
* 传递的dataA,并把自己exchanger.exchange(dataB)传递的dataB传递给另一线程的exchanger.exchange
* 作为返回值
*/
currentBuffer = exchanger.exchange(currentBuffer);
//另一线程到达exchanger.exchange后,彼此线程安全的交换数据exchanger.exchange()函数返回后继续执行
}
}
} catch (InterruptedException ex) { ...handle ...}
}
}
class EmptyingLoop implements Runnable
{
public void run()
{
DataBuffer currentBuffer = initialFullBuffer;
try {
while (currentBuffer != null) {
takeFromBuffer(currentBuffer);
//缓冲区数据是空的
if (currentBuffer.isEmpty())
{
/**
* 1.进入交换点,如果填充线程未到达交换点,则再次阻塞,等待填充线程到达时继续执行
* 2.exchanger.exchange(currentBuffer) 把自己空的缓冲区作为参数传递给填充线程,填充
* 线程exchanger.exchange的返回值就是本线程(exchanger.exchange(dataA)传递的dataA,
* 本线程exchanger.exchange的返回值时另一线程exchanger.exchange(dataB)传递的参数dataB
*/
currentBuffer = exchanger.exchange(currentBuffer);
}
}
} catch (InterruptedException ex) { ...handle ...}
}
}
void start()
{
new Thread(new FillingLoop()).start();
new Thread(new EmptyingLoop()).start();
}
}
5. 处理 InterruptedException
异常中断——注意:不可中断的阻塞方法
https://www.ibm.com/developerworks/cn/java/j-jtp05236.html