您的位置:首页 > 编程语言 > Java开发

关于Java多线程和并发运行的学习(三)

2014-09-11 13:33 316 查看
开学了,早上有课,下午就没课了。所以只有下午才能继续学习咯~~

昨天下午对简单的线程池了解了一下,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;// 返回此条线程得到的数据
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: