Java高并发程序设计笔记5之JDK同步控制

时间:2022-02-28 05:41:58

ReentrantLock

    ReentrantLock是JDK5加入的新类,它的用法和synchronized类似,不过它需要程序员手动添加加锁和解锁的代码。它的特性如下:

     可重入:单线程可以重复进入,但要重复退出
     可中断:lockInterruptibly()
     可限时:超时不能获得锁,就返回false,不会永久等待构成死锁
     公平锁:先来先得
     public ReentrantLock(boolean fair)
   public static ReentrantLock fairLock =new ReentrantLock(true)

比起synchronized的好处。它添加了两个方法:

1 提供了tryLock(),该方法调用的时候。如果锁被另一个对象持有,它会返回false。

2 还一个就是公平锁。在构造ReentrantLock的时候。它拥有一个人boolean类型的参数。该参数就是描述使用使用公平锁。公平锁的好吃就是等待的线程不会被饿死,但是效率还低些。公平锁严格按照锁的顺序来获取锁。非公平锁是可以被抢占的。但是它会导入有些需要锁的线程等死。

下面是ReentrantLock的实列代码:

package com.cosmistar.jk;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
* Created by Administrator on 2016/11/20.
*/
public class ReentrantLockTest {
ReentrantLock lock = new ReentrantLock();
public void reentrantLockInfo(){
System.out.println("查看锁的信息:" + lock.tryLock());
if(lock.tryLock()) {
System.out.println("方法被锁");
try {
System.out.println("方法执行");
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
lock.unlock();
System.out.println("锁被释放");
}
}
else {
System.out.println("没有被执行");
}
}

public void test1(){
Thread thread = new Thread(){
public void run() {
reentrantLockInfo();
}
};
thread.start();
}

public static void main(String[] args) {
ReentrantLockTest test1 = new ReentrantLockTest();
test1.test1();
test1.test1();

}
}

    Condition

类似于Object.wait()和Object.notify(),与ReentrantLock结合使用
主要接口如下:
void await() throws InterruptedException; 
void awaitUninterruptibly(); 
long awaitNanos(long nanosTimeout) throws InterruptedException; 
boolean await(long time, TimeUnit unit) throws InterruptedException;
boolean awaitUntil(Date deadline) throws InterruptedException; 
void signal(); void signalAll();

await()方法会使当前线程等待,同时释放当前锁,当其他线程中使用signal()时或者signalAll()方法时,线程会重新获得锁并继续执行。或者当线程被中断时,也能跳出等待。这和Object.wait()方法很相似。awaitUninterruptibly()方法与await()方法基本相同,但是它并不会再等待过程中响应中断。singal()方法用于唤醒一个在等待中的线程。相对的singalAll()方法会唤醒所有在等待中的线程。这和Obejct.notify()方法很类似。

举个例子来说明:

package com.cosmistar.jk;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
* Created by Administrator on 2016/11/20.
*/
public class ConditionTest implements Runnable {
public static ReentrantLock lock = new ReentrantLock();
public static Condition condition = lock.newCondition();

@Override
public void run()
{
try
{
lock.lock();
condition.await();
System.out.println("Thread is going on");
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
lock.unlock();
}
}

public static void main(String[] args) throws InterruptedException
{
ConditionTest t = new ConditionTest();
Thread thread = new Thread(t);
thread.start();
Thread.sleep(2000);

lock.lock();
condition.signal();
lock.unlock();
}
}
上述例子很简单,让一个线程await住,让主线程去唤醒它。condition.await()/signal只能在得到锁以后使用

Semaphore

     共享锁,运行多个线程同时临界区,对于锁来说,它是互斥的排他的。意思就是,只要我获得了锁,没人能再获得了。
而对于Semaphore来说,它允许多个线程同时进入临界区。可以认为它是一个共享锁,但是共享的额度是有限制的,额度用完了,其他没有拿到额度的线程还是要阻塞在临界区外。当额度为1时,就相等于lock
主要接口如下:
public void acquire() 
public void acquireUninterruptibly() 
public boolean tryAcquire() 
public boolean tryAcquire(long timeout, TimeUnit unit)
public void release()
下面举个例子:

package com.cosmistar.jk;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
* Created by Administrator on 2016/11/20.
*/
public class SemaphoreTest implements Runnable{
final Semaphore semaphore = new Semaphore(6);
public void run() {
try{
semaphore.acquire();
Thread.sleep(2000);
System.out.println(Thread.currentThread().getId() + " done");
}catch (Exception e) {
e.printStackTrace();
}finally {
semaphore.release();
}
}

public static void main(String[] args) throws InterruptedException
{
ExecutorService executorService = Executors.newFixedThreadPool(20);
final SemaphoreTest t = new SemaphoreTest();
for (int i = 0; i < 20; i++)
{
executorService.submit(t);
}
}
}
有一个20个线程的线程池,每个线程都去 Semaphore的许可,Semaphore的许可只有5个,运行后可以看到,5个一批,一批一批地输出。当然一个线程也可以一次申请多个许可
public void acquire(int permits) throws InterruptedException

ReadWriteLock

这是JDK5中提供的读写分离锁,ReadWriteLock是区分功能的锁。读和写是两种不同的功能,读-读不互斥,读-写互斥,写-写互斥。这样的设计是并发量提高了,又保证了数据安全。如果说ReetrantLock是一种互斥锁的话,那么还有一种锁叫读写锁ReadWriteLock。通俗点说,互斥锁相当一个线程持有这个锁以后,其他任何线程都不能再获取这个锁了,但可以设想2个线程对一个公共内容读写的场景,如果一个线程对公共内容进行写操作,那么另一个线程这不能读也不能写这个内容,但如果一个线程对公共内容进行读操作,那实际应该允许另个一线程对该内容进行读操作,但禁止写操作,即读-读允许,读-写互斥,写-写互斥,ReadWriteLock就解决这个问题。

主要接口
private static ReentrantReadWriteLock readWriteLock=new ReentrantReadWriteLock();
private static Lock readLock = readWriteLock.readLock(); 
private static Lock writeLock = readWriteLock.writeLock();
详细例子可以查看 Java实现生产者消费者问题与读者写者问题
下面举个例子:

package com.cosmistar.jk;

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
* Created by Administrator on 2016/11/20.
*/
public class ReadWriteLockInfo {
static ReadWriteLock lock = new ReentrantReadWriteLock();

static class Read implements Runnable{

private String name;
public Read(String name){
this.name = name;
}
@Override
public void run() {
lock.readLock().lock();
try {
for (int i = 0; i < 5; i++) {
System.out.println(name + " read content " + i);
}
} finally {
lock.readLock().unlock();
}
}
}

static class Write implements Runnable{
private String name;
public Write(String name){
this.name = name;
}
@Override
public void run() {
lock.writeLock().lock();
try {
for (int i = 0; i < 5; i++) {
System.out.println(name + " write content " + i);
}
} finally {
lock.writeLock().unlock();
}
}
}

public static void main(String[] args) {
//这是这个2个读线程,所有结果是不会排斥的
new Thread(new Read("t1")).start();
new Thread(new Read("t2")).start();
//这是2个读或写线程,所有结果会排斥的
//new Thread(new Read("t1")).start();
//new Thread(new Write("t2")).start();
}
}