Java并发程序设计(四)JDK并发包之同步控制

时间:2021-05-05 23:48:42

JDK并发包之同步控制

一、重入锁

重入锁使用java.util.concurrent.locks.ReentrantLock来实现。示例代码如下:

public class TryReentrantLock implements Runnable{

static ReentrantLock lock=new ReentrantLock();
static int i=0;
@Override
public void run() {
for(int j=0;j<10000;j++){
lock.lock();
try{
i
++;
}
finally {
lock.unlock();
}

//重入锁可以反复进入,当然这种进入仅限于一个线程!因此可以如下:
/*lock.lock();
lock.lock();
try{
i++;
}finally {
lock.unlock();
lock.unlock();
}
*/
}
}

public static void main(String[] args) throws InterruptedException {
TryReentrantLock l
=new TryReentrantLock();
Thread t1
=new Thread(l);
Thread t2
=new Thread(l);
t1.start(); t2.start();
t1.join(); t2.join();
System.out.println(i);
}
}

对于synchronized来说,如果一个线程在等待锁,那么结果只有两种情况,要么它获得这把锁继续执行,要么继续等待锁。而对于使用重入锁的线程来说则不同,线程在等待锁的过程中,程序可以根据需要取消对锁的请求。

public class IntLock implements Runnable{
public static ReentrantLock lock1=new ReentrantLock();
public static ReentrantLock lock2=new ReentrantLock();
int state;

/*控制加锁顺序,便于产生死锁。*/
public IntLock(int state){
this.state=state;
}
@Override
public void run() {
try{
if(state==1){
lock1.lockInterruptibly();
//设置可中断的锁
try{
Thread.sleep(
500); //便于产生死锁
}catch (InterruptedException e){
e.printStackTrace();
}
lock2.lockInterruptibly();
}
else{
lock2.lockInterruptibly();
try{
Thread.sleep(
500);
}
catch (InterruptedException e){
e.printStackTrace();
}
lock1.lockInterruptibly();
}
}
catch (Exception e){
e.printStackTrace();
}
finally {
if(lock1.isHeldByCurrentThread()) lock1.unlock();
if(lock2.isHeldByCurrentThread()) lock2.unlock();
System.out.println(Thread.currentThread().getName()
+" :quit!");
}
}

public static void main(String[] args) throws InterruptedException {
IntLock intLock1
=new IntLock(1);
IntLock intLock2
=new IntLock(2);
Thread t1
=new Thread(intLock1);
t1.setName(
"t1");
Thread t2
=new Thread(intLock2);
t2.setName(
"t2");
t1.start(); t2.start();
Thread.sleep(
1000);
t2.interrupt();
}
}

使用tryLock()进行限时等待。

public class TimeLock implements Runnable{
public static ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
try{
if(lock.tryLock(5, TimeUnit.SECONDS)){
//注意返回布尔值.如果使用无参数的tryLock方法,
// 当锁被占用时,线程会不等待,并返回false。
Thread.sleep(6000);
}
else{
System.out.println(
"Get lock failed!");
}
}
catch (Exception e){
e.printStackTrace();
}
finally {
if (lock.isHeldByCurrentThread()) lock.unlock();
}
}

public static void main(String[] args){
TimeLock timeLock
=new TimeLock();
Thread t1
=new Thread(timeLock);
Thread t2
=new Thread(timeLock);
t1.start();
t2.start();

}
}

 

公平锁:

大多数情况下,锁的申请都是不公平,随机的。而公平锁,讲究“先来后到”!因此公平锁的一大特点是不会产生饥饿。重入锁允许我们对其公平性进行设置:

public ReentrantLock(boolean fair); //true表示公平锁

公平锁缺点:性能相对低下。

 

二、重入锁的好基友Condition

Condition的做用类似于wait()和notify()只不过Condition和ReentrantLock配合。

Condition接口的基本方法:

void await() throws InterruptedException //释放当前线程持有的锁,进入等待状态,当其他线程使用signal或signalAll()方法时继续执行。当线程被中断时会跳出等待。

void awaitUninterruptibly();与await()方法相似,当中断时不会跳出等待。

void signal();
void signalAll();

用法示例:

public class ReentrantCondition implements Runnable{
static ReentrantLock lock=new ReentrantLock();
static Condition condition=lock.newCondition();

@Override
public void run() {
try{
lock.lock();
System.out.println(Thread.currentThread().getName()
+" is running fast!");
condition.await();
System.out.println(Thread.currentThread().getName()
+" is going no.");
}
catch (InterruptedException e){
e.printStackTrace();
}
finally {
lock.unlock();
}
}

public static void main(String[] args) throws InterruptedException {
ReentrantCondition rc
=new ReentrantCondition();
Thread t
=new Thread(rc,"King of Lock");
t.start();

Thread.sleep(
2000);
lock.lock();
//调用signal前先要获得锁
condition.signal();
lock.unlock();
//调用singal后要释放锁,以让给被唤醒的线程。
}
}

 

