`
zhangwei_david
  • 浏览: 468809 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

java多线程lock详解

阅读更多

    Lock是JDK1.5种新增的同步工具,其实真正的实现Lock接口的类就三个,ReentrantLockReentrantReadWriteLock的两个内部类(ReadLockWriteLock实现了Lock的接口);

          ReentrantLock 实现了标准的互斥操作,也就是一次只能有一个线程持有锁,也即所谓独占锁的概念。我们也一直在强调这个特点。显然这个特点在一定程度上面减低了吞吐量,实际上独占锁是一种保守的锁策略,在这种情况下任何“读/读”,“写/读”,“写/写”操作都不能同时发生。

public class ReentrantLockTest {
	// 公平锁
	private Lock lock = new ReentrantLock(true);

	// 资源
	private Resource resource = new Resource();

	public static void main(String[] args) {
		final ReentrantLockTest test = new ReentrantLockTest();
		ExecutorService service = Executors.newCachedThreadPool();
		for (int i = 0; i < 20; i++) {
			service.submit(new Runnable() {

				@Override
				public void run() {
					Random random = ThreadLocalRandom.current();

					test.write(random.nextInt());
				}
			});

			service.submit(new Runnable() {

				@Override
				public void run() {
					test.read();
				}
			});
		}
		service.shutdown();
	}

	private void write(final int value) {
		// 如果可以获取锁
		if (lock.tryLock()) {
			try {
				// 执行业务逻辑
				System.out.println(Thread.currentThread().getName() + "获取了锁 写入 value=" + value);

				resource.setValue(String.valueOf(value));

			} finally {
				// 释放锁
				System.out.println(Thread.currentThread().getName() + "释放了锁");
				lock.unlock();
			}
		}
	}

	void read() {
		if (lock.tryLock()) {
			try {
				System.out.println(Thread.currentThread().getName() + "获取了锁 value=" + resource.getValue());
			} finally {
				System.out.println(Thread.currentThread().getName() + "释放了锁");
				lock.unlock();
			}
		}
	}
}

 

 

 

   测试的结果是:

 

pool-1-thread-1获取了锁 写入 value=2111184282
pool-1-thread-1释放了锁
pool-1-thread-2获取了锁 value=2111184282
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 写入 value=-1679499784
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 写入 value=1678350750
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 value=1678350750
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 value=1678350750
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 写入 value=152101866
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 value=152101866
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 写入 value=1147384711
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 value=1147384711
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 写入 value=-2130483959
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 value=-2130483959
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 写入 value=417404714
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 value=417404714
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 写入 value=-405288963
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 value=-405288963
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 写入 value=-139338780
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 value=-139338780
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 写入 value=-745627488
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 value=-745627488
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 写入 value=-1455461843
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 value=-1455461843
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 写入 value=-1129478741
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 写入 value=-1560021948
pool-1-thread-2释放了锁

     但实际应用场景中我们会经常遇到这样的情况:某些资源需要并发访问,并且大部分时间是用来进行读操作的,写操作比较少,而锁是有一定的开销的,当并发比较大 的时候,锁的开销就比较可观了。所以如果可能的话就尽量少用锁,如果非要用锁的话就尝试看能否能实现读写分离,将其改造为读写锁。

	// 公平锁
	private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

	// 写锁
	private Lock writeLock = readWriteLock.writeLock();

	// 读锁
	private Lock readLock = readWriteLock.readLock();

	// 资源
	private Resource resource = new Resource();

	public static void main(String[] args) {
		final ReentrantReadWriteLockTest test = new ReentrantReadWriteLockTest();
		ExecutorService service = Executors.newCachedThreadPool();
		for (int i = 0; i < 10; i++) {
			service.submit(new Runnable() {

				@Override
				public void run() {
					Random random = ThreadLocalRandom.current();

					test.write(random.nextInt());
				}
			});

			service.submit(new Runnable() {

				@Override
				public void run() {
					test.read();
				}
			});
		}
		service.shutdown();
	}

	private void write(final int value) {
		// 如果可以获取锁
		if (writeLock.tryLock()) {

			try {
				// 执行业务逻辑
				System.out.println(Thread.currentThread().getName() + "获取了锁 写入 value=" + value);

				resource.setValue(String.valueOf(value));

			} finally {
				// 释放锁
				System.out.println(Thread.currentThread().getName() + "释放了锁");
				writeLock.unlock();
			}
		}
	}

	private void read() {
		if (readLock.tryLock()) {
			try {

				System.out.println(Thread.currentThread().getName() + "获取了锁 value=" + resource.getValue());
			} finally {
				System.out.println(Thread.currentThread().getName() + "释放了锁");
				readLock.unlock();
			}
		}
	}

}

 测试结果是:

pool-1-thread-1获取了锁 写入 value=-702575113
pool-1-thread-1释放了锁
pool-1-thread-1获取了锁 value=-702575113
pool-1-thread-1释放了锁
pool-1-thread-1获取了锁 写入 value=-1619148924
pool-1-thread-1释放了锁
pool-1-thread-1获取了锁 value=-1619148924
pool-1-thread-1释放了锁
pool-1-thread-1获取了锁 写入 value=-1469315956
pool-1-thread-1释放了锁
pool-1-thread-1获取了锁 value=-1469315956
pool-1-thread-1释放了锁
pool-1-thread-1获取了锁 写入 value=1667915800
pool-1-thread-1释放了锁
pool-1-thread-1获取了锁 value=1667915800
pool-1-thread-1释放了锁
pool-1-thread-1获取了锁 写入 value=-1280947014
pool-1-thread-1释放了锁
pool-1-thread-1获取了锁 value=-1280947014
pool-1-thread-1释放了锁
pool-1-thread-1获取了锁 写入 value=1208950056
pool-1-thread-1释放了锁
pool-1-thread-1获取了锁 value=1208950056
pool-1-thread-1释放了锁
pool-1-thread-1获取了锁 写入 value=1089508899
pool-1-thread-1释放了锁
pool-1-thread-1获取了锁 写入 value=760397757
pool-1-thread-1释放了锁
pool-1-thread-2获取了锁 value=760397757
pool-1-thread-2释放了锁
pool-1-thread-2获取了锁 value=760397757
pool-1-thread-1获取了锁 value=760397757
pool-1-thread-2释放了锁
pool-1-thread-1释放了锁

   可以发现写锁是独占锁,读锁是共享锁,那么读锁是不是无限共享呢?实际上不是的,最大同时可以背65534个共享。

public class ReentrantTest {

	private Lock lock = new ReentrantReadWriteLock().readLock();
	static long count = 0;

	/**
	 * 
	 * @param args
	 * @author zhangwei<wei.zw@corp.netease.com>
	 */
	public static void main(String[] args) {
		ReentrantTest test = new ReentrantTest();
		for (;;) {

			if (test.lock.tryLock()) {

				System.out.println(count++);

			}
		}
	}

}

 

65530
65531
65532
65533
65534
Exception in thread "main" java.lang.Error: Maximum lock count exceeded
	at java.util.concurrent.locks.ReentrantReadWriteLock$Sync.tryReadLock(ReentrantReadWriteLock.java:588)
	at java.util.concurrent.locks.ReentrantReadWriteLock$ReadLock.tryLock(ReentrantReadWriteLock.java:803)
	at org.demo.core.lock.ReentrantTest.main(ReentrantTest.java:34)

  锁降级  写线程获取写入锁后可以获取读取锁,然后释放写入锁,这样就从写入锁变成了读取锁,从而实现锁降级的特性。

 

public class DegradeReentrantReadWriteLockTest2 {

	// 公平锁
	private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

	// 写锁
	private Lock writeLock = readWriteLock.writeLock();

	// 读锁
	private Lock readLock = readWriteLock.readLock();

	// 资源
	private Resource resource = new Resource();

	public static void main(String[] args) {
		final DegradeReentrantReadWriteLockTest2 test = new DegradeReentrantReadWriteLockTest2();
		ExecutorService service = Executors.newCachedThreadPool();
		for (int i = 0; i < 10; i++) {
			service.submit(new Runnable() {

				@Override
				public void run() {
					Random random = ThreadLocalRandom.current();

					test.writeAndRead(random.nextInt());
				}
			});

			service.submit(new Runnable() {

				@Override
				public void run() {
					test.read();
				}
			});
		}
		service.shutdown();
	}

	private void writeAndRead(final int value) {
		// 如果可以获取锁
		try {
			try {
				// 写锁锁定
				writeLock.lock();
				// 执行业务逻辑
				System.out.println(Thread.currentThread().getName() + "获取了写锁 写入 value=" + value);

				resource.setValue(String.valueOf(value));

			} finally {
				System.out.println(Thread.currentThread().getName() + "写锁降级为读锁");
				// 读锁锁定
				readLock.lock();
				// 释放写锁
				writeLock.unlock();
			}
			System.out.println(resource.getValue());
		} finally {
			// 释放读锁
			System.out.println(Thread.currentThread().getName() + "释放了读锁");
			readLock.unlock();
		}
	}

	private void read() {
		if (readLock.tryLock()) {
			try {

				System.out.println(Thread.currentThread().getName() + "获取了读锁 value=" + resource.getValue());
			} finally {
				System.out.println(Thread.currentThread().getName() + "释放了读锁");
				readLock.unlock();
			}
		}
	}

}

 

 公平锁的实现:公平性是指最先试图获取锁的线程一定可以保证最先获取

          如果当前线程之前还有线程在等待,获取锁失败!通过队列排序保证获取锁的公平性。

    /**
     * Sync object for fair locks
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

 

 

 

 

 

 

  

1
1
分享到:
评论
4 楼 zhangwei_david 2016-03-04  
lopez 写道
楼主直接new一个接口(new Resource)?

Resource  自定义的一个普通对象
3 楼 lopez 2016-03-03  
楼主直接new一个接口(new Resource)?
2 楼 zhangwei_david 2016-03-02  
IsJiangJiaZhou 写道
和 synchronized 有什么区别

http://wujiu.iteye.com/admin/blogs/2147371
1 楼 IsJiangJiaZhou 2016-03-02  
和 synchronized 有什么区别

相关推荐

    java多线程设计模式详解(PDF及源码)

    (注意,本资源附带书中源代码可供参考) 多线程与并发处理是程序设计好坏优劣的重要课题,本书通过浅显易懂的文字与实例来介绍Java线程相关的设计模式概念,并且通过实际的Java程序范例和 UML图示来一一解说,书中...

    Java多线程详解

    Java多线程模式详解 目录: 一、漫谈UML Java语言的线程 多线程的评量标准 二、 1、Single Threaded Execution ———— 能通过这座桥的,只有一个人 2、Immutable ———— 想破坏它也没办法 3、Guarded ...

    详解Java多线程编程中CountDownLatch阻塞线程的方法

    在Java中和ReadWriteLock.ReadLock一样,CountDownLatch的本质也是一个"共享锁",这里我们就来详解Java多线程编程中CountDownLatch阻塞线程的方法:

    个人总结的深入java多线程开发

    看完《think in java》多线程章节,自己写的多线程文档,还结合了其他的相关网络资料。 线程 一. 线程池 1)为什么要使用线程池 2 2)一个具有线程池的工作队列 3 3)使用线程池的风险: 4 4)有效使用线程池的原则 5...

    JAVA高质量并发详解,多线程并发深入讲解

    本书全面解析了Java并发编程的核心概念、原理和实践,帮助读者深入理解多线程并发编程的精髓,提升编程能力和系统性能。 书中首先介绍了并发编程的基础知识,包括线程的基本概念、线程的生命周期、线程安全等问题。...

    Java多线程和并发知识整理

    1.1为什么需要多线程 1.2不安全示例 1.3并发问题的根源 1.4JMM 1.5线程安全的分类 1.6线程安全的方法 二、线程基础 2.1状态 2.2使用方式 2.3基础机制 2.4中断 2.5互斥同步 2.6线程合作 三、...

    Java多线程中ReentrantLock与Condition详解

    主要介绍了Java多线程中ReentrantLock与Condition详解,需要的朋友可以参考下

    Java高并发编程详解:多线程与架构设计 (Java核心技术系列)

    部分主要阐述Thread的基础知识,详细介绍线程的API使用、线程安全、线程间数据通信,以及如何保护共享资源等内容,它是深入学习多线程内容的基础。 第二部分引入了ClassLoader,这是因为ClassLoader与线程不无关系,...

    Java 并发核心编程

    这篇指南主要是为帮助java多线程开发人员理解并发的核心概念以及如何应用这些理念。本文的主题是关于具有java语言风格的Thread、synchronized、volatile,以及J2SE5中新增的概念,如锁(Lock)、原子性(Atomics)、并发...

    【2018最新最详细】并发多线程教程

    【2018最新最详细】并发多线程教程,课程结构如下 1.并发编程的优缺点 2.线程的状态转换以及基本操作 3.java内存模型以及happens-before规则 4.彻底理解synchronized 5.彻底理解volatile 6.你以为你真的了解final吗...

    java关键字Synchronized详解

    这种机制可以有效地避免多线程环境下的数据竞争和不一致问题。 在Java中,锁膨胀(Lock Inversion)是一个重要的概念。当一个对象被多个线程同时持有锁时,可能会导致锁膨胀现象。锁膨胀是指原本互斥的两个锁,由于...

    Java基础知识点总结.docx

    十二、 多线程★★★★ 39 为什么要使用多线程 39 创建线程和启动 39 线程的生命周期 44 线程管理 45 线程同步 49 线程通信 52 线程池 58 死锁 64 线程相关类 65 十三、 同步★★★★★ 67 十四、 Lock接口 70 十五...

    Java synchronized 详解.docx

    Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。 需要明确的几个问题: synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句...

    Java并发编程原理与实战

    了解多线程所带来的安全风险.mp4 从线程的优先级看饥饿问题.mp4 从Java字节码的角度看线程安全性问题.mp4 synchronized保证线程安全的原理(理论层面).mp4 synchronized保证线程安全的原理(jvm层面).mp4 单例问题...

    龙果java并发编程完整视频

    第2节理解多线程与并发的之间的联系与区别 [免费观看] 00:11:59分钟 | 第3节解析多线程与多进程的联系以及上下文切换所导致资源浪费问题 [免费观看] 00:13:03分钟 | 第4节学习并发的四个阶段并推荐学习并发的资料 ...

    汪文君高并发编程实战视频资源下载.txt

    │ 高并发编程第一阶段05讲、采用多线程方式模拟银行排队叫号.mp4 │ 高并发编程第一阶段06讲、用Runnable接口将线程的逻辑执行单元从控制中抽取出来.mp4 │ 高并发编程第一阶段07讲、策略模式在Thread和Runnable...

    龙果 java并发编程原理实战

    第2节理解多线程与并发的之间的联系与区别 [免费观看] 00:11:59分钟 | 第3节解析多线程与多进程的联系以及上下文切换所导致资源浪费问题 [免费观看] 00:13:03分钟 | 第4节学习并发的四个阶段并推荐学习并发的资料 ...

    JAVA核心知识点整理(有效)

    25 JAVA8 与元数据.................................................................................................................................25 2.4. 垃圾回收与算法 .................................

    java面试题,180多页,绝对良心制作,欢迎点评,涵盖各种知识点,排版优美,阅读舒心

    【多线程】简述synchronized 和java.util.concurrent.locks.Lock的异同? 90 【线程】ThreadLocal的作用 90 【Spring】什么是IOC和DI?DI是如何实现的 91 【Spring】spring中的IOC(控制反转)的原理 92 【Spring】...

Global site tag (gtag.js) - Google Analytics