一、介绍
首先,java的锁分为两类:
- 第一类是synchronized同步关键字,这个关键字属于隐式的锁,是jyvm层面实现,使用的时候看不见;
- 第二类是在jdk5后增加的Lock接口以及对应的各种实现类,这属于显式的锁,就是我们能在代码层面看到锁这个对象,而这些个对象的方法实现,大都是直接依赖CPU指令的,无关jvm的实现。
二、synchronized
- 如果修饰的是具体对象:锁的是对象;
- 如果修饰的是成员方法:那锁的就是this(作用的对象是调用这个方法的对象) ;
- 如果修饰的是静态方法:锁的就是这个对象.class。
在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。
修饰一个类
class ClassName {
public void method() {
synchronized(ClassName.class) {
}
}
}
修饰成员方法
public class SynchronizedLockFunction implements Runnable{
private String[] first ={"繁星积","墨染","星辰","晚霞"};
private Integer id = 0;
private boolean flag = true;
public static void main(String[] args) throws InterruptedException {
Runnable synchronizedLockFunction = new SynchronizedLockFunction();
Thread thread = new Thread(synchronizedLockFunction);
Thread thread1 = new Thread(synchronizedLockFunction);
thread.start();
thread1.start();
}
public synchronized void lockFunction(){
while (flag) {
if (id < first.length) {
System.out.println(first[id]);
System.out.println(Thread.currentThread().getName());
++id;
} else {
flag = false;
}
}
}
@Override
public void run() {
lockFunction();
}
}
修饰一个代码块
public class SynchronizedLockCodeBlock implements Runnable{
private boolean flag = true;
private Integer id = 0;
public static void main(String[] args) {
SynchronizedLockCodeBlock synchronizedLockCodeBlock = new SynchronizedLockCodeBlock();
Thread thread = new Thread(synchronizedLockCodeBlock);
Thread thread1 = new Thread(synchronizedLockCodeBlock);
thread.start();
thread1.start();
}
@Override
public void run() {
Object o = new Object();
while(flag) {
synchronized (o) {
if(id < 33){
System.out.println(Thread.currentThread().getName()+id);
id++;
}else {
flag =false;
}
}
}
}
}
三、lock
Lock是一个接口,实现类常见的有:
- 重入锁(ReentrantLock )
- 读锁(ReadLock )
- 写锁(writeLock )
一般使用ReentrantLock类做为锁。在加锁和解锁处需要通过lock()和unlock()显示指出。所以一般会在finally块中写unlock()以防死锁 。
public class LocklivingExample implements Runnable {
private boolean flag = true;
private Integer id = 0;
public static void main(String[] args) throws InterruptedException {
LocklivingExample locklivingExample = new LocklivingExample();
Thread thread = new Thread(locklivingExample);
Thread thread1 = new Thread(locklivingExample);
thread.start();
thread1.start();
}
@Override
public void run() {
Lock reentrantLock = new ReentrantLock();
while (flag) {
try {
if (reentrantLock.tryLock(3, TimeUnit.SECONDS)) {
if (id > 33) {
flag = false;
}
System.out.println(Thread.currentThread().getName() + id);
id++;
}
} catch (Exception e) {
e.getMessage();
}finally {
reentrantLock.unlock();
}
}
}
}
对比完了synchronized 和Lock 两个锁。对于java 的线程同步机制,往往还会提到的另外两个内容就是volatile 关键字和CAS 操作以及对应的原子类。 因此这里再提一下:
volatile 关键字常被称为轻量级的synchronized ,实际上这两个完全不是一个东西。我们知道了synchronized通过的是jwm层面的管程隐式的加了锁。而volatile关键字则是另一个角度,jvm也采用相应的手段,保证;
- 被它修饰的变量的可见性:线程对变量进行修改后,要立刻写回主内存;
- 线程对变量读取的时候,要从主内存读,而不是缓存;
- 在它修饰变量上的操作禁止指令重排序。
CAS是一种CPU的指令,也不屋于加锁,它通过假设没有冲突而去试探性的完成操作,如果因为冲突失败了就重试,直到成功。那么实际上我们很少直接使用CAS,但是 java里提供了一些原子变量类,就是juc包里面的各种Atomioxo类,这些类的底层实现直接使用了CAS操作来保证使用这些类型的变量的时候,操作都是原子操作,当使用他们作为共享变量的时候,也就不存在线程安全问题了。
注意: synchronized同步块执行完成或者遇到异常是锁会自动释放,而lock必须调用unlock()方法释放锁,因此在finally块中释放锁。