[size=large]Lock和synchronized区别[/size]
1、synchronized是java提供的内置关键字,一旦使用线程就被锁住,直到线程执行完成或者处于wait状态下才会释放;如果线程处于阻塞了就会其他线程就会一直等待,如sleep;
2、Lock是一个接口,在JDK1.5提供,属于JUC下面常用的同步处理接口。在执行同步是都需要通过方法获取锁,执行完成之后必须释放锁;在获取锁时,通过tryLock方法可以判断锁是否被占用,从而避免线程因锁被占用而阻塞。
用synchronized实现简单的加锁和解锁
执行结果
当然这只是一个实例,这种方式远没有lock接口直接和方便。
[size=large]ReentrantLock[/size]
ReentrantLock是Lock的实现接口,用法比较简单;synchronized和ReentrantLock都是可重入锁,可重入锁获取是基于线程,而不是基于方法调用的分配。如下:
当调用lock1时,锁被获取,再调用lock2时,由于具备可重入性,不会出现等待或者死锁问题。
ReentrantLock 是一种独占锁,具有可重入性,独占锁是一种互斥锁,即当前线程占用锁之后,其他线程只能等待锁释放之后才能继续占有。它分为公平锁和非公平锁,公平锁是通过AQS 类中一个CLH对列(FIFO)来保证实现公平性,非公平锁是直接得到一个可获取状态的锁,不管是不是在队列头还是队列中。
ReentrantLock 关键UML图:
[img]http://dl2.iteye.com/upload/attachment/0119/7182/08d93587-f46d-3c3f-b18b-b00cec688633.png[/img]
Lock:接口类,包括各种lock、unlock、ttyLock方法;
Sync:ReentrantLock中内部类,继承了AQS类;
NonfairSync:内部类,非公平锁实现,继承了Sync类;
FairSync:内部类,公平锁实现,继承Sync类。
[b]NonfairSync[/b] 类解读:
lock方法实现:
直接通过CAS方法获取锁,成功则设置线程获取,失败则通过acquire方法获取,acquire是AQS类中的方法,后续会讲解。1表示的是锁的状态state。对于独占锁而言,如果所处于可获取状态,其状态为0,当锁初次被线程获取时状态变成1。
而tryAcquire方法是直接调用父类Sync中的nonfairTryAcquire方法,具体实现了非公平锁的获取
非公平锁获取,先获取当前状态,如果为0表示处于可获取状态,通过CAS方法直接获取锁;如果当前线程就是锁的用户,则状态值增加。
[b]FairSync类:[/b]
lock方法:
调用AQS类中acquire方法,与非公平锁一致,tryAcquire是重写的AQS类中的tryAcquire方法。
[size=large]读写锁[/size]
ReentrantReadWriteLock就是对ReadWriteLock进行实现的,即读写互斥,准确的是说,一旦发现写锁,那么当前锁就会被独占,而如果只有读锁。这可以多个线程同时操作。
其实现和ReetrantLock差不多,使用示例:
执行结果:
可以看出,只有读锁时,可以多个线程共享,但是一点有写锁,那么就被独占了。
从读锁源码看到,读锁是采用的共享锁;而写锁是用的独占锁;
写锁:
1、synchronized是java提供的内置关键字,一旦使用线程就被锁住,直到线程执行完成或者处于wait状态下才会释放;如果线程处于阻塞了就会其他线程就会一直等待,如sleep;
2、Lock是一个接口,在JDK1.5提供,属于JUC下面常用的同步处理接口。在执行同步是都需要通过方法获取锁,执行完成之后必须释放锁;在获取锁时,通过tryLock方法可以判断锁是否被占用,从而避免线程因锁被占用而阻塞。
用synchronized实现简单的加锁和解锁
private boolean isLock = false;
private Thread currentThread = null;
public synchronized void lock() throws InterruptedException{
while (isLock) {
wait();
}
isLock = true;
currentThread = Thread.currentThread();
}
public synchronized void unlock() throws InterruptedException{
if (this.currentThread != Thread.currentThread()) {
throw new InterruptedException("当前线程没有加锁");
} else {
notify();
isLock = false;
currentThread = null;
}
}
public void myLockTest() throws InterruptedException{
lock();
System.out.println(Thread.currentThread().getName() + " 获得了锁 ");
try {
for (int i = 0; i < 10; i++) {
Thread.sleep(200);
System.out.println("执行第 "+i+"次");
}
} finally {
System.out.println(Thread.currentThread().getName() + " 释放了锁 ");
unlock();
}
}
public static void main(String[] args) {
final MyLock lock = new MyLock();
ExecutorService service = Executors.newFixedThreadPool(2);
service.submit(new Runnable() {
public void run() {
try {
lock.myLockTest();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
service.submit(new Runnable() {
public void run() {
try {
lock.myLockTest();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
service.shutdown();
}
执行结果
pool-1-thread-1 获得了锁
执行第 0次
执行第 1次
执行第 2次
pool-1-thread-1 释放了锁
pool-1-thread-2 获得了锁
执行第 0次
执行第 1次
执行第 2次
pool-1-thread-2 释放了锁
当然这只是一个实例,这种方式远没有lock接口直接和方便。
[size=large]ReentrantLock[/size]
ReentrantLock是Lock的实现接口,用法比较简单;synchronized和ReentrantLock都是可重入锁,可重入锁获取是基于线程,而不是基于方法调用的分配。如下:
public void lock1(){
locks.lock();
System.out.println(Thread.currentThread().getName() + " 获得了锁 ");
try {
lock2();
} finally {
locks.unlock();
System.out.println(Thread.currentThread().getName() + " 释放了锁 ");
}
}
public void lock2(){
locks.lock();
System.out.println(Thread.currentThread().getName() + " 获得了锁 ");
try {
for (int i = 0; i < 3; i++) {
System.out.println("执行第 "+i+"次");
}
} finally {
locks.unlock();
System.out.println(Thread.currentThread().getName() + " 释放了锁 ");
}
}
当调用lock1时,锁被获取,再调用lock2时,由于具备可重入性,不会出现等待或者死锁问题。
ReentrantLock 是一种独占锁,具有可重入性,独占锁是一种互斥锁,即当前线程占用锁之后,其他线程只能等待锁释放之后才能继续占有。它分为公平锁和非公平锁,公平锁是通过AQS 类中一个CLH对列(FIFO)来保证实现公平性,非公平锁是直接得到一个可获取状态的锁,不管是不是在队列头还是队列中。
ReentrantLock 关键UML图:
[img]http://dl2.iteye.com/upload/attachment/0119/7182/08d93587-f46d-3c3f-b18b-b00cec688633.png[/img]
Lock:接口类,包括各种lock、unlock、ttyLock方法;
Sync:ReentrantLock中内部类,继承了AQS类;
NonfairSync:内部类,非公平锁实现,继承了Sync类;
FairSync:内部类,公平锁实现,继承Sync类。
[b]NonfairSync[/b] 类解读:
lock方法实现:
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
直接通过CAS方法获取锁,成功则设置线程获取,失败则通过acquire方法获取,acquire是AQS类中的方法,后续会讲解。1表示的是锁的状态state。对于独占锁而言,如果所处于可获取状态,其状态为0,当锁初次被线程获取时状态变成1。
而tryAcquire方法是直接调用父类Sync中的nonfairTryAcquire方法,具体实现了非公平锁的获取
/**
* 非公平锁实现
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
非公平锁获取,先获取当前状态,如果为0表示处于可获取状态,通过CAS方法直接获取锁;如果当前线程就是锁的用户,则状态值增加。
[b]FairSync类:[/b]
lock方法:
final void lock() {
acquire(1);
}
调用AQS类中acquire方法,与非公平锁一致,tryAcquire是重写的AQS类中的tryAcquire方法。
[size=large]读写锁[/size]
ReentrantReadWriteLock就是对ReadWriteLock进行实现的,即读写互斥,准确的是说,一旦发现写锁,那么当前锁就会被独占,而如果只有读锁。这可以多个线程同时操作。
其实现和ReetrantLock差不多,使用示例:
private ReentrantReadWriteLock readLock = new ReentrantReadWriteLock();
public void read(){
readLock.readLock().lock();
if (!readLock.isWriteLocked()) {
System.out.println(Thread.currentThread().getName()+"当前为读锁----");
}
try {
System.out.println(Thread.currentThread().getName()+"开始读操作.");
for (int i=0; i<5; i++) {
System.out.println(Thread.currentThread().getName()+"进行读操作...");
}
System.out.println(Thread.currentThread().getName()+"读操作完毕.");
} finally {
System.out.println(Thread.currentThread().getName()+"释放读锁-----");
readLock.readLock().unlock();
}
}
public void write(){
readLock.writeLock().lock();
if (readLock.isWriteLocked()) {
System.out.println(Thread.currentThread().getName()+"当前为写锁----");
}
try {
System.out.println(Thread.currentThread().getName()+"开始写操作.");
for (int i=0; i<5; i++) {
System.out.println(Thread.currentThread().getName()+"进行写操作...");
}
System.out.println(Thread.currentThread().getName()+"写操作完毕.");
} finally {
System.out.println(Thread.currentThread().getName()+"释放写锁-----");
readLock.writeLock().unlock();
}
}
class ReadThread implements Runnable{
@Override
public void run() {
read();
}
}
class writeThread implements Runnable{
@Override
public void run() {
write();
}
}
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
LockTest lockTest = new LockTest();
service.submit(lockTest.new ReadThread());
service.submit(lockTest.new ReadThread());
service.submit(lockTest.new writeThread());
service.submit(lockTest.new writeThread());
service.shutdown();
}
执行结果:
pool-1-thread-1当前为读锁----
pool-1-thread-1开始读操作.
pool-1-thread-1进行读操作...
pool-1-thread-2当前为读锁----
pool-1-thread-1进行读操作...
pool-1-thread-2开始读操作.
pool-1-thread-1进行读操作...
pool-1-thread-2进行读操作...
pool-1-thread-1进行读操作...
pool-1-thread-2进行读操作...
pool-1-thread-2进行读操作...
pool-1-thread-1进行读操作...
pool-1-thread-2进行读操作...
pool-1-thread-1读操作完毕.
pool-1-thread-2进行读操作...
pool-1-thread-1释放读锁-----
pool-1-thread-2读操作完毕.
pool-1-thread-2释放读锁-----
pool-1-thread-2当前为写锁----
pool-1-thread-2开始写操作.
pool-1-thread-2进行写操作...
pool-1-thread-2进行写操作...
pool-1-thread-2进行写操作...
pool-1-thread-2进行写操作...
pool-1-thread-2进行写操作...
pool-1-thread-2写操作完毕.
pool-1-thread-2释放写锁-----
pool-1-thread-1当前为写锁----
pool-1-thread-1开始写操作.
pool-1-thread-1进行写操作...
pool-1-thread-1进行写操作...
pool-1-thread-1进行写操作...
pool-1-thread-1进行写操作...
pool-1-thread-1进行写操作...
pool-1-thread-1写操作完毕.
pool-1-thread-1释放写锁-----
可以看出,只有读锁时,可以多个线程共享,但是一点有写锁,那么就被独占了。
从读锁源码看到,读锁是采用的共享锁;而写锁是用的独占锁;
public static class ReadLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = -5992448646407690164L;
private final Sync sync;
/**
*/
protected ReadLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
/**
* 共享锁
*/
public void lock() {
sync.acquireShared(1);
}
//略
}
写锁:
public static class WriteLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = -4992448646407690164L;
private final Sync sync;
/**
*/
protected WriteLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
/**
* 独占锁..
*/
public void lock() {
sync.acquire(1);
}
//略
}