------- android培训、java培训、期待与您交流! ----------
读写锁:
分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥,也就是说可以多个线程在读取数据,不能一边在读取数据一边在写入数据,也不能一个线程在写另一个线程也在写,保证了数据的完整性。
创建读写锁:
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
设置、释放读写锁:
rwl.readLock().lock(); 设置读锁
rwl.readLock().unlock(); 释放读锁
rwl.writeLock().lock();设置写锁
rwl.writeLock().unlock(); 释放写锁
注意:释放锁的操作必须放到finally中,即使前面的代码发生异常也要释放资源。
1 public static void main(String[] args) { 2 // 创建线程数固定为5的线程池 3 ExecutorService threadPool = Executors.newFixedThreadPool(5); 4 //内部类在局部时只能访问被final修饰的局部变量 5 final ReadWrite rw = new ReadWrite(); 6 for (int i = 0; i < 10; i++) { 7 //内部类在局部时只能访问被final修饰的局部变量 8 final int num = i; 9 //添加任务10 threadPool.execute(new Runnable() {11 12 @Override13 public void run() {14 //交替执行读写操作15 if (num % 2 == 0) {16 rw.write("ssssss");17 }else{18 rw.read();19 }20 }21 });22 }23 }24 25 static class ReadWrite {26 private String value;27 // 创建读写锁28 private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();29 30 // 读31 public void read() {32 //设置读锁33 rwl.readLock().lock();34 try {35 36 System.out.println(Thread.currentThread().getName() + "read data ");37 System.out.println(value);38 System.out.println("end read data");39 } finally {40 41 //释放读锁42 //释放锁操作一定要放在finally中,因为前面有可能发生异常,如果没放在finally中就不能释放资源43 rwl.readLock().unlock();44 }45 }46 47 // 写48 public void write(String num) {49 //设置写锁50 rwl.writeLock().lock();51 try {52 System.out.println(Thread.currentThread().getName() +"write data begin!");53 Thread.sleep(10);54 value = num;55 System.out.println(Thread.currentThread().getName() +"write data ........end!");56 }catch(Exception e){57 58 } finally {59 //释放写锁60 rwl.writeLock().unlock();61 }62 }63 }
缓存类
1 package cn.itcst.day5; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 import java.util.concurrent.ExecutorService; 6 import java.util.concurrent.Executors; 7 import java.util.concurrent.locks.ReadWriteLock; 8 import java.util.concurrent.locks.ReentrantReadWriteLock; 9 10 /**11 * 缓存类12 * 13 * @author Administrator14 * 15 */16 public class CacheDemo {17 18 public static void main(String[] args) {19 // TODO Auto-generated method stub20 //创建线程池21 ExecutorService threadPool = Executors.newFixedThreadPool(3);22 for (int i = 0; i < 3; i++) {23 threadPool.execute(new Runnable() {24 25 @Override26 public void run() {27 // 调用方法28 System.out.println("main方法中输出:" + get());29 30 }31 });32 }33 34 }35 static Mapmap = new HashMap ();36 public static String get() {37 // 创建读写锁38 ReadWriteLock rwl = new ReentrantReadWriteLock();39 String name;40 // 设置读锁41 rwl.readLock().lock();42 try {43 //每次获得都是为null!!!!44 name = map.get("id");45 System.out.println("重新进入方法集合的长度为:" + map.size());46 try {47 Thread.sleep(200);48 } catch (InterruptedException e) {49 // TODO Auto-generated catch block50 e.printStackTrace();51 }52 if (name == null) {53 // 如果name为null就释放读锁54 rwl.readLock().unlock();55 // 设置写锁,进行赋值56 rwl.writeLock().lock();57 try {58 //每个线程都会进来!!!!59 System.out.println(Thread.currentThread().getName() + "--jin--"60 + name);61 // 赋值并存入集合62 name = "zhangsan";63 map.put("id", name);64 System.out.println("在把数据存入集合后根据ID获得name: " + map.get("id"));65 } finally {66 rwl.readLock().lock();67 // 释放写锁68 rwl.writeLock().unlock();69 }70 }71 72 } finally {73 // 释放读锁74 rwl.readLock().unlock();75 }76 return name;77 }78 79 }
控制台输出:
重新进入方法集合的长度为:0
重新进入方法集合的长度为:0重新进入方法集合的长度为:0pool-1-thread-3--jin--null在把数据存入集合后根据ID获得name: zhangsanmain方法中输出:zhangsanpool-1-thread-2--jin--null在把数据存入集合后根据ID获得name: zhangsanmain方法中输出:zhangsanpool-1-thread-1--jin--null在把数据存入集合后根据ID获得name: zhangsanmain方法中输出:zhangsan在上面代码中有个问题没有解决,单纯的在读锁中读取和切换到写锁中写数据是没有问题的,但是我想在第一次写入之后存入集合避免以后进来都创建锁、写数据一系列动作,每次存到map的数据在再次访问的时候都没有了,本来我以为是线程太快排进去了,就在run方法中叫了sleep(),但是还是不行。
最后我在分配任务前也加了sleep(),居然就正常了,由此推断还是因为运行得太快全部都排进去了,但是不知道为什么在Run方法中调用方法前sleep()没有作用,或许是线程池分配任务机制有所不同
1 public static void main(String[] args) { 2 // TODO Auto-generated method stub 3 //创建线程池 4 ExecutorService threadPool = Executors.newFixedThreadPool(3); 5 for (int i = 0; i < 3; i++) { 6 try { 7 Thread.sleep(2000); 8 } catch (InterruptedException e) { 9 // TODO Auto-generated catch block10 e.printStackTrace();11 }12 threadPool.execute(new Runnable() {13 14 @Override15 public void run() {16 // 调用方法17 System.out.println("main方法中输出:" + get());18 19 }20 });21 }22 23 24 }25 static Mapmap = new HashMap ();26 public static String get() {27 // 创建读写锁28 ReadWriteLock rwl = new ReentrantReadWriteLock();29 String name;30 // 设置读锁31 32 rwl.readLock().lock();33 try {34 35 name = map.get("id");36 System.out.println("重新进入方法集合的长度为:" + map.size());37 try {38 Thread.sleep(200);39 } catch (InterruptedException e) {40 // TODO Auto-generated catch block41 e.printStackTrace();42 }43 if (name == null) {44 // 如果name为null就释放读锁45 rwl.readLock().unlock();46 // 设置写锁,进行赋值47 rwl.writeLock().lock();48 try {49 System.out.println(Thread.currentThread().getName() + "--jin--"50 + name);51 // 赋值并存入集合52 name = "zhangsan";53 map.put("id", name);54 System.out.println("在把数据存入集合后根据ID获得name: " + map.get("id"));55 } finally {56 rwl.readLock().lock();57 // 释放写锁58 rwl.writeLock().unlock();59 }60 }61 62 } finally {63 // 释放读锁64 rwl.readLock().unlock();65 }66 return name;67 }
控制台输出:
重新进入方法集合的长度为:0
pool-1-thread-1--jin--null在把数据存入集合后根据ID获得name: zhangsanmain方法中输出:zhangsan重新进入方法集合的长度为:1main方法中输出:zhangsan重新进入方法集合的长度为:1main方法中输出:zhangsan