三、允许多个线程同时访问:信号量(Semaphore)

Semaphore可以指定多个线程同时访问一个资源。

public Semaphore(int permits); //permits指定同时能有几个线程访问同一资源。
public Semaphore(int permits,boolean fair) //fair指定是否采用公平锁

Semaphore主要的逻辑方法有:

public void acquire()  //尝试获得一个准入许可,若无法获得,则线程会等待,直到有线程释放了一个许可,或者当前线程被中断
public void acquireUninterruptibly() //不响应中断
public void release() //释放一个许可

 

四、ReadWriteLock读写锁

读写锁允许多个线程同时读。

Java并发程序设计(四)JDK并发包之同步控制

 

五、CountDownLatch倒计时器

通常用来控制线程等待,可以让某个线程等待直到倒计时结束,再开始执行。

public CountDownLatch(int count) //count为需要完成几个线程上的任务,CountDownLatch上的线程才能继续执行。

 

 示例代码:

public class CountDownLatchDemo implements Runnable{
static final CountDownLatch latch=new CountDownLatch(10); //需要完成10个线程的任务,
// CountDownLatch上的线程才能继续执行
static final CountDownLatchDemo demo=new CountDownLatchDemo();

@Override
public void run() {
try{
Thread.sleep(
1000);
System.out.println(Thread.currentThread().getName()
+" :mission completed!");
latch.countDown();
//通知CountDownLatch一个任务已经完成。
}catch (Exception e){
e.printStackTrace();
}
}

public static void main(String[] args) throws InterruptedException {
ExecutorService ex
= Executors.newFixedThreadPool(10);
for(int i=0;i<10;i++){
ex.submit(demo);
}

latch.await();
//设置要等待其他线程完成的那个线程
System.out.println("Killed them all!");
ex.shutdown();
}
}

 

六、循环栅栏:CyclicBarrier

与CountDownLatch类似CyclicBarrier也是用来阻止线程继续执行,让线程进入等待状态。但此计数器可以反复使用。

public CyclicBarrier(int parties,Runnable barrierAction) //barrierAction为计数完成后要执行的动作

示例代码:

public class CyclicBarrierDemo {

public static class Soldier implements Runnable{

private String name;
private final CyclicBarrier barrier;

Soldier(String name,CyclicBarrier barrier){
this.name=name;
this.barrier=barrier;
}

@Override
public void run() {
try{
barrier.await();
//进入等待状态,直到所有线程完成计数。
doWork(); //所有线程完成一次await,继续执行。
barrier.await(); //第二次await再次进入等待状态。
}catch (Exception e){

}
}

void doWork(){
try{
Thread.sleep(Math.abs(
new Random().nextInt()%10000));
}
catch (Exception e){
e.printStackTrace();
}
System.out.println(
"任务完成!");
}
}

public static class FlyRun implements Runnable{
boolean flag;
int count;

public FlyRun(boolean flag,int count){
this.flag=flag;
this.count=count;
}


@Override
public void run() {
if (flag) System.out.println("队长:"+count+" 个士兵完成任务!");
else {
System.out.println(
"队长:"+count+" 个士兵集合完毕!");
flag
=true;
}
}
}

public static void main(String[] args){
final int count=12;
Thread[] soldiers
=new Thread[count];
boolean flag=false;
CyclicBarrier barrier
=new CyclicBarrier(count,new FlyRun(flag,count));
//集合队伍
System.out.println("集合队伍!");
for(int j=0;j<count;j++){
System.out.println(
"士兵"+j+"报道!");
soldiers[j]
=new Thread(new Soldier("士兵"+j,barrier));
soldiers[j].start();
}
}
}

 

七、线程阻塞工具类:LockSupport

LockSupport可以在线程内任意位置让线程阻塞。但与Object.wait()不同,它不需要先获得某个对象的锁,也不会抛出InterruptedException异常。示例代码:

public class LockSupportDemo {

public static Object instance=new Object();
public static ChangThread t1=new ChangThread("t1");
public static ChangThread t2=new ChangThread("t2");

public static class ChangThread extends Thread{
public ChangThread(String name){
super.setName(name);
}

@Override
public void run(){
synchronized(instance){
System.out.println(
"in"+getName());
LockSupport.park();
}
}

}

public static void main(String[] args) throws InterruptedException {
t1.start();
Thread.sleep(
100);
t2.start();
LockSupport.unpark(t1);
//即使unpark()发生在park前,程序也能正常执行。
LockSupport.unpark(t2);
t1.join();
t2.join();
}
}

park还支持中断影响,且不抛出异常。