java 多线程 ReentrantLock互斥锁

互斥锁:

在上节中我们谈到了Object类的notify()方法随机唤醒单个线程,而不是唤醒指定线程,这就会导致一个问题,比如三个线程A,B,C,A在执行线程体,B,C在等待,A执行完该B执行了,notify方法随机唤醒一个线程,显然不能用,notifyAll方法把两个线程都唤醒,然后C再次设置成等待,这不太好.

为了解决不能唤醒指定线程的问题,jdk5.0的增加了ReenTrantLock类和Condition接口替换synchronized关键字和wait、notify方法。

先看ReenTrantLock类,暂时不深入分析

ReenTrantLock类:在java.util.concurrent.locks包下面,java.util.concurrent包中是常用的并发控制类

构造方法:

public ReentrantLock(),创建非公平锁对象,随机竞争来得到锁,先lock的线程不一定先获得锁,默认的构造方法。

public ReentrantLock(boolean fair)  ,创建公平锁对象,按线程加锁的顺序来获取锁

构造方法的源码如下(jdk11):

public ReentrantLock() {
        sync = new NonfairSync();
    }

public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
   }
//NonfairSync和NonfairSyn两个类是ReenTrantLock的内部类

列举了几个常用方法:

 void lock()如果锁可以, 对线程上锁., 如果锁已被其他线程占有, 暂时禁用当前线程, 直到当前线程获取到锁,再对线程上锁.

unvoid unlock()对线程解锁,持有锁的线程才能释放锁

boolean tryLock(): 此方法是尝试获取锁,如果锁可用, 则对该线程上锁返回true, 否则返回false. 锁被其他线程占有时, 不会禁用当前线程, 当前线程继续往下执行代码.

Condition newCondition()返回一个Condition实例

getHoldCount() 查询当前线程保持住此锁的次数,当前线程执行(lock和unlock)的次数

getQueueLength()返回正等待获取此锁的线程预估计最大数,比如启动10个线程,1个线程获得锁,此时返回的是9

hasQueuedThread(Thread thread)返回给定的线程是否在等待获取此锁

hasQueuedThreads()是否有线程等待此锁

isFair()判断该锁是否为公平锁

isLock()此锁是否被别的线程获取到

Condition接口:在java.util.concurrent.locks包下

Condition是监视器接口,Condition对象是由lock对象创建,同一个锁Lock对象可创建多个Condition的对象,即创建多个对象监视器,每个线程一个监视器。await方法和signal方法用来让指定线程等待和唤醒指定线程.跟wait/notify相同的是,await/signal也是在同步代码区内执行.

常用方法:

void await() throws InterruptedException让指定线程等待

void signal()唤醒指定的正在等待的线程

void signalAll()唤醒所有正在等待的线程

示例:两个线程轮流打印0-100

package chen_chapter_9;

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

public class ReenTrantLockTest01 {
	static PrintNum pn = null;// 内部类调用静态成员变量

	public static void main(String[] args) {
		pn = new PrintNum();
		Thread t1 = new Thread("线程t1") {
			@Override
			public void run() {
				pn.print1();
			}
		};
		Thread t2 = new Thread("线程t2") {
			@Override
			public void run() {
				pn.print2();
			}
		};
		t1.start();
		t2.start();
	}

}

class PrintNum {
	private ReentrantLock r1 = new ReentrantLock();//创建互斥锁对象
	private Condition c1 = r1.newCondition();//创建多个线程的监视器对象
	private Condition c2 = r1.newCondition();
	private int i = 0;
	private boolean flag = true;

	public void print1() {
		while (i < 100) {
			r1.lock();//lock()和unlock()之间的内容类似于以前的synchronized同步代码块
			if (!flag) {
				flag = !flag;
				try {
					c1.await();//指定这个线程等待
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} else {
				flag = !flag;
				System.out.println(Thread.currentThread().getName() + " : " + i++);
				c2.signal();//唤醒另一个线程
			}
			r1.unlock();
		}
	}

	public void print2() {
		while (i < 100) {
			r1.lock();
			if (!flag) {
				flag = !flag;
				try {
					c2.await();//指定这个线程等待
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} else {
				flag = !flag;
				System.out.println(Thread.currentThread().getName() + " : " + i++);
				c1.signal();//唤醒另一个线程
			}
			r1.unlock();

		}

	}

}

示例:三个线程轮流打印0-100

package chen_chapter_9;

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

public class ReenTrantLockTest2 {
	static PrintNum1 pn = null;// 内部类调用静态成员变量

	public static void main(String[] args) {
		pn = new PrintNum1();
		Thread t1 = new Thread("线程t1") {
			@Override
			public void run() {
				pn.print1();
			}
		};
		Thread t2 = new Thread("线程t2") {
			@Override
			public void run() {
				pn.print2();
			}
		};
		Thread t3 = new Thread("线程t3") {
			@Override
			public void run() {
				pn.print3();
			}
		};
		t1.start();
		t2.start();
		t3.start();
	}

}

class PrintNum1 {
	private ReentrantLock r1 = new ReentrantLock();// 创建互斥锁对象
	private Condition c1 = r1.newCondition();// 创建多个线程的监视器对象
	private Condition c2 = r1.newCondition();
	private Condition c3 = r1.newCondition();
	private int i = 0;
	private int flag = 1;

	public void print1() {
		while (i < 100) {
			r1.lock();// lock()和unlock()之间的内容类似于以前的synchronized同步代码块
			if (flag != 1) { // 三个线程中,保证对同一个变量,只有一个不进入等待状态
				try {
					c1.await();// 指定这个线程等待
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} else {
				System.out.println(Thread.currentThread().getName() + " : " + i++);
				flag = 2;
				c2.signal();// 唤醒另一个线程
			}
			r1.unlock();
		}
	}

	public void print2() {
		while (i < 100) {
			r1.lock();
			if (flag != 2) {
				try {
					c2.await();// 指定这个线程等待
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} else {

				System.out.println(Thread.currentThread().getName() + " : " + i++);
				flag = 3;
				c3.signal();// 唤醒另一个线程
			}
			r1.unlock();

		}

	}

	public void print3() {
		while (i < 100) {
			r1.lock();
			if (flag != 3) {
				try {
					c3.await();// 指定这个线程等待
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} else {

				System.out.println(Thread.currentThread().getName() + " : " + i++);
				flag = 1;
				c1.signal();// 唤醒另一个线程
			}
			r1.unlock();

		}

	}

}

synchronized和ReenTrantLock的区别:

synchronized

ReenTrantLock

猜你喜欢

转载自blog.csdn.net/sinat_41132860/article/details/84554685