关于锁的种类介绍可见https://www.cnblogs.com/dennyLee2025/p/16600426.html
下面记录几种java中常用到的并发锁
synchronized
synchronized是java中的一个关键字,被其修饰的方法或代码块在任意时刻只能被一个线程执行。
对象锁:java中每个类对象都存在一个锁对象,该锁被称为对象锁。类中被synchronized修饰的方法执行前会将对象锁上锁,从而使得同一时刻同个对象只能执行一个被synchronized修饰的方法。
类锁:java中每个类都存在一个类的Class对象锁,该锁被称为类锁,针对类而非对象。(相当于静态成员)
-
获取对象锁
-
synchronized(this|object) {}
-
修饰非静态方法
-
-
获取类锁
-
synchronized(类.class) {}
-
修饰静态方法
-
简单用例1(直接获取对象锁):
class MyRunnable implements Runnable{ @Override public void run(){
//获取对象锁 synchronized(this){ try{ int i=(int)Thread.currentThread().threadId(); System.out.println("this is " + i); Thread.sleep(1000); System.out.println("finish " + i); }catch(Exception e){ e.printStackTrace(); } } } } public class synchronizedTest { public static void main(String[] Args){ Runnable myRunnable = new MyRunnable(); Thread[] test = new Thread[5]; for(int i=0;i<5;++i)test[i]=new Thread(myRunnable); var startTime = System.currentTimeMillis(); for(int i=0;i<5;++i)test[i].start(); try{ for(int i=0;i<5;++i)test[i].join(); }catch(Exception e){ } var endTime = System.currentTimeMillis(); System.out.println("spend " + (endTime - startTime)); } }
输出如下:

简单用例2(synchronized 方法):
public class synchronizedTest { private static int i = 0; private static synchronized void inc(){ i++; System.out.println(i); } public static void main(String[] Args){ Thread[] test = new Thread[5]; for(int i=0;i<5;++i)test[i]=new Thread(new Runnable() { @Override public void run(){ inc(); } }); for(int i=0;i<5;++i)test[i].start(); } }
输出如下:

若上面inc()方法去除synchronized关键字,则输出如下(结果不唯一):

可见synchronized关键字可有效保证数据的原子性。
wait,notify,notifyAll
Object对象wait,notify,notifyAll方法为public final
此三方法只能被持有对应对象的对象锁的线程调用,否则会抛出异常
wait方法会让当前线程挂起并释放对象锁。
notify方法会随机唤醒一个被wait方法挂起的线程。
notifyAll会唤醒所有被wait方法挂起的线程。
需注意:
synchronized 关键字修饰静态方法获得的锁是类锁,反之获得的是对象锁
synchronized 关键字修饰的方法,其synchronized特性不会被继承。即父类中synchronized方法在子类中若不覆盖,则默认无synchronized修饰。
定义接口方法或构造方法时无法使用synchronized修饰
synchronized与volatile的区别类似于c++中std::atomic和volatile的区别(见之前文章)。只不过synchronized无法修饰变量,而volatile只能修饰实例变量。
synchronized是可重入锁
Lock接口
Lock接口是java提供的锁接口,提供了以下方法:
- lock(); 获取锁,如果锁不可用,则出于线程调度的目的,当前线程将被禁用,并且在获取锁之前处于休眠状态。
- tryLock(); 试图获取锁,根据获取是否成功返回boolean,该方法不会让线程陷入等待。
- tryLock(long time, TimeUnit unit); 同2,但是会等待参数时间,之后返回结果。
- lockInterruptibly(); 试图获取锁,失败则抛出异常InterruptedException
- unLock(); 释放锁
继承该接口的常用实现类有
ReentrantLock(可重入锁)
ReentrantReadWriteLock(读写锁)
- writeLock():获取写锁。
- readLock():获取读锁。