线程锁技术
程序员文章站
2022-05-22 12:34:09
...
Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象。两个线程执行的代码片段要实现同步互斥的效果,他们必须用同一个Lock对象,锁是上在代表要操作的资源的类的内部方法中,而不是线程代码中。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest {
public static void main(String[] args) {
new LockTest().init();
}
public void init(){
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("zhangxiaoxiang");
}
}
}).start();
new Thread(new Runnable(){
@Override
public void run() {
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("liwenfei");
}
}
}).start();
}
/**
* 会出现线程安全的问题
*
*/
/* class Outputer{
public void output(String name){
int len = name.length();
for(int i=0; i<len; i++){
System.out.print(name.charAt(i));
}
System.out.println();//换行
}
}*/
static class Outputer{
Lock lock = new ReentrantLock();
public void output(String name){
int len = name.length();
lock.lock();
try{//使用try是确保里边的代码不管有没有出错,最后都要打开锁
for(int i=0; i<len; i++){
System.out.print(name.charAt(i));
}
System.out.println();//换行
}finally{
lock.unlock();
}
}
/**
* 方法二:把要同步的代码所在方法全部同步
* 在方法上使用synchronized后不要在内部再使用synchronized,不然很容易出现死锁
*/
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的,那么为了确保不管谁调用同步方法,锁对象是同一个,
* 那么锁只能为当前类的字节码对象,即Outputer.class
* @param name
*/
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();//换行
}
}
}
Lock分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥。这是由jvm自己控制的,你只要上好相应的锁即可。如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;如果你的代码修改数据,只能有一个人在写,且不能同时读数据,那就上写锁。总之读的时候上读锁,写的时候上写锁。提高性能。
import java.util.Random;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockTest {
public static void main(String[] args) {
final Queue3 q3 = new Queue3();
for (int i = 0; i < 3; i++) {
new Thread() {
@Override
public void run() {
while (true) {
q3.get();
}
}
}.start();
new Thread() {
public void run() {
while (true) {
q3.put(new Random().nextInt(10000));
}
};
}.start();
}
}
}
class Queue3 {
private Object data = null; // 共享数据,只能又一个线程能写该数据,但是可以有多个线程可以读
private ReentrantReadWriteLock 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() + " hava read data : " + data);
} catch (Exception e) {
e.printStackTrace();
} finally {
rwl.readLock().unlock(); // 使用finally不管出现什么意外,一定要将锁释放掉
}
}
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() + " hava writed data : " + data);
} catch (Exception e) {
e.printStackTrace();
} finally {
rwl.writeLock().unlock();
}
}
}
在jdk api java.util.concurrent.locks.ReentrantReadWriteLock类中有个缓存器设计的伪代码示例:
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();
}
}
缓存的思想:
上一篇: 用Matlab画出相图进行相平面分析
下一篇: 线程池原理