关于Java多线程和并发运行的学习(三)
2014-09-11 13:33
316 查看
开学了,早上有课,下午就没课了。所以只有下午才能继续学习咯~~
昨天下午对简单的线程池了解了一下,Lock以及读写锁,缓存系统构成的大概原理,最后是Future和Callable(其实我还不知道最后两个到底有什么,不过作为知识点,我还是了解了一下。)
上一偏的知识点是关于线程中的线程范围数据共享,ThreadLocal以及多线程数据共享。
二,关于Lock以及读写锁
Lock & Condition实现线程同步通信:
Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象。两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象。锁是上在代表 要操作的 资源的 类的 内部方法 中,而不是线程代码中!!这里贴出Lock与synchronized的对比代码,这里引用的是synchronized例子的代码,只是在同步锁的地方将synchronized替换为Lock。代码如下:
在读写锁中,分读锁readLock和写锁writeLock,读读不用上锁,读写和写写需要上锁。
里面有一个例子,如何利用重入来执行升级缓存后的锁降级:
在使用某些种类的 Collection 时,可以使用 ReentrantReadWriteLock 来提高并发性。通常,在预期 collection 很大,读取者线程访问它的次数多于写入者线程,并且 entail 操作的开销高于同步开销时,这很值得一试。例如,以下是一个使用 TreeMap 的类,预期它很大,并且能被同时访问。
实现注意事项:此锁最多支持 65535 个递归写入锁和 65535 个读取锁。试图超出这些限制将导致锁方法抛出
那么实现系统缓存,代码如下:
昨天下午对简单的线程池了解了一下,Lock以及读写锁,缓存系统构成的大概原理,最后是Future和Callable(其实我还不知道最后两个到底有什么,不过作为知识点,我还是了解了一下。)
上一偏的知识点是关于线程中的线程范围数据共享,ThreadLocal以及多线程数据共享。
一,关于线程池
这里我们会使用到接口 ExecutorService,这个接口比较繁琐。不懂请查阅API文档。<span style="font-size:14px;">package Multithreading; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class ThreadPoolTest { public static void main(String[] args) { //ExecutorService threadPool = Executors.newFixedThreadPool(3);//创建3个线程(创建固定大小的线程池) //ExecutorService threadPool = Executors.newCachedThreadPool();//动态线程,需要多少给多少(创建缓存线程池) ExecutorService threadPool = Executors.newSingleThreadExecutor();//创建单一线程池,如果这个池子里的线程死了,它就再生成一个线程,总之要保持一个线程(如何实现线程低调后重新启动?) for (int i = 1; i <= 10; i++) { final int task = i; threadPool.execute(new Runnable() { public void run() { for (int j = 1; j <= 10; j++) { try { Thread.sleep(30); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " is looping of " + j + " for task of " + task); } } }); } System.out.println("all of 10 tasks have committed!"); threadPool.shutdown();//运行后关闭线程 //threadPool.shutdownNow();//立即关闭线程 Executors.newScheduledThreadPool(3).schedule(new Runnable() { public void run() { System.out.println("bombing!"); } }, 10, TimeUnit.SECONDS); } }</span>
二,关于Lock以及读写锁
Lock & Condition实现线程同步通信:Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象。两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象。锁是上在代表 要操作的 资源的 类的 内部方法 中,而不是线程代码中!!这里贴出Lock与synchronized的对比代码,这里引用的是synchronized例子的代码,只是在同步锁的地方将synchronized替换为Lock。代码如下:
package Multithreading; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockTest { public static void main(String[] args) { new LockTest().init(); } // 要想创建内部类的实体对象,就必须有一个外部类的实体对象 private void init() { final Outputer outputer = new Outputer(); new Thread(new Runnable() { public void run() { while (true) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } outputer.output("zhangxiaoxiang"); } } }).start(); new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } outputer.output("lihongming"); } } }).start(); } static class Outputer { Lock lock = new ReentrantLock();// 制造一把锁 public void output(String name) { int len = name.length(); lock.lock();//锁上 try { for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } finally { lock.unlock();//开锁 } } public synchronized void output2(String name) { int len = name.length(); for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } // static方法的线程锁 public static synchronized void output3(String name) { int len = name.length(); for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } } }
在读写锁中,分读锁readLock和写锁writeLock,读读不用上锁,读写和写写需要上锁。
package Multithreading; import java.util.Random; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadWriteLockTest { public static void main(String[] args) { final Queue q = new Queue(); for (int i = 0; i < 3; i++) {//创建3个线程 new Thread() { public void run() { while (true) { q.get();//读线程 } } }.start(); new Thread() { public void run() { while (true) { q.put(new Random().nextInt(10000));//写线程 } } }.start(); } } } class Queue { private Object data = null;// 共享数据,只能有一个线程能写该数据,但可以有多个线程读该数据 private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();//创建读写锁 public void get() { rwl.readLock().lock();//上读锁 System.out.println(Thread.currentThread().getName() + " be ready to read data ! "); try { Thread.sleep((long) (Math.random() * 1000)); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " have read data :" + data); rwl.readLock().unlock();//解读锁 } public void put(Object data) { rwl.writeLock().lock(); System.out.println(Thread.currentThread().getName() + " be ready to write data !"); try { Thread.sleep((long) (Math.random() * 1000)); } catch (InterruptedException e) { e.printStackTrace(); } this.data = data; System.out.println(Thread.currentThread().getName() + " have write data :" + data); rwl.writeLock().unlock(); } }
三,关于系统缓存
这里可以查询API手册里的ReentrantReadWriteLock,里面有一个例子,如何利用重入来执行升级缓存后的锁降级:
<span style="font-size:14px;">class CachedData { Object data; volatile boolean cacheValid; ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); void processCachedData() { rwl.readLock().lock(); if (!cacheValid) { // Must release read lock before acquiring write lock rwl.readLock().unlock(); rwl.writeLock().lock(); // Recheck state because another thread might have acquired // write lock and changed state before we did. if (!cacheValid) { data = ... cacheValid = true; } // Downgrade by acquiring read lock before releasing write lock rwl.readLock().lock(); rwl.writeLock().unlock(); // Unlock write, still hold read } use(data); rwl.readLock().unlock(); } } </span>
在使用某些种类的 Collection 时,可以使用 ReentrantReadWriteLock 来提高并发性。通常,在预期 collection 很大,读取者线程访问它的次数多于写入者线程,并且 entail 操作的开销高于同步开销时,这很值得一试。例如,以下是一个使用 TreeMap 的类,预期它很大,并且能被同时访问。
class RWDictionary { private final Map<String, Data> m = new TreeMap<String, Data>(); private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); private final Lock r = rwl.readLock(); private final Lock w = rwl.writeLock(); public Data get(String key) { r.lock(); try { return m.get(key); } finally { r.unlock(); } } public String[] allKeys() { r.lock(); try { return m.keySet().toArray(); } finally { r.unlock(); } } public Data put(String key, Data value) { w.lock(); try { return m.put(key, value); } finally { w.unlock(); } } public void clear() { w.lock(); try { m.clear(); } finally { w.unlock(); } } }
实现注意事项:此锁最多支持 65535 个递归写入锁和 65535 个读取锁。试图超出这些限制将导致锁方法抛出错误
那么实现系统缓存,代码如下:package Multithreading; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * 写一个缓存系统 */ public class CacheDemo { private Map<String, Object> cache = new HashMap<String, Object>(); public static void main(String[] args) { } private ReadWriteLock rwl = new ReentrantReadWriteLock(); public Object getData(String key) { rwl.readLock().lock();//读锁 Object value = null; try { value = cache.get(key); if (value == null) {// 读到的结果如果为空,则需要写入 rwl.readLock().unlock();// 先解开读锁 /* * 此时这里会再次进入多个线程 */ rwl.writeLock().lock();// 再锁上写锁,只允许一个线程进,其他线程堵在锁外 try { if (value == null) {//如果检查得到进入的线程为空,则从数据库写入数据;如果不为空,则不用再次写入数据,因为缓存value中含有其对应的值 value = "aaaa";// 实际是去queryDB();缓存进cache中 } } finally { rwl.writeLock().unlock();// 解开写锁 } rwl.readLock().lock();// 恢复读锁 } } finally { rwl.readLock().unlock();// 此条线程读完后,解开读锁 } return value;// 返回此条线程得到的数据 } }
相关文章推荐
- 关于Java多线程和并发运行的学习(五)——同步集合类
- 关于Java多线程和并发运行的学习(四)——之CyclicBarrier
- 关于Java多线程和并发运行的学习(一)
- 关于Java多线程和并发运行的学习(五)——并发 Collection
- 关于Java多线程和并发运行的学习(四)——之Semaphore
- 关于Java多线程和并发运行的学习(四)——之Condition
- 关于Java多线程和并发运行的学习(二)
- 关于Java多线程和并发运行的学习(五)——阻塞队列
- 关于Java多线程和并发运行的学习(四)——之CountDownLatch
- 关于Java多线程和并发运行的学习(四)——之Exchanger
- 关于java并发锁的学习总结
- 关于Android SQLite3多线程并发问题,学习笔记。
- Java多线程编程--(8)学习Java5.0 并发编程包--线程池、Callable & Future 简介
- 【学习anroid编译】关于运行android模拟器
- Java多线程编程--(9)学习Java5.0 并发编程包--线程工具类
- C#学习小记7一个多载,及我想实验关于抽象类能否直接编译运行
- Java多线程和并发编程实践学习总结---提高篇1
- 【JAVA基础】关于JAVA中的static方法、并发问题以及JAVA运行时内存模型
- JAVA多线程与并发学习总结
- 关于JAVA多线程并发synchronized的测试与合理使用