java多线程学习笔记(2)–synchronized与lock使用
7、synchronized
在java中有两种机制可以处理线程并发,一个是synchronized关键字,一个是Java SE5.0引入的Lock锁对象的相关类。
从JAVA SE1.0开始,java中的每一个对象都有一个内部锁,如果一个方法使用 synchronized关键字进行声明,那么这个对象将保护整个方法,也就是说调用该方法线程必须获得内部的对象锁。一旦有一个线程通过synchronied方法获取到内部锁,该类的所有 synchronied方法或者代码块都无法被其他线程访问直到当前线程释放了内部锁。
synchronized最主要的三种使用方式
-
修饰实例方法,作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁
-
修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁.访问静态 synchronized 方法占用的锁是当前类的锁,而访问非静态 synchronized 方法占用的锁是当前实例对象锁。即给当前类加锁,会作用于类的所有对象实例
-
修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁.作用于代码块时1、synchronized(this)同步代码块于其修饰实例方法一致;2、synchronized(任意自定义对象),多个线程持有对象同一个监视器作下,同一时间只有一个线程可以执行synchronized(任意自定义对象)同步代码块。3、同步synchronized(*.class)代码块的作用其实和synchronized static方法作用一样。Class锁对类的所有对象实例起作用。
//同步方法
public synchronized void method{
}
//同步代码块
Object obj = new Object();
synchronized(obj){
}
示例:双重校验锁实现单例模式
public class Singleton {
//私有构造方法
private Singleton() {}
private static volatile Singleton instance;
//对外提供静态方法获取该对象
public static Singleton getInstance() {
//第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实际
if(instance == null) {
synchronized (Singleton.class) {
//抢到锁之后再次判断是否为空
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
8、lock锁
- Lock是显式锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
- 优先使用顺序:Lock>同步代码块(已经进入了方法体,分配了相应的资源) >同步方法(在方法体之外)
与使用synchronized方法和语句相比, Lock实现提供了更广泛的锁定操作。它们允许更灵活的结构,可能具有完全不同的属性,并且可能支持多个关联的Condition对象。
当锁定和解锁发生在不同的范围内时,必须注意确保所有在持有锁时执行的代码都受到 try-finally 或 try-catch 的保护,以确保在必要时释放锁。
Lock接口的主要API
方法 | 作用 |
---|---|
void lock() | 获取锁。 如果锁不可用,则当前线程将被禁用以用于线程调度目的并处于休眠状态,直到获得锁为止。 |
void lockInterruptibly() | 该方法会响应中断,即在获取锁中可以中断当前线程。 如果可用,则获取锁并立即返回。 如果锁不可用,则当前线程将被禁用以用于线程调度目的并处于休眠状态,直到发生以下两种情况之一: 锁被当前线程获取;或者其他一些线程中断当前线程,支持获取锁的中断。 |
boolean tryLock(); | 尝试非阻塞获取锁,仅当调用时它是空闲的时才获取锁。 如果锁可用,则获取锁并立即返回值为true 。如果锁不可用,则此方法将立即返回值false |
boolean tryLock(long time, TimeUnit unit) | 超时获取锁,当前线程在以下3种情况返回: 1.当前线程在超时时间内获取了锁 2.当前线程在超时时间被中断 3.当前线程超时时间结束 |
void unlock(); | 释放锁。 |
Condition newCondition(); | 条件对象Condition将Object监视器方法( wait 、notify和notifyAll )分解为不同的对象,通过将它们与任意Lock实现的使用结合起来,使每个对象具有多个等待集的效果。 Lock代替了synchronized方法和语句的使用, Condition代替了 Object 监视器方法的使用 此方法返回绑定到此Lock实例的新Condition实例。 |
使用方法
ReentrantLock lock = new ReentrantLock(); //参数默认false,不公平锁
ReentrantLock lock = new ReentrantLock(true); //公平锁
lock.lock(); //如果被其它资源锁定,会在此等待锁释放,达到暂停的效果
try {
//操作
} finally {
lock.unlock(); //释放锁
}
if (lock.tryLock()) { //如果已经被lock,则立即返回false不会等待
try {
//操作
} finally {
lock.unlock();
}
}
//需要将unlock操作放在finally代码块。如果在临界区的代码抛出异常,锁必须被释放。否则,其他线程将永远阻塞。
同步问题示例
class Ticket implements Runnable{
//当前拥有的票数
private int num = 100;
private Lock ticketLock= new ReentrantLock();
public void run(){
while(true){
try{
ticketLock.lock();
if(num>0){
Thread.sleep(10);
//输出卖票信息
System.out.println(Thread.currentThread().getName()+"还剩票数:"+num--);
}else break;
}catch (InterruptedException e){
e.printStackTrace();
Thread.currentThread().interrupt();
}finally {
ticketLock.unlock();
}
}
}
}