package com.yao; |
002 |
003 | import java.util.concurrent.ExecutorService; |
004 | import java.util.concurrent.Executors; |
005 | import java.util.concurrent.Future; |
006 | import java.util.concurrent.locks.Lock; |
007 | import java.util.concurrent.locks.ReadWriteLock; |
008 | import java.util.concurrent.locks.ReentrantLock; |
009 | import java.util.concurrent.locks.ReentrantReadWriteLock; |
010 |
011 | /** |
012 |
* Lockers |
013 |
* 在多线程编程里面一个重要的概念是锁定,如果一个资源是多个线程共享的,为了保证数据的完整性, |
014 |
* 在进行事务性操作时需要将共享资源锁定,这样可以保证在做事务性操作时只有一个线程能对资源进行操作, |
015 |
* 从而保证数据的完整性。在5.0以前,锁定的功能是由Synchronized关键字来实现的。 |
016 |
*/ |
017 | public class Lockers { |
018 | |
019 | /** |
020 | * 测试Lock的使用。在方法中使用Lock,可以避免使用Synchronized关键字。 |
021 | */ |
022 | public static class LockTest { |
023 |
024 | Lock lock = new ReentrantLock(); // 锁 |
025 | double value = 0d; // 值 |
026 | int addtimes = 0 ; |
027 |
028 | /** |
029 | * 增加value的值,该方法的操作分为2步,而且相互依赖,必须实现在一个事务中 |
030 | * 所以该方法必须同步,以前的做法是在方法声明中使用Synchronized关键字。 |
031 | */ |
032 | public void addValue( double v) { |
033 | lock.lock(); // 取得锁 |
034 | System.out.println( "LockTest to addValue: " + v + " " |
035 | + System.currentTimeMillis()); |
036 | try { |
037 | Thread.sleep( 1000 ); |
038 | } catch (InterruptedException e) { |
039 | } |
040 | this .value += v; |
041 | this .addtimes++; |
042 | lock.unlock(); // 释放锁 |
043 | } |
044 |
045 | public double getValue() { |
046 | return this .value; |
047 | } |
048 | } |
049 | public static void testLockTest() throws Exception{ |
050 | final LockTest lockTest = new LockTest(); |
051 | // 新建任务1,调用lockTest的addValue方法 |
052 | Runnable task1 = new Runnable(){ |
053 | public void run(){ |
054 | lockTest.addValue( 55.55 ); |
055 | } |
056 | }; |
057 | // 新建任务2,调用lockTest的getValue方法 |
058 | Runnable task2 = new Runnable(){ |
059 | public void run(){ |
060 | System.out.println( "value: " + lockTest.getValue()); |
061 | } |
062 | }; |
063 | // 新建任务执行服务 |
064 | ExecutorService cachedService = Executors.newCachedThreadPool(); |
065 | Future future = null ; |
066 | // 同时执行任务1三次,由于addValue方法使用了锁机制,所以,实质上会顺序执行 |
067 | for ( int i= 0 ; i< 3 ; i++){ |
068 | future = cachedService.submit(task1); |
069 | } |
070 | // 等待最后一个任务1被执行完 |
071 | future.get(); |
072 | // 再执行任务2,输出结果 |
073 | future = cachedService.submit(task2); |
074 | // 等待任务2执行完后,关闭任务执行服务 |
075 | future.get(); |
076 | cachedService.shutdownNow(); |
077 | } |
078 | |
079 | /** |
080 | * ReadWriteLock内置两个Lock,一个是读的Lock,一个是写的Lock。 |
081 | * 多个线程可同时得到读的Lock,但只有一个线程能得到写的Lock, |
082 | * 而且写的Lock被锁定后,任何线程都不能得到Lock。ReadWriteLock提供的方法有: |
083 | * readLock(): 返回一个读的lock |
084 | * writeLock(): 返回一个写的lock, 此lock是排他的。 |
085 | * ReadWriteLockTest很适合处理类似文件的读写操作。 |
086 | * 读的时候可以同时读,但不能写;写的时候既不能同时写也不能读。 |
087 | */ |
088 | public static class ReadWriteLockTest{ |
089 | // 锁 |
090 | ReadWriteLock lock = new ReentrantReadWriteLock(); |
091 | // 值 |
092 | double value = 0d; |
093 | int addtimes = 0 ; |
094 | |
095 | /** |
096 | * 增加value的值,不允许多个线程同时进入该方法 |
097 | */ |
098 | public void addValue( double v) { |
099 | // 得到writeLock并锁定 |
100 | Lock writeLock = lock.writeLock(); |
101 | writeLock.lock(); |
102 | System.out.println( "ReadWriteLockTest to addValue: " + v + " " |
103 | + System.currentTimeMillis()); |
104 | try { |
105 | Thread.sleep( 1000 ); |
106 | } catch (InterruptedException e) { |
107 | } |
108 | try { |
109 | // 做写的工作 |
110 | this .value += v; |
111 | this .addtimes++; |
112 | } finally { |
113 | // 释放writeLock锁 |
114 | writeLock.unlock(); |
115 | } |
116 | } |
117 | /** |
118 | * 获得信息。当有线程在调用addValue方法时,getInfo得到的信息可能是不正确的。 |
119 | * 所以,也必须保证该方法在被调用时,没有方法在调用addValue方法。 |
120 | */ |
121 | public String getInfo() { |
122 | // 得到readLock并锁定 |
123 | Lock readLock = lock.readLock(); |
124 | readLock.lock(); |
125 | System.out.println( "ReadWriteLockTest to getInfo " |
126 | + System.currentTimeMillis()); |
127 | try { |
128 | Thread.sleep( 1000 ); |
129 | } catch (InterruptedException e) { |
130 | } |
131 | try { |
132 | // 做读的工作 |
133 | return this .value + " : " + this .addtimes; |
134 | } finally { |
135 | // 释放readLock |
136 | readLock.unlock(); |
137 | } |
138 | } |
139 | } |
140 | |
141 | public static void testReadWriteLockTest() throws Exception{ |
142 | final ReadWriteLockTest readWriteLockTest = new ReadWriteLockTest(); |
143 | // 新建任务1,调用lockTest的addValue方法 |
144 | Runnable task_1 = new Runnable(){ |
145 | public void run(){ |
146 | readWriteLockTest.addValue( 55.55 ); |
147 | } |
148 | }; |
149 | // 新建任务2,调用lockTest的getValue方法 |
150 | Runnable task_2 = new Runnable(){ |
151 | public void run(){ |
152 | System.out.println( "info: " + readWriteLockTest.getInfo()); |
153 | } |
154 | }; |
155 | // 新建任务执行服务 |
156 | ExecutorService cachedService_1 = Executors.newCachedThreadPool(); |
157 | Future future_1 = null ; |
158 | // 同时执行5个任务,其中前2个任务是task_1,后两个任务是task_2 |
159 | for ( int i= 0 ; i< 2 ; i++){ |
160 | future_1 = cachedService_1.submit(task_1); |
161 | } |
162 | for ( int i= 0 ; i< 2 ; i++){ |
163 | future_1 = cachedService_1.submit(task_2); |
164 | } |
165 | // 最后一个任务是task_1 |
166 | future_1 = cachedService_1.submit(task_1); |
167 | // 这5个任务的执行顺序应该是: |
168 | // 第一个task_1先执行,第二个task_1再执行;这是因为不能同时写,所以必须等。 |
169 | // 然后2个task_2同时执行;这是因为在写的时候,就不能读,所以都等待写结束, |
170 | // 又因为可以同时读,所以它们同时执行 |
171 | // 最后一个task_1再执行。这是因为在读的时候,也不能写,所以必须等待读结束后,才能写。 |
172 | |
173 | // 等待最后一个task_2被执行完 |
174 | future_1.get(); |
175 | cachedService_1.shutdownNow(); |
176 | } |
177 |
178 | public static void main(String[] args) throws Exception{ |
179 | Lockers.testLockTest(); |
180 | System.out.println( "---------------------" ); |
181 | Lockers.testReadWriteLockTest(); |
182 | } |
183 | } |