java多线程学习笔记(2)

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();
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值