版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yuxiatongzhi/article/details/75356327
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()就能正常获取
* <pre>
* mAlbumArtWorker = new Worker("album art worker");
* mAlbumArtHandler = new AlbumArtHandler(mAlbumArtWorker.getLooper());
* </pre>
*/
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
锁可在对象之间传递,因此使用更灵活
扫描二维码关注公众号,回复:
2904483 查看本文章
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<V>
对象交换器
适用于两个线程需要交换数据的场景。
- 只能有2个线程交换数据
- exchange是交换点,另一线程未到达则本线程在此等待
- 各自线程exchange函数的返回值是另一线程exchange的参数值
public class FillAndEmpty
{
Exchanger<DataBuffer> exchanger = new Exchanger<DataBuffer>();
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();
}
}