之前学习了通过synchronized关键字实现线程同步效果。除此之外,通过Lock对象来实现同步效果。
1- ReentrantLock类的使用
2- ReentrantReadWriteLock类的使用
运行类:
package ReentrantLock_part1;
public class Test {
public static void main(String[] args) {
Service1 service1 = new Service1();
Thread1 thread1 = new Thread1(service1);
Thread1 thread2 = new Thread1(service1);
Thread1 thread3 = new Thread1(service1);
thread1.start();
thread2.start();
thread3.start();
}
}
线程类:
package ReentrantLock_part1;
public class Thread1 extends Thread{
private Service1 service1;
public Thread1(Service1 service1) {
super();
this.service1 = service1;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
service1.task();
}
}
业务类:
package ReentrantLock_part1;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Service1 {
private Lock lock = new ReentrantLock();
public void task(){
//lock()方法获得锁
lock.lock();
for(int i=0;i<5;i++){
System.out.println("当前线程为: "+Thread.currentThread().getName());
}
//unlock()方法释放锁
lock.unlock();
}
}
运行结果:
当前线程为: Thread-0
当前线程为: Thread-0
当前线程为: Thread-0
当前线程为: Thread-0
当前线程为: Thread-0
当前线程为: Thread-1
当前线程为: Thread-1
当前线程为: Thread-1
当前线程为: Thread-1
当前线程为: Thread-1
当前线程为: Thread-2
当前线程为: Thread-2
当前线程为: Thread-2
当前线程为: Thread-2
当前线程为: Thread-2
结论为:该ReentrantLock类的lock()和unlock()方法确实可以实现线程的同步效果。
synchronized使用wait / nitify实现。而在ReentrantLock类中,使用Condition对象实现。
不同点:wait / nitify的唤醒是随机唤醒的。Condition对象可实现选择性唤醒。
测试工程代码如下:
运行类:
package ReentrantLock_part1;
public class Test {
public static void main(String[] args) throws InterruptedException {
Service1 service1 = new Service1();
Thread1 thread1 = new Thread1(service1);
thread1.start();
Thread.sleep(50);
Thread2 thread2 = new Thread2(service1);
thread2.start();
}
}
await线程类:
package ReentrantLock_part1;
public class Thread1 extends Thread{
private Service1 service1;
public Thread1(Service1 service1) {
super();
this.service1 = service1;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
service1.await();
}
}
signla线程类:
package ReentrantLock_part1;
public class Thread2 extends Thread{
private Service1 service1;
public Thread2(Service1 service1) {
super();
this.service1 = service1;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
service1.signal();
}
}
业务类:
package ReentrantLock_part1;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Service1 {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void await(){
try {
//lock()方法获得锁
lock.lock();
System.out.println("await时间为: "+System.currentTimeMillis()/1000);
Thread.sleep(2000);
condition.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//unlock()方法释放锁
lock.unlock();
}
public void signal(){
lock.lock();
System.out.println("signal时间为 : "+System.currentTimeMillis()/1000);
condition.signal();
lock.unlock();
}
}
运行结果如下:
await时间为: 1526543282
signal时间为 : 1526543284
结论:可以实现线程的等待/唤醒功能。
扩展:之前说到ReentrantLock类的Condition对象可以选择性唤醒线程,实现方式是在业务类中创建多各Condition对象,不同线程使用同样的ReentrantLock对象所以可以实现线程同步效果,而不同的Condition对象意味着各个线程拥有不同的“对象监视器”,所以可以通过指定对象监视器的signal()方法或者signalAll()方法唤醒受到该对象监视的所有线程,而拥有其他对象监视器的线程不被唤醒。由此实现了线程的选择性唤醒。
工程代码如下:
运行类:
package ReentrantLock_part1;
public class Test {
public static void main(String[] args) throws InterruptedException {
Service1 service1 = new Service1();
Thread1 thread1 = new Thread1(service1);
thread1.start();
Thread2 thread2 = new Thread2(service1);
thread2.start();
}
}
生产者线程:
package ReentrantLock_part1;
public class Thread1 extends Thread{
private Service1 service1;
public Thread1(Service1 service1) {
super();
this.service1 = service1;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
while(1==1){
service1.produce();
}
}
}
消费者线程:
package ReentrantLock_part1;
public class Thread2 extends Thread{
private Service1 service1;
public Thread2(Service1 service1) {
super();
this.service1 = service1;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
while(1==1){
service1.custom();
}
}
}
业务类:
package ReentrantLock_part1;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Service1 {
volatile private boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void produce(){
try {
lock.lock();
while(flag == false){
condition.await();
}
flag = false;
System.out.println("****");
condition.signal();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lock.unlock();
}
public void custom(){
try {
lock.lock();
while(flag == true){
condition.await();
}
flag = true;
System.out.println("====");
condition.signal();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lock.unlock();
}
}
部分运行结果:
====
****
====
****
====
****
====
****
====
同synchronized下的多对多。使用signalAll()方法避免假死,使用volatile关键字达到可见性目的(确保boolean量及时更新到各个线程副本中)。
工程代码如下:
运行类:
package ReentrantLock_part1;
public class Test {
public static void main(String[] args) throws InterruptedException {
Service1 service1 = new Service1();
Thread1[] thread1s = new Thread1[10];
Thread2[] thread2s = new Thread2[10];
for(int i=0;i<10;i++){
thread1s[i] = new Thread1(service1);
thread1s[i].start();
thread2s[i] = new Thread2(service1);
thread2s[i].start();
}
}
}
生产者线程:
package ReentrantLock_part1;
public class Thread1 extends Thread{
private Service1 service1;
public Thread1(Service1 service1) {
super();
this.service1 = service1;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
while(1==1){
service1.produce();
}
}
}
消费者线程类:
package ReentrantLock_part1;
public class Thread2 extends Thread{
private Service1 service1;
public Thread2(Service1 service1) {
super();
this.service1 = service1;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
while(1==1){
service1.custom();
}
}
}
业务层类:
package ReentrantLock_part1;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Service1 {
volatile private boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void produce(){
try {
lock.lock();
while(flag == false){
condition.await();
}
flag = false;
System.out.println("****");
condition.signalAll();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lock.unlock();
}
public void custom(){
try {
lock.lock();
while(flag == true){
condition.await();
}
flag = true;
System.out.println("====");
condition.signalAll();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lock.unlock();
}
}
部分运行结果如下:
====
****
====
****
====
****
====
****
====
公平锁:多线程获取锁的顺序是按照线程加锁的顺序来分配的。
非公平锁:都是随机,可能造成某些线程一直无法运行。
如何实现:
在业务类中,生成ReentrantLock对象时,通过其参数boolean值决定是公平锁还是非公平锁。
公平锁:
Lock lock = new ReentrantLock(true);
非公平锁:
Lock lock = new ReentrantLock(false);
查询当前以lock对象为对象监视器的线程数量。
使用方法:int num = lock.getHoldCount();
查询当前等待lock锁权限的线程数(排除await状态的)
使用方法:int length = lock.getQueueLength();
查询当前lock监视器下处于await状态的线程数量
使用方法:int length = lock.getWaitQueueLength(Condition condition)
查询指定线程是否处于等待锁的状态
使用方法:boolean isWait = lock.hasQueueThread(Thread thread)
查询是否存在线程等待当前锁对象
使用方法:boolean isWait = lock.hasQueueThreads()
查询是否有线程等待当前锁的condition对象
使用方法:boolean hasWait = lock.hasWaiters(Condition condition)
判断当前lock对象是否为公平锁
使用方法:boolean isfair = lock.isFair();
判断当前线程是否以该锁对象为对象监视器。
使用方法:boolean isHeld = lock.isHeldByCurrentThread();
查询该锁对象是否处于锁定状态
使用方法:boolean isLocked = lock.isLocked();
如果当前线程未被中断,则获取此锁定,若已经中断则抛出异常。
当前锁未被线程锁定时,才可以被其他线程锁定。
给定int+时间单位的参数,即在该时间内,该锁未被其他线程锁定,则可以被当前线程锁定。
多个读锁之间不互斥,读锁和写锁之间互斥,写锁与写锁之间也互斥
及,多个读操作可异步执行,读写操作须同步执行,写写操作也须同步执行。
工程代码如下
运行类
package ReadLock;
public class Test {
public static void main(String[] args) {
Service1 service1 = new Service1();
Thread1 thread1 = new Thread1(service1);
Thread2 thread2 = new Thread2(service1);
thread1.setName("AAA");
thread2.setName("BBB");
thread1.start();
thread2.start();
}
}
线程类1
package ReadLock;
public class Thread1 extends Thread{
private Service1 service1;
public Thread1(Service1 service1) {
super();
this.service1 = service1;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
service1.doReads();
}
}
线程类2
package ReadLock;
public class Thread2 extends Thread{
private Service1 service1;
public Thread2(Service1 service1) {
super();
this.service1 = service1;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
service1.doReads();
}
}
逻辑类
package ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Service1 {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void doReads(){
try {
System.out.println(Thread.currentThread().getName()+" start read at "+System.currentTimeMillis()/1000);
lock.readLock().lock();
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+" stop read at "+System.currentTimeMillis()/1000);
} catch (Exception e) {
// TODO: handle exception
}
lock.readLock().unlock();
}
}
运行结果
BBB start read at 1526609034
AAA start read at 1526609034
AAA stop read at 1526609036
BBB stop read at 1526609036
如上,开启两个线程同时去争抢锁,发现当锁为读锁时,两线程运行逻辑的时间时几乎一致的,即两者时异步执行的。提高了运行的效率。
工程代码如下
运行类
package ReadWriteLock;
public class Test {
public static void main(String[] args) {
Service1 service1 = new Service1();
Thread1 thread1 = new Thread1(service1);
Thread2 thread2 = new Thread2(service1);
thread1.setName("AAA");
thread2.setName("BBB");
thread1.start();
thread2.start();
}
}
读线程
package ReadWriteLock;
public class Thread1 extends Thread{
private Service1 service1;
public Thread1(Service1 service1) {
super();
this.service1 = service1;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
service1.doReads();
}
}
写线程
package ReadWriteLock;
public class Thread2 extends Thread{
private Service1 service1;
public Thread2(Service1 service1) {
super();
this.service1 = service1;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
service1.doWrites();
}
}
逻辑代码
package ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Service1 {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void doReads(){
try {
System.out.println(Thread.currentThread().getName()+" start read at "+System.currentTimeMillis()/1000);
lock.readLock().lock();
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+" stop read at "+System.currentTimeMillis()/1000);
} catch (Exception e) {
// TODO: handle exception
}
lock.readLock().unlock();
}
public void doWrites(){
try {
System.out.println(Thread.currentThread().getName()+" start write at "+System.currentTimeMillis()/1000);
lock.writeLock().lock();
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+" stop write at "+System.currentTimeMillis()/1000);
} catch (Exception e) {
// TODO: handle exception
}
lock.writeLock().unlock();
}
}
运行结果
AAA start read at 1526609551
BBB start write at 1526609551
AAA stop read at 1526609553
BBB stop write at 1526609555
从结果看,读线程和写线程时同步的,说明读写是互斥的。
工程代码如下
运行类
package WriteLock;
public class Test {
public static void main(String[] args) {
Service1 service1 = new Service1();
Thread1 thread1 = new Thread1(service1);
Thread2 thread2 = new Thread2(service1);
thread1.setName("AAA");
thread2.setName("BBB");
thread1.start();
thread2.start();
}
}
线程类1
package WriteLock;
public class Thread1 extends Thread{
private Service1 service1;
public Thread1(Service1 service1) {
super();
this.service1 = service1;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
service1.doWrites();
}
}
线程类2
package WriteLock;
public class Thread2 extends Thread{
private Service1 service1;
public Thread2(Service1 service1) {
super();
this.service1 = service1;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
service1.doWrites();
}
}
逻辑代码
package WriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Service1 {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void doWrites(){
try {
System.out.println(Thread.currentThread().getName()+" start write at "+System.currentTimeMillis()/1000);
lock.writeLock().lock();
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+" stop write at "+System.currentTimeMillis()/1000);
} catch (Exception e) {
// TODO: handle exception
}
lock.writeLock().unlock();
}
}
运行结果
AAA start write at 1526609743
BBB start write at 1526609743
AAA stop write at 1526609745
BBB stop write at 1526609747
由结果看,写写操作也是互斥的。
Lock可视为synchronized关键字的进阶。将其替换出来,且具有synchronized关键字不具备的功能,包括Condition对象带来的等待/唤醒时的选择性唤醒,以及读写锁使用以提升操作效率等。