1.内置锁:
(1)原理:通过内部的一个叫做监视器锁的原理来实现的,但是监视器锁本质又是依赖于底层的操作系统的Mutes Lock来实现的,操作系统之间实现线程的切换需要从用户态转换到核心态,这个成本非常高,状态之间转换需要很长的时间,所以内置锁效率较低。
(2)如何加锁和释放锁:
锁对象越小越好
内置锁获得锁和释放锁是隐式的,进入synchronized修饰的代码块就获得锁,走出就释放锁
(3)通信
wait(),notify()
wait()会立刻释放当前的锁,并进入等待状态,等待到相应的notify并重新获得锁过后才能继续执行;notify()不会立刻释放锁,必须要等待notify所在线程执行完synchronized块中的所有代码才会释放
(4)编码
模式较为简单,不必显式的加锁释放锁
(5)灵活性
一旦进入等待状态没既不能中断也不能取消,容易产生饥饿和死锁的问题
在线程调用notify方法时,会随机选择相应的对象的等待队列的一个线程将其唤醒,而不是按照FIFO(先入先出队列)。
(6)性能
与显示锁相效率较低,但是java6以后性能差别不大
2.显式锁
参考原文:http://blog.csdn.net/ghsau/article/details/7461369/
在一些内置锁无法满足需求的情况下,显式锁可以作为一种高级工具。当需要一些高级功能时才应该使用ReentrantLock,这些功能包括:可定时,可轮询,可中断,公平队列,及非块结构的锁。否则还是应该优先使用synchronized。
读写锁: ReadWriteLock
class Data {
private int data;// 共享数据
private ReadWriteLock rwl = new ReentrantReadWriteLock();
public void set(int data) {
rwl.writeLock().lock();// 取到写锁
try {
System.out.println(Thread.currentThread().getName() + "准备写入数据");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.data = data;
System.out.println(Thread.currentThread().getName() + "写入" + this.data);
} finally {
rwl.writeLock().unlock();// 释放写锁
}
}
public void get() {
rwl.readLock().lock();// 取到读锁
try {
System.out.println(Thread.currentThread().getName() + "准备读取数据");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "读取" + this.data);
} finally {
rwl.readLock().unlock();// 释放读锁
}
}
}
public static void main(String[] args) {
final Data data = new Data();
for (int i = 0; i < 3; i++) {
new Thread(new Runnable() {
public void run() {
for (int j = 0; j < 5; j++) {
data.set(new Random().nextInt(30));
}
}
}).start();
}
for (int i = 0; i < 3; i++) {
new Thread(new Runnable() {
public void run() {
for (int j = 0; j < 5; j++) {
data.get();
}
}
}).start();
}
}