Java多线系列文章是Java多线程的详解介绍,对多线程还不熟悉的同学可以先去看一下我的这篇博客Java基础系列3:多线程超详细总结,这篇博客从宏观层面介绍了多线程的整体概况,接下来的几篇文章是对多线程的深入剖析。

Lock锁

1、简介

1、从Java5开始,Java提供了一种功能更强大的线程同步机制——通过显式定义同步锁对象来实现同步,在这种机制下,同步锁由Lock对象充当。

2、Lock 提供了比synchronized方法和synchronized代码块更广泛的锁定操作,Lock允许实现更灵活的结构,可以具有差别很大的属性,并且支持多个相关的Condition对象。

3、Lock是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。

4、某些锁可能允许对共享资源并发访问,如ReadWriteLock(读写锁),Lock、ReadWriteLock是Java5提供的两个根接口,并为Lock 提供了ReentrantLock(可重入锁)实现类,为ReadWriteLock提供了ReentrantReadWriteLock 实现类。

5、Java8新增了新型的StampedLock类,在大多数场景中它可以替代传统的ReentrantReadWriteLock。ReentrantReadWriteLock 为读写操作提供了三种锁模式:Writing、ReadingOptimistic、Reading。

2、Lock锁使用

class X{
//定义锁对象
private final ReentrantLock lock=new ReentrantLock(); //定义需要保证线程安全的方法
public void m() {
//加锁
lock.lock();
try {
//需要保证线程安全的代码
}
finally {
lock.unlock();
}
}
}

  

ReentranLock

1、简介

在Java多线程中,可以使用synchronized关键字来实现线程之间同步互斥,但在JDK1.5中新增加了ReentrantLock类也能达到同样的效果,并且在扩展功能上也更加强大,比如具有嗅探锁定、多路分支通知等功能,而且在使用上也比synchronized更加的灵活。

2、使用ReentranLock实现同步

既然ReentrantLock类在功能上相比synchronized更多,那么就以一个初步的程序示例来介绍一下ReentrantLock类的使用。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; class MyService{
private Lock lock=new ReentrantLock(); public void testMethod() {
lock.lock();
for(int i=0;i<5;i++) {
System.out.println("ThreadName= "+Thread.currentThread().getName()+(" "+(i+1)));
}
lock.unlock();
}
} class MyThread extends Thread{
private MyService service; public MyThread(MyService service) {
this.service=service;
} @Override
public void run() {
service.testMethod();
}
} public class LockTest { public static void main(String[] args) {
MyService service=new MyService();
MyThread t1=new MyThread(service);
MyThread t2=new MyThread(service);
MyThread t3=new MyThread(service);
MyThread t4=new MyThread(service);
MyThread t5=new MyThread(service);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start(); } }

  

运行结果:

ThreadName= Thread-2 1
ThreadName= Thread-2 2
ThreadName= Thread-2 3
ThreadName= Thread-2 4
ThreadName= Thread-2 5
ThreadName= Thread-0 1
ThreadName= Thread-0 2
ThreadName= Thread-0 3
ThreadName= Thread-0 4
ThreadName= Thread-0 5
ThreadName= Thread-3 1
ThreadName= Thread-3 2
ThreadName= Thread-3 3
ThreadName= Thread-3 4
ThreadName= Thread-3 5
ThreadName= Thread-4 1
ThreadName= Thread-4 2
ThreadName= Thread-4 3
ThreadName= Thread-4 4
ThreadName= Thread-4 5
ThreadName= Thread-1 1
ThreadName= Thread-1 2
ThreadName= Thread-1 3
ThreadName= Thread-1 4
ThreadName= Thread-1 5

  

从运行的结果来看,当前线程打印完毕之后将锁进行释放,其他线程才可以继续打印。线程打印的数据是分组打印,因为当前线程已经持有锁,但线程之间打印的顺序是随机的。lock.lock()是对当前线程加锁,当线程执行完毕后调用lock.unlock()释放锁,这时候其他线程可以去获取锁,至于是哪一个线程可以争抢到锁还是看CPU的调度

3、使用Condition实现等待/通知:错误用法与解决

