`

Java并发编程之ReentrantLock学习

阅读更多

Lock和synchronized区别

1、synchronized是java提供的内置关键字,一旦使用线程就被锁住,直到线程执行完成或者处于wait状态下才会释放;如果线程处于阻塞了就会其他线程就会一直等待,如sleep;
2、Lock是一个接口,在JDK1.5提供,属于JUC下面常用的同步处理接口。在执行同步是都需要通过方法获取锁,执行完成之后必须释放锁;在获取锁时,通过tryLock方法可以判断锁是否被占用,从而避免线程因锁被占用而阻塞。

用synchronized实现简单的加锁和解锁

	private boolean isLock = false;
	
	private Thread currentThread = null;
	
	public synchronized void lock() throws InterruptedException{
		while (isLock) {
			wait();
		}
		isLock = true;
		currentThread = Thread.currentThread();
	}
	
	public synchronized void unlock() throws InterruptedException{
		if (this.currentThread != Thread.currentThread()) {
			throw new InterruptedException("当前线程没有加锁");
		} else {
			notify();
			isLock = false;
			currentThread = null;
		}
	}

	public void myLockTest() throws InterruptedException{
		lock();
		System.out.println(Thread.currentThread().getName() + " 获得了锁 ");
		try {
			for (int i = 0; i < 10; i++) {
				Thread.sleep(200);
				System.out.println("执行第 "+i+"次");
			}
		} finally {
			System.out.println(Thread.currentThread().getName() + " 释放了锁 ");
			unlock();
		}
	}

	public static void main(String[] args) {
		
		final MyLock lock = new MyLock();
		
		ExecutorService service = Executors.newFixedThreadPool(2);
		service.submit(new Runnable() {
			public void run() {
				try {
					lock.myLockTest();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		});
		service.submit(new Runnable() {
			public void run() {
				try {
					lock.myLockTest();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		});
		service.shutdown();
	}

执行结果
pool-1-thread-1 获得了锁 
执行第 0次
执行第 1次
执行第 2次
pool-1-thread-1 释放了锁 
pool-1-thread-2 获得了锁 
执行第 0次
执行第 1次
执行第 2次
pool-1-thread-2 释放了锁 


当然这只是一个实例,这种方式远没有lock接口直接和方便。

ReentrantLock

ReentrantLock是Lock的实现接口,用法比较简单;synchronized和ReentrantLock都是可重入锁,可重入锁获取是基于线程,而不是基于方法调用的分配。如下:
public void lock1(){
		locks.lock();
		System.out.println(Thread.currentThread().getName() + " 获得了锁 ");
		try {
			lock2();
		} finally {
			locks.unlock();
			System.out.println(Thread.currentThread().getName() + " 释放了锁 ");
		}
	}
	
	public void lock2(){
		locks.lock();
		System.out.println(Thread.currentThread().getName() + " 获得了锁 ");
		try {
			for (int i = 0; i < 3; i++) {
				System.out.println("执行第 "+i+"次");
			}
		} finally {
			locks.unlock();
			System.out.println(Thread.currentThread().getName() + " 释放了锁 ");
		}
	}


当调用lock1时,锁被获取,再调用lock2时,由于具备可重入性,不会出现等待或者死锁问题。

ReentrantLock 是一种独占锁,具有可重入性,独占锁是一种互斥锁,即当前线程占用锁之后,其他线程只能等待锁释放之后才能继续占有。它分为公平锁和非公平锁,公平锁是通过AQS 类中一个CLH对列(FIFO)来保证实现公平性,非公平锁是直接得到一个可获取状态的锁,不管是不是在队列头还是队列中。

ReentrantLock 关键UML图:




Lock:接口类,包括各种lock、unlock、ttyLock方法;
Sync:ReentrantLock中内部类,继承了AQS类;
NonfairSync:内部类,非公平锁实现,继承了Sync类;
FairSync:内部类,公平锁实现,继承Sync类。

NonfairSync 类解读:

lock方法实现:
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

直接通过CAS方法获取锁,成功则设置线程获取,失败则通过acquire方法获取,acquire是AQS类中的方法,后续会讲解。1表示的是锁的状态state。对于独占锁而言,如果所处于可获取状态,其状态为0,当锁初次被线程获取时状态变成1。

而tryAcquire方法是直接调用父类Sync中的nonfairTryAcquire方法,具体实现了非公平锁的获取

        /**
         * 非公平锁实现
         */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

非公平锁获取,先获取当前状态,如果为0表示处于可获取状态,通过CAS方法直接获取锁;如果当前线程就是锁的用户,则状态值增加。

FairSync类:
lock方法:
        final void lock() {
            acquire(1);
        }

调用AQS类中acquire方法,与非公平锁一致,tryAcquire是重写的AQS类中的tryAcquire方法。

读写锁
ReentrantReadWriteLock就是对ReadWriteLock进行实现的,即读写互斥,准确的是说,一旦发现写锁,那么当前锁就会被独占,而如果只有读锁。这可以多个线程同时操作。

其实现和ReetrantLock差不多,使用示例:

	private ReentrantReadWriteLock readLock = new ReentrantReadWriteLock();
	
	
	public void read(){
		readLock.readLock().lock();
		if (!readLock.isWriteLocked()) {
			System.out.println(Thread.currentThread().getName()+"当前为读锁----");
		}
		try {
			System.out.println(Thread.currentThread().getName()+"开始读操作.");
			for (int i=0; i<5; i++) {
				System.out.println(Thread.currentThread().getName()+"进行读操作...");
			}
			System.out.println(Thread.currentThread().getName()+"读操作完毕.");
		} finally {
			System.out.println(Thread.currentThread().getName()+"释放读锁-----");
			readLock.readLock().unlock();
		}
	}
	
	public void write(){
		readLock.writeLock().lock();
		if (readLock.isWriteLocked()) {
			System.out.println(Thread.currentThread().getName()+"当前为写锁----");
		}
		try {
			System.out.println(Thread.currentThread().getName()+"开始写操作.");
			for (int i=0; i<5; i++) {
				System.out.println(Thread.currentThread().getName()+"进行写操作...");
			}
			System.out.println(Thread.currentThread().getName()+"写操作完毕.");
		} finally {
			System.out.println(Thread.currentThread().getName()+"释放写锁-----");
			readLock.writeLock().unlock();
		}
	}
	
	class ReadThread implements Runnable{
		@Override
		public void run() {
			read();
		}
	}
	
	class writeThread implements Runnable{
		@Override
		public void run() {
			write();
		}
	}
	
	
	public static void main(String[] args) {
		
		ExecutorService service = Executors.newCachedThreadPool();
		LockTest lockTest = new LockTest();
		
		service.submit(lockTest.new ReadThread());
		service.submit(lockTest.new ReadThread());
		service.submit(lockTest.new writeThread());
		service.submit(lockTest.new writeThread());
		
		service.shutdown();
	}


执行结果:
pool-1-thread-1当前为读锁----
pool-1-thread-1开始读操作.
pool-1-thread-1进行读操作...
pool-1-thread-2当前为读锁----
pool-1-thread-1进行读操作...
pool-1-thread-2开始读操作.
pool-1-thread-1进行读操作...
pool-1-thread-2进行读操作...
pool-1-thread-1进行读操作...
pool-1-thread-2进行读操作...
pool-1-thread-2进行读操作...
pool-1-thread-1进行读操作...
pool-1-thread-2进行读操作...
pool-1-thread-1读操作完毕.
pool-1-thread-2进行读操作...
pool-1-thread-1释放读锁-----
pool-1-thread-2读操作完毕.
pool-1-thread-2释放读锁-----
pool-1-thread-2当前为写锁----
pool-1-thread-2开始写操作.
pool-1-thread-2进行写操作...
pool-1-thread-2进行写操作...
pool-1-thread-2进行写操作...
pool-1-thread-2进行写操作...
pool-1-thread-2进行写操作...
pool-1-thread-2写操作完毕.
pool-1-thread-2释放写锁-----
pool-1-thread-1当前为写锁----
pool-1-thread-1开始写操作.
pool-1-thread-1进行写操作...
pool-1-thread-1进行写操作...
pool-1-thread-1进行写操作...
pool-1-thread-1进行写操作...
pool-1-thread-1进行写操作...
pool-1-thread-1写操作完毕.
pool-1-thread-1释放写锁-----


可以看出,只有读锁时,可以多个线程共享,但是一点有写锁,那么就被独占了。

从读锁源码看到,读锁是采用的共享锁;而写锁是用的独占锁;
  public static class ReadLock implements Lock, java.io.Serializable {
        private static final long serialVersionUID = -5992448646407690164L;
        private final Sync sync;

        /**
         */
        protected ReadLock(ReentrantReadWriteLock lock) {
            sync = lock.sync;
        }

        /**
         * 共享锁
         */
        public void lock() {
            sync.acquireShared(1);
        }
    //略
  }

写锁:
 public static class WriteLock implements Lock, java.io.Serializable {
        private static final long serialVersionUID = -4992448646407690164L;
        private final Sync sync;

        /**
         */
        protected WriteLock(ReentrantReadWriteLock lock) {
            sync = lock.sync;
        }

        /**
         * 独占锁..
         */
        public void lock() {
            sync.acquire(1);
        }
  //略
}
  • 大小: 53.9 KB
分享到:
评论

相关推荐

    java并发编程实战源码,java并发编程实战pdf,Java

    通过学习《Java并发编程实战》的源码,你可以更直观地了解这些概念如何在实际代码中实现,从而提升你的并发编程能力。在IDE中运行这些示例,可以加深对并发原理的理解,同时也能锻炼解决问题的能力。记住,实践是...

    《java 并发编程实战高清PDF版》

    在Java并发编程中,多线程是核心概念之一。多线程允许程序同时执行多个任务,从而充分利用系统资源,提高程序性能。然而,多线程编程也带来了同步和竞态条件等问题,这需要开发者具备良好的线程管理和同步机制的知识...

    Java 并发编程实战.pdf

    根据提供的信息,“Java 并发编程实战.pdf”这本书聚焦于Java并发编程的实践与应用,旨在帮助读者深入了解并掌握...无论是对于初学者还是有一定经验的开发者来说,《Java并发编程实战》都是一本值得深入学习的好书。

    Java并发编程实战华章专业开发者书库 (Tim Peierls 等 美Brian Goetz).pdf

    第四部分深入探讨了Java并发编程的高级主题,包括显式锁(如ReentrantLock)、原子变量(Atomic类)、非阻塞算法以及自定义同步组件的开发。这些高级主题帮助开发者解决复杂并发场景下的问题,实现更高层次的并发...

    java并发编程艺术

    锁机制是Java并发编程中的另一大主题,包括内置锁(互斥锁)和显式锁(如`ReentrantLock`)。内置锁是`synchronized`关键字提供的,而显式锁提供了更细粒度的控制和更丰富的功能。书中可能还会讨论读写锁(`...

    java并发编程2

    Java并发编程是Java开发中的重要领域,特别是在多核处理器和分布式系统中,高效地利用并发可以极大地提升程序的性能和响应速度。以下是对标题和描述中所提及的几个知识点的详细解释: 1. **线程与并发** - **线程*...

    Java并发编程实践高清pdf及源码

    通过学习《Java并发编程实践》,开发者将能够更好地理解和利用Java平台的并发特性,编写出更高效、更可靠的多线程应用程序。无论是初级开发者还是经验丰富的专业人员,都能从这本书中收获宝贵的并发编程知识。

    java并发编程内部分享PPT

    Java并发编程是Java开发中的重要领域,特别是在多核处理器和分布式系统中,高效地利用并发可以极大地提升程序的性能和响应速度。这份“java并发编程内部分享PPT”显然是一个深入探讨这一主题的资料,旨在帮助开发者...

    java 并发编程的艺术pdf清晰完整版 源码

    《Java并发编程的艺术》这本书是Java开发者深入理解并发编程的重要参考书籍。这本书全面地介绍了Java平台上的并发和多线程编程技术,旨在帮助开发者解决在实际工作中遇到的并发问题,提高程序的性能和可伸缩性。 ...

    java并发编程实战(英文版)

    综上所述,《Java并发编程实战》不仅涵盖了Java并发编程的基础知识和技术细节,还包含了丰富的实践经验和前瞻性的思考,是任何一位从事Java开发工作的程序员不可或缺的学习资源。无论是初学者还是有经验的开发者都能...

    JAVA并发编程艺术pdf版

    通过深入学习《JAVA并发编程艺术》,开发者能更好地理解并发编程的原理,熟练运用Java提供的并发工具和API,解决实际开发中的多线程问题,提高软件的性能和稳定性。这是一本值得每一位Java开发者研读的书。

    java并发编程实践pdf笔记

    Java并发编程实践是Java开发中不可或缺的一个领域,它涉及到如何高效、正确地处理多线程环境中的任务。这本书的读书笔记涵盖了多个关键知识点,旨在帮助读者深入理解Java并发编程的核心概念。 1. **线程和进程的...

    (PDF带目录)《Java 并发编程实战》,java并发实战,并发

    通过学习《Java 并发编程实战》,开发者可以更好地理解和掌握Java并发编程的核心技术,从而编写出更健壮、高效的并发程序。这本书的PDF版本包含了清晰的目录结构,方便读者查阅和学习。使用福昕阅读器打开,能确保...

    java并发编程与实践

    通过深入学习"Java并发编程与实践"文档,开发者能够提升自己在高并发环境下的编程能力,设计出更加健壮和高效的Java应用程序。这份资料对于理解Java并发原理、优化并发代码和解决并发问题具有极大的价值。

    java并发编程

    Java并发编程是Java开发者必须掌握的关键技能之一,它涉及到如何在多线程环境中高效、安全地执行程序。并发编程能够充分利用多核处理器的计算能力,提高应用程序的响应速度和整体性能。《Java编程并发实战》这本书是...

    Java并发编程实践.pdf

    ### Java并发编程实践 #### 一、并发编程基础 ##### 1.1 并发与并行的区别 ...通过上述知识点的学习,我们可以更好地理解和掌握Java并发编程的基本原理和技巧,为开发高效稳定的并发应用程序打下坚实的基础。

    Java并发编程设计原则和模式

    通过学习“Java并发编程设计原则和模式”,开发者可以更好地理解和应用这些概念,编写出高效、可靠的并发程序。理解并熟练掌握这些知识,对于提升Java程序员的技能水平和解决实际问题的能力至关重要。

    JAVA并发编程实践 中文 高清 带书签 完整版 Doug Lea .pdf

    根据提供的文件信息,“JAVA并发编程实践 中文 高清 带书签 完整版 Doug Lea .pdf”,我们可以推断出这...通过深入学习这些知识点,可以帮助开发者更好地理解和应用Java并发编程技术,从而提高软件系统的性能和稳定性。

    Java并发编程学习笔记

    Java并发编程是指在Java...学习Java并发编程,重点在于理解线程如何协同工作,以及如何确保并发执行的正确性和高效性。通过深入掌握Java并发API,可以为大型分布式系统和高并发应用场景编写出更加健壮和高效的代码。

Global site tag (gtag.js) - Google Analytics