synchronized的优缺点
优点:
1:发生异常自动释放锁
2:书写简单
缺点:
1:无法使等待中的线程中断等待
2:无法得知线程是否成功为资源加锁了
3:无法使读操作并发进行
由此引入了Lock与ReadWriteLock接口
Lock
1:加锁的错误写法
public class Test01 {
public static void main(String[] args) {
Test01 test01 = new Test01();
new Thread(() -> {
test01.test();
}, "one").start();
new Thread(() -> {
test01.test();
},"tow").start();
}
public void test(){
Lock lock = new ReentrantLock();
lock.lock();
try {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
Thread.sleep(10);
}
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
运行结果
为什么不能同步了呢,原因是Lock的声明写在了业务方法test()里了,当两个线程运行到test()方法时,声明了两个不同的锁,这样的情况下,两个线程使用不同的锁对同一个对象加锁,是可以成功的,所以就无法同步了
将Lock对象的声明写到外面,使多线程使用的锁是同一把,它们就无法对同一个对象进行加锁了(synchronized默认使用同一把锁,这也是synchronized的优点,不用自己编程更多的代码)
2:lock()的正确写法
public class Test02 {
Lock lock = new ReentrantLock();
public static void main(String[] args) {
Test02 test02 = new Test02();
new Thread(() -> {
test02.test();
}, "one").start();
new Thread(() -> {
test02.test();
},"tow").start();
}
public void test(){
lock.lock();
try {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
Thread.sleep(10);
}
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
3:tryLock()
public class Test03 {
Lock lock = new ReentrantLock();
public static void main(String[] args) {
Test03 test03 = new Test03();
new Thread(() -> {
try {
test03.test();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"one").start();
new Thread(() -> {
try {
test03.test();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"tow").start();
}
public void test() throws InterruptedException {
if(lock.tryLock()){
try {
System.out.println(Thread.currentThread().getName()+"获得了锁");
Thread.sleep(10);
} finally {
lock.unlock();
System.out.println(Thread.currentThread().getName()+"释放了锁");
}
}
else {
System.out.println(Thread.currentThread().getName()+"加锁失败");
}
}
}
运行结果
4:lockInterruptibly():可中断锁
public class Test04 {
private Lock lock = new ReentrantLock();
public static void main(String[] args) {
Test04 test04 = new Test04();
Thread thread01 = new Thread(() -> {
try {
test04.test();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+" 被中断");
}
},"one");
Thread thread02 = new Thread(() -> {
try {
test04.test();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+" 被中断");
}
},"tow");
thread01.start();
thread02.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread02.interrupt();
}
public void test() throws InterruptedException {
lock.lockInterruptibly();
try {
System.out.println(Thread.currentThread().getName()+" 获得了锁");
Thread.sleep(1000);
}finally {
lock.unlock();
System.out.println(Thread.currentThread().getName()+" 释放了锁");
}
}
}
运行结果
ReadWriteLock
ReadWriteLock的简单使用
ReadWriteLock里面只有两个方法,readLock()与writeLock(),其主要的实现类也只有一个:ReentrantReadWriteLock
1:readLock():读锁
(1)先看普通的锁进行读操作时
public class Test05 {
Lock lock = new ReentrantLock();
public static void main(String[] args) {
Test05 test05 = new Test05();
new Thread(() -> {
test05.test();
},"one").start();
new Thread(() -> {
test05.test();
},"tow").start();
}
public void test(){
lock.lock();
try {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+" 正在进行读操作");
Thread.sleep(10);
}
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
System.out.println(Thread.currentThread().getName()+" 读操作完毕");
}
}
}
运行结果
线程按一个一个执行
(2)再看ReadWriteLock的读锁完成读操作
public class Test06 {
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public static void main(String[] args) {
Test06 Test06 = new Test06();
new Thread(() -> {
Test06.test();
},"one").start();
new Thread(() -> {
Test06.test();
},"tow").start();
}
public void test(){
readWriteLock.readLock().lock();
try {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+" 正在进行读操作");
Thread.sleep(10);
}
}catch (InterruptedException e){
e.printStackTrace();
}finally {
readWriteLock.readLock().unlock();
System.out.println(Thread.currentThread().getName()+" 读操作完毕");
}
}
}
运行结果
多个线程可以同时进行读操作,提高了读操作的效率,这就是读写锁的优越性
需要注意的是
- 一个资源被加了读锁之后,其他线程可以再对其加读锁,但不能加写锁
- 一个资源被加了写锁之后,其他线程不可以再对其加任何锁
synchronized与Lock区别及几个主要锁的概念
见开头的思维导图
————————————
Java并发编程:Lock