关键字synchronized与wait()和notify()/notifyAll()方法相结合可以实现等待/通知模式,类ReentrantLock也可以实现同样的功能,但需要借助于Condition对象。Condition类是在JDK5中出现的技术,使用它有更好的灵活性,比如可以实现多路通知功能,也就是在一个Lock对象里面可以创建多个Condition(即对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以有选择性地进行线程通知,在调度线程上更加灵活。

在使用notify(O/notifyAll0方法进行通知时,被通知的线程却是由JVM随机选择的。但使用ReentrantLock结合Condition类是可以实现前面介绍过的“选择性通知”,这个功能是非常重要的,而且在Condition类中是默认提供的。

而synchronized就相当于整个Lock对象中只有一个单一的Condition对象,所有的线程都注册在它一个对象的身上。线程开始notifyAll()时,需要通知所有的WAITING线程,没有选择权,会出现相当大的效率问题。

package Thread05;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; class MyService{
private Lock lock=new ReentrantLock();
private Condition condition=lock.newCondition();
public void await() {
try {
lock.lock();
System.out.println("A");
condition.await();
System.out.println("B");
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
System.out.println("锁释放了");
}
}
} class MyThread extends Thread{
private MyService service; public MyThread(MyService service) {
this.service=service;
} @Override
public void run() {
service.await();
}
} public class LockTest { public static void main(String[] args) {
MyService service=new MyService();
MyThread thread=new MyThread(service);
thread.start(); } }

  

输出结果:

A

  

我们可以看到输出结果只有一个A,并没有其他的输出,这是因为调用Condition的await()方法,使当前执行任务的线程进入了等待的状态

注意:在使用Condition方法时要先调用lock.lock()代码获得同步监视器

4、正确使用Condition实现等待/通知

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; class MyService{
private Lock lock=new ReentrantLock();
private Condition condition=lock.newCondition();
public void await() {
try {
lock.lock();
System.out.println("await时间为"+System.currentTimeMillis());
condition.await();
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
System.out.println("锁释放了");
}
} public void signal() {
try {
lock.lock();
System.out.println("signal时间为"+System.currentTimeMillis());
condition.signal();
}finally {
lock.unlock();
}
}
} class MyThread extends Thread{
private MyService service; public MyThread(MyService service) {
this.service=service;
} @Override
public void run() {
service.await();
}
} public class LockTest { public static void main(String[] args) throws InterruptedException {
MyService service=new MyService();
MyThread thread=new MyThread(service);
thread.start();
Thread.sleep(3000);
service.signal(); } }

  

运行结果:

await时间为1575599786039
signal时间为1575599789051
锁释放了

  

成功实现等待/通知模式

Object类中的wait()方法相当于Condition类中的await()方法,Object类中的wait(long timeout)方法相当于Condition类中的await(long time,TimeUnit unit)方法。Object类中的notify()方法相当于Condition类中的signal()方法。Object类中的notifyAll()方法相当于Condition类中的signalAll()方法。

5、使用多个Condition实现通知所有线程

前面使用一个Condition对象来实现等待/通知模式,其实Condition对象也可以创建多个。那么一个Condition对象和多个Condition对象在使用上有什么区别呢?

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; class MyService{
private Lock lock=new ReentrantLock();
private Condition condition=lock.newCondition();
public void awaitA() {
try {
lock.lock();
System.out.println("begin awaitA时间为"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName());
condition.await();
System.out.println("end awaitA时间为"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName());
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
} public void awaitB() {
try {
lock.lock();
System.out.println("begin awaitB时间为"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName());
condition.await();
System.out.println("end awaitB时间为"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName());
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
} public void signalAll() {
try {
lock.lock();
System.out.println("signalAll时间为"+System.currentTimeMillis());
condition.signalAll();
}finally {
lock.unlock();
}
}
} class MyThreadA extends Thread{
private MyService service; public MyThreadA(MyService service) {
this.service=service;
} @Override
public void run() {
service.awaitA();
}
} class MyThreadB extends Thread{
private MyService service; public MyThreadB(MyService service) {
this.service=service;
} @Override
public void run() {
service.awaitB();
}
} public class LockTest { public static void main(String[] args) throws InterruptedException {
MyService service=new MyService();
MyThreadA threadA=new MyThreadA(service);
threadA.setName("A");
threadA.start();
MyThreadB threadB=new MyThreadB(service);
threadB.setName("B");
threadB.start();
Thread.sleep(3000);
service.signalAll();
} }

  

运行结果:

begin awaitA时间为1575600904529ThreadNameA
begin awaitB时间为1575600904545ThreadNameB
signalAll时间为1575600907537
end awaitA时间为1575600907537ThreadNameA
end awaitB时间为1575600907537ThreadNameB

  

6、使用多个Condition实现通知部分线程

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; class MyService{
private Lock lock=new ReentrantLock();
private Condition conditionA=lock.newCondition();
private Condition conditionB=lock.newCondition();
public void awaitA() {
try {
lock.lock();
System.out.println("begin awaitA时间为"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName());
conditionA.await();
System.out.println("end awaitA时间为"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName());
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
} public void awaitB() {
try {
lock.lock();
System.out.println("begin awaitB时间为"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName());
conditionB.await();
System.out.println("end awaitB时间为"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName());
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
} //通知A
public void signalAll_A() {
try {
lock.lock();
System.out.println("signalAll_A时间为"+System.currentTimeMillis()+"ThreadName="+Thread.currentThread().getName());
conditionA.signalAll();
}finally {
lock.unlock();
}
} //通知B
public void signalAll_B() {
try {
lock.lock();
System.out.println("signalAll_A时间为"+System.currentTimeMillis()+"ThreadName="+Thread.currentThread().getName());
conditionA.signalAll();
}finally {
lock.unlock();
}
}
} class MyThreadA extends Thread{
private MyService service; public MyThreadA(MyService service) {
this.service=service;
} @Override
public void run() {
service.awaitA();
}
} class MyThreadB extends Thread{
private MyService service; public MyThreadB(MyService service) {
this.service=service;
} @Override
public void run() {
service.awaitB();
}
} public class LockTest { public static void main(String[] args) throws InterruptedException {
MyService service=new MyService();
MyThreadA threadA=new MyThreadA(service);
threadA.setName("A");
threadA.start();
MyThreadB threadB=new MyThreadB(service);
threadB.setName("B");
threadB.start();
Thread.sleep(3000);
service.signalAll_A();
} }

  

运行结果:

begin awaitA时间为1575601785167ThreadNameA
begin awaitB时间为1575601785167ThreadNameB
signalAll_A时间为1575601788181ThreadName=main
end awaitA时间为1575601788181ThreadNameA

  

上面的代码实现通知部分线程,定义了两个Condition,在测试类中只是唤醒了A,从输出结果可以看出,线程A被唤醒了,线程B依然处于等待状态

7、实现生产者/消费者模式:一个生产者一个消费者

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; class MyService{
private Lock lock=new ReentrantLock();
private Condition condition=lock.newCondition();
private boolean hasValue=false;
public void set() {
try {
lock.lock();
while(hasValue==true) {
condition.await();
}
System.out.println("打印★");
hasValue=true;
condition.signal();
}catch(Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
} public void get() {
try {
lock.lock();
while(hasValue==false) {
condition.await();
}
System.out.println("打印☆");
hasValue=false;
condition.signal();
}catch(Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
} class MyThreadA extends Thread{
private MyService service; public MyThreadA(MyService service) {
this.service=service;
} @Override
public void run() {
for(int i=0;i<Integer.MAX_VALUE;i++) {
service.set();
} }
} class MyThreadB extends Thread{
private MyService service; public MyThreadB(MyService service) {
this.service=service;
} @Override
public void run() {
for(int i=0;i<Integer.MAX_VALUE;i++) {
service.get();
}
}
} public class LockTest { public static void main(String[] args) throws InterruptedException {
MyService service=new MyService();
MyThreadA a=new MyThreadA(service);
a.start();
MyThreadB b=new MyThreadB(service);
b.start();
} }

  

运行结果:

上面代码实现了生产者消费者的功能,一个生产一个消费,如果hasValue=false相当于生产者没有生产产品,当前没有可消费的产品,所以调用生产者生产,当hasValue=true说明当前有产品还没有被消费,那么生产者应该停止生产,调用消费者消费

8、实现生产者/消费者模式:多个生产者多个消费者

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; class MyService{
private Lock lock=new ReentrantLock();
private Condition condition=lock.newCondition();
private boolean hasValue=false;
public void set() {
try {
lock.lock();
while(hasValue==true) {
System.out.println("有可能★★连续");
condition.await();
}
System.out.println("打印★");
hasValue=true;
condition.signal();
}catch(Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
} public void get() {
try {
lock.lock();
while(hasValue==false) {
System.out.println("有可能☆☆连续");
condition.await();
}
System.out.println("打印☆");
hasValue=false;
condition.signal();
}catch(Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
} class MyThreadA extends Thread{
private MyService service; public MyThreadA(MyService service) {
this.service=service;
} @Override
public void run() {
for(int i=0;i<Integer.MAX_VALUE;i++) {
service.set();
} }
} class MyThreadB extends Thread{
private MyService service; public MyThreadB(MyService service) {
this.service=service;
} @Override
public void run() {
for(int i=0;i<Integer.MAX_VALUE;i++) {
service.get();
}
}
} public class LockTest { public static void main(String[] args) throws InterruptedException {
MyService service=new MyService();
MyThreadA[] threadA=new MyThreadA[10];
MyThreadB[] threadB=new MyThreadB[10];
for(int i=0;i<10;i++) {
threadA[i]=new MyThreadA(service);
threadB[i]=new MyThreadB(service);
threadA[i].start();
threadB[i].start();
}
} }

  

运行结果:

运行程序后出现了假死,因为出现了生产者释放生产者或者消费者释放消费者的情况,那么该如何解决这个问题呢?在使用synchronized实现生产者消费者的时候我们也遇到过这种情况,当时是使用notifyAll()来解决这个问题的,那么现在使用锁我们则用signalAll()方法来解决死锁问题,将上述代码中signal()方法改成signalAll()即可

修改后程序运行结果如下

程序一直正常运行,没有出现死锁情况

9、公平锁和非公平锁

公平与非公平锁:锁Lock分为“公平锁”和“非公平锁”,公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序。而非公平锁就是一种获取锁的抢占机制,是随机获得锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁,结果也就是不公平的了。

创建公平锁和非公平锁ReentrantLock lock=new ReentrantLock(boolean a),创建锁时如果a为true的话,则创建的是公平锁,如果a为false的话,则创建的是非公平锁

公平锁

import java.util.concurrent.locks.ReentrantLock;

class Service{
private ReentrantLock lock;
public Service(boolean isFair) {
lock=new ReentrantLock(isFair);
} public void serviceMethod() {
try {
lock.lock();
System.out.println("ThreadName="+Thread.currentThread().getName()+"获得锁定");
}catch(Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
} public class LockTest { public static void main(String[] args) throws InterruptedException {
final Service service=new Service(true);
Runnable runnable=new Runnable() { @Override
public void run() {
System.out.println("★线程"+Thread.currentThread().getName()+"运行了");
service.serviceMethod();
}
};
Thread[] threadArray=new Thread[10];
for(int i=0;i<10;i++) {
threadArray[i]=new Thread(runnable);
}
for(int i=0;i<10;i++) {
threadArray[i].start();
} } }

  

运行结果:

★线程Thread-2运行了
★线程Thread-3运行了
★线程Thread-0运行了
★线程Thread-9运行了
★线程Thread-4运行了
★线程Thread-8运行了
★线程Thread-5运行了
★线程Thread-1运行了
★线程Thread-6运行了
★线程Thread-7运行了
ThreadName=Thread-2获得锁定
ThreadName=Thread-6获得锁定
ThreadName=Thread-1获得锁定
ThreadName=Thread-8获得锁定
ThreadName=Thread-0获得锁定
ThreadName=Thread-7获得锁定
ThreadName=Thread-5获得锁定
ThreadName=Thread-3获得锁定
ThreadName=Thread-9获得锁定
ThreadName=Thread-4获得锁定

  

结果显示输出基本是呈有序的状态,这就是公平锁的特点

非公平锁

import java.util.concurrent.locks.ReentrantLock;

class Service{
private ReentrantLock lock;
public Service(boolean isFair) {
lock=new ReentrantLock(isFair);
} public void serviceMethod() {
try {
lock.lock();
System.out.println("ThreadName="+Thread.currentThread().getName()+"获得锁定");
}catch(Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
} public class LockTest { public static void main(String[] args) throws InterruptedException {
final Service service=new Service(false);
Runnable runnable=new Runnable() { @Override
public void run() {
System.out.println("★线程"+Thread.currentThread().getName()+"运行了");
service.serviceMethod();
}
};
Thread[] threadArray=new Thread[10];
for(int i=0;i<10;i++) {
threadArray[i]=new Thread(runnable);
}
for(int i=0;i<10;i++) {
threadArray[i].start();
} } }

  

运行结果:

★线程Thread-2运行了
★线程Thread-9运行了
★线程Thread-7运行了
★线程Thread-0运行了
★线程Thread-3运行了
★线程Thread-1运行了
★线程Thread-6运行了
★线程Thread-5运行了
★线程Thread-4运行了
ThreadName=Thread-1获得锁定
★线程Thread-8运行了
ThreadName=Thread-8获得锁定
ThreadName=Thread-2获得锁定
ThreadName=Thread-7获得锁定
ThreadName=Thread-5获得锁定
ThreadName=Thread-3获得锁定
ThreadName=Thread-4获得锁定
ThreadName=Thread-9获得锁定
ThreadName=Thread-0获得锁定
ThreadName=Thread-6获得锁定

  

非公平锁的运行结果基本上是乱序的,说明先start()启动的线程不代表先获得锁

10、ReentranLock方法概述:

(1)、int getHoldCount()

  getHoldCount()的作用是查询当前线程保持此锁定的个数,也就是调用lock()方法的次数。

(2)、int getQueueLength()

  getQueueLength()的作用是返回正等待获取此锁定的线程估计数,比如有5个线程,1个线程首先执行awai()方法,那么在调用getQueueLength()方法后返回值是4,说明有4个线程同时在等待lock的释放。

(3)、int getWaitQueueLength(Condition condition)

  getWaitQueueLength(Condition condition)的作用是返回等待与此锁定相关的给定条件Condition的线程估计数,比如有5个线程,每个线程都执行了同一个condition对象的await()方法,则调用getWaitQueueLength(Condition condition)方法时返回的int值是5。

(4)、boolean hasQueuedThread(Thread thread)

  hasQueuedThread(Thread thread)的作用是查询指定的线程是否正在等待获取此锁定

  hasQueuedThreads()的作用是查询是否有线程正在等待获取此锁定。

(5)、boolean hasWaiters(Condition condition)

  hasWaiters(Condition condition)的作用是查询是否有线程正在等待与此锁定有关的condition条件。

(6)、boolean isFair()

  isFair()的作用是判断是不是公平锁

(7)、boolean isHeldByCurrentThread()

  isHeldByCurrentThread的作用是查询当前线程是否保持此锁定

(8)、boolean isLocked()

  isLocked()的作用是查询此锁定是否由任意的线程保持

ReentrantReadWriteLock

类ReentrantLock具有完全互斥排他的效果,即同一时间只有一个线程在执行ReentrantLock.lock()方法后面的任务。这样做虽然保证了实例变量的线程安全性,但效率却是非常低下的。所以在JDK中提供了一种读写锁ReentrantReadWriteLock类,使用它可以加快运行效率,在某些不需要操作实例变量的方法中,完全可以使用读写锁ReentrantReadWriteLock 来提升该方法的代码运行速度。

读写锁表示也有两个锁,一个是读操作相关的锁,也称为共享锁;另一个是写操作相关的锁,也叫排他锁。也就是多个读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥。在没有线程Thread进行写入操作时,进行读取操作的多个Thread都可以获取读锁,而进行写入操作的Thread只有在获取写锁后才能进行写入操作。即多个Thread可以同时进行读取操作,但是同一时刻只允许一个Thread进行写入操作。

一、ReentrantReadWriteLock读读共享

import java.util.concurrent.locks.ReentrantReadWriteLock;

class Service{
private ReentrantReadWriteLock lock=new ReentrantReadWriteLock(); public void read() {
try {
try {
lock.readLock().lock();
System.out.println("获取读锁"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
Thread.sleep(10000);
}finally {
lock.readLock().unlock();
}
}catch(Exception e) {
e.printStackTrace();
}
}
} class MyThreadA extends Thread{
private Service service; public MyThreadA(Service service) {
this.service=service;
} @Override
public void run() {
service.read();
}
} class MyThreadB extends Thread{
private Service service; public MyThreadB(Service service) {
this.service=service;
} @Override
public void run() {
service.read();
}
} public class LockTest { public static void main(String[] args) throws InterruptedException {
Service service=new Service();
MyThreadA a=new MyThreadA(service);
a.setName("A");
MyThreadB b=new MyThreadB(service);
b.setName("B");
a.start();
b.start();
} }

  

运行结果:

获取读锁A 1575611161158
获取读锁B 1575611161158

  

从输出结果打印的时间来看,两个线程几乎同时进入lock()方法后面的代码。说明在此使用了lock.readLock()读锁可以提高程序运行效率,允许多个线程同时执行lock()方法后面的代码。

二、ReentrantReadWriteLock写写互斥

import java.util.concurrent.locks.ReentrantReadWriteLock;

class Service{
private ReentrantReadWriteLock lock=new ReentrantReadWriteLock(); public void write() {
try {
try {
lock.writeLock().lock();
System.out.println("获取写锁"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
Thread.sleep(10000);
}finally {
lock.writeLock().unlock();
}
}catch(Exception e) {
e.printStackTrace();
}
}
} class MyThreadA extends Thread{
private Service service; public MyThreadA(Service service) {
this.service=service;
} @Override
public void run() {
service.write();
}
} class MyThreadB extends Thread{
private Service service; public MyThreadB(Service service) {
this.service=service;
} @Override
public void run() {
service.write();
}
} public class LockTest { public static void main(String[] args) throws InterruptedException {
Service service=new Service();
MyThreadA a=new MyThreadA(service);
a.setName("A");
MyThreadB b=new MyThreadB(service);
b.setName("B");
a.start();
b.start();
} }

  

运行结果:

获取写锁B 1575611458260
获取写锁A 1575611468273

  

结果显示写锁的效果是同一时间只允许一个线程执行lock()后面的代码

三、ReentrantReadWriteLock读写互斥

import java.util.concurrent.locks.ReentrantReadWriteLock;

class Service{
private ReentrantReadWriteLock lock=new ReentrantReadWriteLock(); public void read() {
try {
try {
lock.readLock().lock();
System.out.println("获取读锁"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
Thread.sleep(10000);
}finally {
lock.readLock().unlock();
}
}catch(Exception e) {
e.printStackTrace();
}
} public void write() {
try {
try {
lock.writeLock().lock();
System.out.println("获取写锁"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
Thread.sleep(10000);
}finally {
lock.writeLock().unlock();
}
}catch(Exception e) {
e.printStackTrace();
}
}
} class MyThreadA extends Thread{
private Service service; public MyThreadA(Service service) {
this.service=service;
} @Override
public void run() {
service.read();
}
} class MyThreadB extends Thread{
private Service service; public MyThreadB(Service service) {
this.service=service;
} @Override
public void run() {
service.write();
}
} public class LockTest { public static void main(String[] args) throws InterruptedException {
Service service=new Service();
MyThreadA a=new MyThreadA(service);
a.setName("A");
MyThreadB b=new MyThreadB(service);
b.setName("B");
a.start();
b.start();
} }

  

运行结果:

获取读锁A 1575611689661
获取写锁B 1575611699665

  

从读写的时间上可以看出读写的操作时互斥的

Java多线程——锁的更多相关文章

  1. Java多线程--锁的优化

    Java多线程--锁的优化 提高锁的性能 减少锁的持有时间 一个线程如果持有锁太长时间,其他线程就必须等待相应的时间,如果有多个线程都在等待该资源,整体性能必然下降.所有有必要减少单个线程持有锁的时间 ...

  2. synchronized与static synchronized 的差别、synchronized在JVM底层的实现原理及Java多线程锁理解

    本Blog分为例如以下部分: 第一部分:synchronized与static synchronized 的差别 第二部分:JVM底层又是怎样实现synchronized的 第三部分:Java多线程锁 ...

  3. Java 多线程 锁 存款 取款

    http://jameswxx.iteye.com/blog/806968 最近想将java基础的一些东西都整理整理,写下来,这是对知识的总结,也是一种乐趣.已经拟好了提纲,大概分为这几个主题: ja ...

  4. Java多线程——锁概念与锁优化

    为了性能与使用的场景,Java实现锁的方式有非常多.而关于锁主要的实现包含synchronized关键字.AQS框架下的锁,其中的实现都离不开以下的策略. 悲观锁与乐观锁 乐观锁.乐观的想法,认为并发 ...

  5. [java多线程] - 锁机制&同步代码块&信号量

    在美眉图片下载demo中,我们可以看到多个线程在公用一些变量,这个时候难免会发生冲突.冲突并不可怕,可怕的是当多线程的情况下,你没法控制冲突.按照我的理解在java中实现同步的方式分为三种,分别是:同 ...

  6. 详解Java多线程锁之synchronized

    synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法. synchronized的四种使用方式 修饰代码块:被修饰的代码块称为同步语句块,其作用的范围是大括号{}括 ...

  7. Java——多线程锁的那些事

    引入 Java提供了种类丰富的锁,每种锁因其特性的不同,在适当的场景下能够展现出非常高的效率. 下面先带大家来总体预览一下锁的分类图 1.乐观锁 VS 悲观锁 乐观锁与悲观锁是一种广义上的概念,体现了 ...

  8. java多线程-锁

    自 Java 5 开始,java.util.concurrent.locks 包中包含了一些锁的实现,因此你不用去实现自己的锁了.但是你仍然需要去了解怎样使用这些锁. 一个简单的锁 让我们从 java ...

  9. Java多线程-锁的区别与使用

    目录 锁类型 可中断锁 公平锁/非公平锁 可重入锁 独享锁/共享锁 互斥锁/读写锁 乐观锁/悲观锁 分段锁 偏向锁/轻量级锁/重量级锁 自旋锁 Synchronized与Static Synchron ...

随机推荐

  1. 最强中文NLP预训练模型艾尼ERNIE官方揭秘【附视频】

    “最近刚好在用ERNIE写毕业论文” “感觉还挺厉害的” “为什么叫ERNIE啊,这名字有什么深意吗?” “我想让艾尼帮我写作业” 看了上面火热的讨论,你一定很好奇“艾尼”.“ERNIE”到底是个啥? ...

  2. Go调用cpp类

    CGO是C语言和Go语言之间的桥梁,所以GO是没有办法直接使用CPP的类的. 我们可以通过增加一族C语言函数接口作为CPP类和CGO之前的桥梁的,这样 就可以实现C和Go之间的互联. my_buffe ...

  3. ASP.NET Core主机地址过滤HostFiltering

    前言 在ASP.Net Core2.X调用的CreateWebHostBuilder和3.X的主要区别在于WebHost的调用,CreateDefaultBuilder被Host替换,另一个区别是对C ...

  4. docker项目——搭建飞机大战小游戏

    项目2:搭建打飞机小游戏,验证数据持久化(最底下有链接) 第一步:拉取镜像 [root@localhost docker-image]# docker load < httpd_img.tar. ...

  5. 用IDEA导入Eclipse的JavaWEB项目

    1.File----Open 2.选择项目路径 点击OK,这时候会弹出一个窗口,让你选择本窗口打开项目还是新建一个窗口,这个随意. 3.添加WEB框架 4.添加各种自己需要的jar包 5.添加Tomc ...

  6. nyoj 36-最长公共子序列 (动态规划,DP, LCS)

    36-最长公共子序列 内存限制:64MB 时间限制:3000ms Special Judge: No accepted:18 submit:38 题目描述: 咱们就不拐弯抹角了,如题,需要你做的就是写 ...

  7. nyoj 96-n-1位数 (strlen, atoi, ceil)

    96-n-1位数 内存限制:64MB 时间限制:3000ms 特判: No 通过数:30 提交数:47 难度:1 题目描述: 已知w是一个大于10但不大于1000000的无符号整数,若w是n(n≥2) ...

  8. mysql数据库如何赋予远程某个IP 访问权限

    1.授权用户root使用密码jb51从任意主机连接到mysql服务器: 代码如下: GRANT ALL PRIVILEGES ON *.* TO 'ROOT'@'%' IDENTIFIED BY 'j ...

  9. 力扣(LeetCode)两数相加 个人题解

    给出两个 非空 的链表用来表示两个非负的整数.其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字. 如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和 ...

  10. opencv 5 图像转换(3 重映射 仿射变换 直方图均衡化)

    重映射 实现重映射(remap函数) 基础示例程序:基本重映射 //---------------------------------[头文件.命名空间包含部分]------------------- ...