普通锁(Lock)
package org.xyz.java.thread.demo07;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Lock 普通的锁是对 读-读操作、读-写操作、写-写操作都是互斥的,无论多个线程是读操作还是写操作,每次都只能有一个线程获取到锁
* @author kevin.chen
*
*/
public class LockTest {
public static void main(String[] args) {
new LockTest().init();
}
private void init() {
final Outputer outputer = new Outputer();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("kevin-chen");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("lock-test");
}
}
}).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();
}
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();
}
}
}
读写锁(ReadWriteLock)
package org.xyz.java.thread.demo07;
import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* ReadWriteLock 读写锁是对 读-读操作不互斥、读-写操作和写-写操作是互斥的,多个线程读之间不互斥,多个线程读写和写写互斥
* @author kevin.chen
*
*/
public class ReadWriteLockTest {
public static void main(String[] args) {
final MyData md = new MyData();
for(int i=0; i<3; i++) {
new Thread(new Runnable() {
@Override
public void run() {
while(true)
md.get();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true)
md.put(new Random().nextInt(10000));
}
}).start();
}
}
static class MyData {
//共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。
private Object data = null;
// 这里使用读写锁可以保证读-读之间不互斥
private ReadWriteLock rwl = new ReentrantReadWriteLock();
// 取数据
public void get() {
// 上读锁
rwl.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " be ready to read data!");
Thread.sleep((long)(Math.random()*1000));
System.out.println(Thread.currentThread().getName() + " have read data :" + data);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
rwl.readLock().unlock();
}
}
// 放数据
public void put(Object data) {
// 上写锁
rwl.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " be ready to write data!");
Thread.sleep((long)(Math.random()*1000));
this.data = data;
System.out.println(Thread.currentThread().getName() + " have write data: " + data);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
rwl.writeLock().unlock();
}
}
}
}
使用锁来实现缓存案例
package org.xyz.java.thread.demo07;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 使用读写锁实现多个线程操作缓存相关的数据
* @author kevin.chen
*
*/
public class CacheDemo {
// 这里使用map来存放数据充当缓存
private static Map<String,Object> cache = new HashMap<String,Object>();
// 这个方法存在线程不安全的问题,当多个线程同时来操作数据时,有的读有的写就会出现脏数据的情况
public Object getData(String key) {
Object value = cache.get(key);
if(value == null) {
// 如果缓存中没有查询到数据,那就去数据库或外部存储系统中获取
value = "kevin.chen"; // 这里略去查询的操作
// 将获取的数据放入到缓存中,其他线程来获取的时候就不用再查询了
cache.put(key, value);
}
return value;
}
// 使用同步关键字来保证线程的安全性,但是性能不是很好
public synchronized Object getData1(String key) {
// 从缓存获取数据
Object value = cache.get(key);
if(value == null) {
// 如果缓存中没有查询到数据,那就去数据库或外部存储系统中获取
value = "kevin.chen"; // 这里略去查询的操作
// 将获取的数据放入到缓存中,其他线程来获取的时候就不用再查询了
cache.put(key, value);
}
return value;
}
// 使用Lock锁来保证线程的安全性,但是性能不是很好
private Lock lock = new ReentrantLock();
public Object getData2(String key) {
lock.lock();
Object value = null;
try {
// 从缓存获取数据
value = cache.get(key);
if(value == null) {
// 如果缓存中没有查询到数据,那就去数据库或外部存储系统中获取
value = "kevin.chen"; // 这里略去查询的操作
// 将获取的数据放入到缓存中,其他线程来获取的时候就不用再查询了
cache.put(key, value);
}
}finally {
lock.unlock();
}
return value;
}
// 使用同步关键字来保证线程的安全性,但是性能不是很好
private ReadWriteLock rwl = new ReentrantReadWriteLock();
public Object getData3(String key) {
// 先获取读锁,并对读锁进行加锁,读锁对多个线程来说是不互斥的,多个线程可以同时获取到读锁
rwl.readLock().lock();
Object value = null;
try {
// 从缓存获取数据
value = cache.get(key);
if(value == null) {
// 如果缓存中没有查询到数据,那就去数据库或外部存储系统中获取
// 此时立刻是否读锁,立刻获取写锁并对写进行加锁
rwl.readLock().unlock();
// 写锁互斥,只能有一个线程拿到锁,保证线程安全
rwl.writeLock().lock();
try {
// 这个分空判断一定要加,确保多个线程同时都执行到77行时,只去查一次数据
if(value == null ) {
value = "kevin.chen"; // 这里略去查询的操作
// 将获取的数据放入到缓存中,其他线程来获取的时候就不用再查询了
cache.put(key, value);
}
// 数据放入到缓存中后,再加上读锁
rwl.readLock().lock();
}finally {
// 数据放入到缓存中后,释放写锁
rwl.writeLock().unlock();
}
}
}finally {
// 最后释放读锁
rwl.readLock().unlock();
}
return value;
}
public static void main(String[] args) {
CacheDemo cd = new CacheDemo();
// Object obj = cd.getData("name");
// Object obj = cd.getData1("name");
// Object obj = cd.getData2("name");
Object obj = cd.getData3("name");
System.out.println(obj == null ? "数据不存在!" : obj.toString());
}
}