【多线程 - 09、线程同步 Lock】

重入锁实现线程同步

在JDK1.5中新增了一个java.util.concurrent包来支持同步。

使用JUC里的Lock与使用synchronized方法和块具有相同的基本行为和语义,并且扩展了其能力

关键字synchronized实现的同步的锁,是隐藏的,所以并不明确是在哪里加上了锁,在哪里释放了锁。为了更明确的控制从哪里开始锁,在哪里释放锁,JDK1.5提供了Lock。

Lock是一个接口,真正用的是它的实现类ReentrantLock。

ReenreantLock类的常用方法

  • ReentrantLock() : 创建一个ReentrantLock实例 ,传入参数true可以获得公平锁,但由于能大幅度降低程序运行效率,不推荐使用
  • lock() : 获得锁
  • unlock() : 释放锁

例子:四个线程卖100张票

public class ThreadTest {
    public static void main(String[] args) {
        synchronizeThread st = new synchronizeThread();
        new Thread(st, "1").start();
        new Thread(st, "2").start();
        new Thread(st, "3").start();
        new Thread(st, "4").start();
    }
}

class synchronizeThread implements Runnable {
    private Integer ticketNumber = 100;
    private Lock lock = new ReentrantLock();//创建一个重入锁对象
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            lock.lock();//开启锁
           try {
               if (ticketNumber > 0) {
                   System.out.println("线程【" + Thread.currentThread().getName() + "】卖出了一张票,现在剩余了【" + ticketNumber + "】张票");
                   ticketNumber--;
               } else {
                   break;
               }
           }catch (Exception e){
               e.printStackTrace();
           }finally {//注意:要放到finally中
               lock.unlock();//释放锁
           }
        }
    }
}

Lock对象和synchronized关键字的选择

在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。

同步锁的释放问题的总结

用关键字synchronized构成同步代码块和同步方法,来实现多线程的同步,本质上可以理解为底层的程序给线程加了一把看不见的隐藏的锁,只有获取到这把锁的线程才能被执行,没拿到的线程就得等着,从而控制线程的执行顺序,达到同步效果,所以,任何线程进入同步代码块、同步方法之前,必须先获得对于同步监测器的锁定,那么谁释放对同步监测器的锁定呢?

在Java中,程序无法显式的释放对同步监测器的锁定,释放权在底层的JVM上,JVM会从释放机制中自动的释放。

下面情况下会进行同步监测器锁定的释放

  • 当前线程的同步方法、同步代码块执行正常结束,当前线程即释放随同步监测器的锁定;
  • 当前线程的同步方法、同步代码块中遇到break、return终止了该代码块、方法的继续执行,当前线程会释放同步监测器的锁定;
  • 当前线程在同步方法、同步代码块中出现了未处理的error或者exception,导致了该代码块、该方法异常结束时,当前线程会释放同步监测器的锁定;
  • 当前线程执行同步代码块或同步方法时,程序调用了同步监测器的wait()方法,当前线程暂停,则当前线程会释放同步监测器的锁定。

下面情况下不会进行同步监测器锁定的释放

  • 线程执行同步代码块或者同步方法时,程序调用了Thread.sleep()、Thread.yield()方法来暂停当前线程执行,当前线程不会释放对同步监测器的锁定;
  • 线程执行同步代码块时,其他线程调用了该线程的suspend()方法(suspend会阻塞线程直到另一个线程调用resume,这个方法容易死锁,已经不推荐使用了,了解一下就ok)将该线程挂起,也不会释放同步监测器的锁定。

你可能感兴趣的:(多线程,java,多线程)