`
ftj20003
  • 浏览: 132066 次
  • 性别: Icon_minigender_1
  • 来自: ...
社区版块
存档分类
最新评论

多线程基础总结七--ReentrantLock

    博客分类:
  • Java
阅读更多
    之前总结了部分无锁机制的多线程基础,理想的状态当然是利用无锁同步解决多线程程序设计的问题。但是实际碰到的问题使得很多情况下,我们不得不借助锁同步来保证线程安全。自从JDK5开始,有两种机制来屏蔽代码块在并行访问的干扰,synchronized关键字已经介绍过了部分内容,所以这次简单的说说另一种锁机制:ReentrantLock。

    对于synchronized的缺点之前也简单的说了一些,实际使用中比较烦扰的几点是:a.只有一个"条件"与锁相关联,这对于大量并发线程的情况是很难管理(等待和唤醒);b.多线程竞争一个锁时,其余未得到锁的线程只能不停的尝试获得锁,而不能中断。这种情况对于大量的竞争线程会造成性能的下降等后果。JDK5以后提供了ReentrantLock的同步机制对于前面提的两种情况有相对的改善。下面我还是写个小例子分析一下:
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author: yanxuxin
 * @date: 2010-1-4
 */
public class ReentrantLockSample {

	public static void main(String[] args) {
		testSynchronized();
		testReentrantLock();
	}

	public static void testReentrantLock() {
		final SampleSupport1 support = new SampleSupport1();
		Thread first = new Thread(new Runnable() {
			public void run() {
				try {
					support.doSomething();
				}
				catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});

		Thread second = new Thread(new Runnable() {
			public void run() {
				try {
					support.doSomething();
				}
				catch (InterruptedException e) {
					System.out.println("Second Thread Interrupted without executing counter++,beacuse it waits a long time.");
				}
			}
		});

		executeTest(first, second);
	}

	public static void testSynchronized() {
		final SampleSupport2 support2 = new SampleSupport2();

		Runnable runnable = new Runnable() {
			public void run() {
				support2.doSomething();
			}
		};

		Thread third = new Thread(runnable);
		Thread fourth = new Thread(runnable);

		executeTest(third, fourth);
	}

	/**
	 * Make thread a run faster than thread b,
     * then thread b will be interruted after about 1s.
	 * @param a
	 * @param b
	 */
	public static void executeTest(Thread a, Thread b) {
		a.start();
		try {
			Thread.sleep(100);
			b.start(); // The main thread sleep 100ms, and then start the second thread.

			Thread.sleep(1000);
    // 1s later, the main thread decided not to allow the second thread wait any longer.
			b.interrupt(); 
		}
		catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

abstract class SampleSupport {

	protected int counter;

	/**
	 * A simple countdown,it will stop after about 5s. 
	 */
	public void startTheCountdown() {
		long currentTime = System.currentTimeMillis();
		for (;;) {
			long diff = System.currentTimeMillis() - currentTime;
			if (diff > 5000) {
				break;
			}
		}
	}
}

class SampleSupport1 extends SampleSupport {

	private final ReentrantLock lock = new ReentrantLock();

	public void doSomething() throws InterruptedException {
		lock.lockInterruptibly(); // (1)
		System.out.println(Thread.currentThread().getName() + " will execute counter++.");
		startTheCountdown();
		try {
			counter++;
		}
		finally {
			lock.unlock();
		}
	}
}

class SampleSupport2 extends SampleSupport {

	public synchronized void doSomething() {
		System.out.println(Thread.currentThread().getName() + " will execute counter++.");
		startTheCountdown();
		counter++;
	}
}

    在这个例子中,辅助类SampleSupport提供一个倒计时的功能startTheCountdown(),这里倒计时5s左右。SampleSupport1,SampleSupport2继承其并分别的具有doSomething()方法,任何进入方法的线程会运行5s左右之后counter++然后离开方法释放锁。SampleSupport1是使用ReentrantLock机制,SampleSupport2是使用synchronized机制。

    testSynchronized()和testReentrantLock()都分别开启两个线程执行测试方法executeTest(),这个方法会让一个线程先启动,另一个过100ms左右启动,并且隔1s左右试图中断后者。结果正如之前提到的第二点:interrupt()对于synchronized是没有作用的,它依然会等待5s左右获得锁执行counter++;而ReentrantLock机制可以保证在线程还未获得并且试图获得锁时如果发现线程中断,则抛出异常清除中断标记退出竞争。所以testReentrantLock()中second线程不会继续去竞争锁,执行异常内的打印语句后线程运行结束。

    这里我是用了ReentrantLock的lockInterruptibly()方法,在SampleSupport1的代码(1)处。这个方法保证了中断线程的响应,如果仅仅是lock()则不会有此功能。但是不管怎么说ReentrantLock提供了解决方案。至于提到的第一点“多条件”的机制我通过java.util.concurrent.ArrayBlockingQueue(源码参考1.6.0.17内的实现)简单的介绍一下:
public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable {
    ...
    /** Main lock guarding all access */
    private final ReentrantLock lock;

    /** Condition for waiting takes */
    private final Condition notEmpty;

    /** Condition for waiting puts */
    private final Condition notFull;

    ...
    public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = (E[]) new Object[capacity];
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }

    public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        final E[] items = this.items;
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            try {
                while (count == items.length)
                    notFull.await();
            } catch (InterruptedException ie) {
                notFull.signal(); // propagate to non-interrupted thread
                throw ie;
            }
            insert(e);
        } finally {
            lock.unlock();
        }
    }

    private void insert(E x) {
        items[putIndex] = x;
        putIndex = inc(putIndex);
        ++count;
        notEmpty.signal();
    }

    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            try {
                while (count == 0)
                    notEmpty.await();
            } catch (InterruptedException ie) {
                notEmpty.signal(); // propagate to non-interrupted thread
                throw ie;
            }
            E x = extract();
            return x;
        } finally {
            lock.unlock();
        }
    }

    private E extract() {
        final E[] items = this.items;
        E x = items[takeIndex];
        items[takeIndex] = null;
        takeIndex = inc(takeIndex);
        --count;
        notFull.signal();
        return x;
    }
    ...
}

    这里notEmpty和notFull作为lock的两个条件是可以分别负责管理想要加入元素的线程和想要取出元素的线程的wait和notify分别通过await()和signal(),signalAll()方法,有效的分离了不同职责的线程。例如put()方法在元素个数达到最大限制时会使用notFull条件把试图继续插入元素的线程都扔到等待集中,而执行了take()方法时如果顺利进入extract()则会空出空间,这时notFull负责随机的通知被其扔到等待集中的线程执行插入元素的操作。这样的设计使得线程按照功能行为职责管理成为了现实。
   
    通过上述的总结,对于ReentrantLock的优点有了一定的认识,但是它也是实现了与synchronized相同语义和行为的可重用完全互斥锁,所以在竞争机制上不会有什么性能提高,功能倒是强大了不少。不过使用它要配合try{...}finally{...}显式的释放锁,这点是决定如果业务实现没有需要使用其特有的功能,更好的方式是使用synchronized。后者毕竟不用自己去释放锁,降低了开发的失误率。当然在java.util.concurrent.locks包内还一个很有意思的锁:ReentrantReadWriteLock,其提供了部分互斥的锁实现,以后的总结会有介绍。
1
0
分享到:
评论
发表评论

文章已被作者锁定,不允许评论。

相关推荐

    IBM多线程编程基础教程

    7. **总结**(IBM多线程教程七-总结.doc): - 整合上述知识点,强调实践中的注意事项,如避免死锁、减少锁的粒度以及有效利用线程池等。 通过这个教程,你将能够掌握在IBM环境中使用Java进行多线程编程的基本技巧...

    多线程精品资源--️这是一条汇总网上许多资料,而不是资料的纯粹堆砌,让人眼花缭乱的复制粘贴,这不是帮你在总结所有的知.zip

    一、多线程基础知识 1. **线程定义**:线程是操作系统分配处理器时间的基本单位,一个进程可以包含一个或多个线程,每个线程都有自己独立的程序计数器、系统栈和局部变量,但共享同一块内存空间,包括全局变量和...

    Java_多线程编程线程:大总结

    ### Java多线程编程大总结 #### 一、Java多线程编程的演进与重要性 Java多线程编程自Java 5之后经历了显著的革新与扩展,引入了强大的并发包,极大地增强了多线程编程的能力。在Java 5之前,多线程的支持较为薄弱...

    JAVA多线程总结

    本篇总结涵盖了Java多线程的基础概念、创建与启动、线程调度、同步与协作以及新特性。 **一、Java线程:概念与原理** 1. **线程与进程**: - **进程**:是操作系统资源分配的基本单位,每个进程都有独立的内存...

    Java 多线程学习总结归纳(附代码)

    以上内容涵盖了Java多线程的基础知识,包括创建、同步、终止、线程安全和并发控制等方面。通过实际的代码实践,可以深入理解并掌握这些概念,提升多线程编程的能力。同时,文档中的代码示例能帮助读者更好地理解和...

    Java多线程小结

    ### Java多线程小结 ...但是,多线程编程也面临着诸如死锁、竞态条件等问题,因此需要开发者具备良好的设计思路和扎实的基础知识。掌握多线程的原理和实现机制对于编写高质量的Java应用至关重要。

    Java多线程编程总结

    Java多线程编程是构建高性能、高并发应用程序的基础,涉及线程的创建、管理、同步、调度等多个方面。掌握线程生命周期、同步机制、并发工具和最佳实践是成为一名优秀Java开发者的关键。通过深入理解并熟练运用Java多...

    JAVA-基础多线程

    ### JAVA-基础多线程 #### 一、线程概念 1. **线程定义**: - 线程是程序执行时的一条路径,是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。 - 一个线程指的是进程中一个...

    大学毕业设计Java多线程与线程安全实践-基于Http协议的断点续传.zip

    一、Java多线程基础 多线程是指在单个应用程序中同时执行多个线程的能力。在Java中,我们可以通过实现Runnable接口或继承Thread类来创建线程。通过多线程,可以将不同的任务分配到不同的线程中,从而实现任务的并行...

    Java多线程的总结

    这篇总结将深入探讨Java多线程的基础概念、特性以及常见用法,旨在为初学者提供一个全面的学习指南。 一、线程的基本概念 在Java中,线程是程序执行的最小单位,每个线程都有自己的程序计数器、虚拟机栈、本地方法...

    多线程起步,总结的,一些方法的使用,简单的列子,使用方法

    本文将深入探讨多线程的基础知识,包括它的概念、使用场景、核心方法以及通过简单示例来帮助初学者理解多线程的实践操作。 1. 多线程概念: 多线程是指在一个应用程序中同时执行多个独立的执行路径,每个路径称为一...

    Java多线程与线程安全实践-基于Http协议的断点续传

    总结来说,Java多线程技术与HTTP断点续传相结合,能够提供高效且用户友好的文件下载服务。但同时,我们必须重视线程安全问题,采用适当的同步机制来保护共享资源,以确保程序的稳定性和正确性。在实际开发中,理解并...

    JAVA多线程与线程安全实践-基于Http协议的断点续传

    总结,Java多线程与线程安全在实现HTTP协议的断点续传中起着关键作用,通过合理的线程管理和并发控制,能够实现高效且可靠的文件传输。在实际项目中,应结合具体需求,灵活运用各种并发工具和策略,确保程序的稳定性...

    Java多线程与线程安全实践-基于Http协议的断点续传.zip

    总结一下,Java多线程和线程安全是构建并发应用程序的基础,而HTTP协议的断点续传功能则可以优化大文件的下载体验。通过合理利用Java提供的多线程机制和同步工具,以及HTTP协议特性,我们可以构建出高效且可靠的断点...

    Java 多线程学习总结6

    【Java多线程学习总结6】这篇博文主要探讨了Java平台上的多线程编程,这是一个核心的编程概念,尤其在服务器端应用和并发处理中至关重要。在Java中,多线程允许程序同时执行多个任务,提高了系统资源的利用率,提升...

    JAVA多线程编程案例_火车站售票系统

    1. **多线程基础** - 在Java中,多线程是指在运行时能够同时执行多个任务的能力。 - Java提供`Thread`类作为创建线程的基础,还可以通过实现`Runnable`接口来定义线程的任务。 2. **线程同步** - 当多个线程试图...

    Java 开发JAVA多线程与线程安全实践-基于Http协议的断点续传.rar

    总结,学习Java多线程和线程安全是提升程序性能和并发能力的基础,而结合Http协议的断点续传技术则能有效地处理大文件传输,提供更优秀的用户体验。通过深入理解和实践这些知识点,开发者可以构建更加稳定、高效的...

    JAVA源码JAVA多线程与线程安全实践-基于Http协议的断点续传

    #### 多线程基础 在深入探讨如何实现基于HTTP协议的断点续传之前,我们首先需要了解一些关于Java多线程的基础知识。Java多线程是Java编程语言的一个核心特性,它允许同时执行多个线程来提高程序的性能。线程是一种...

    java线程 线程学习资料 java线程教程

    - **目标读者**: 本教程主要面向具备丰富Java基础知识但缺乏多线程编程经验的学习者。 - **学习成果**: 学习者能够掌握编写简单的多线程程序的能力,并能够理解和分析使用线程的基本程序。 #### 二、线程基础知识 -...

Global site tag (gtag.js) - Google Analytics