目录
前言
多线程开发中,同步控制是必不可少的手段。而同步的实现需要用到锁,Java中提供了两种基本的锁,分别是synchronized 和 Lock。两种锁都非常常用,但也各有利弊,下面开始学习。
synchronized用法
synchronized 是Java的关键字,是应用最为广泛的同步工具之一。当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码,同时,值得说明的是,它是在软件层面依赖JVM实现同步的。
synchronized 的用法很简单,直接用其修饰代码块即可,一般可将其用于修饰方法和代码块,根据修饰地方的不同还有不同的作用域,下面一一介绍。
修饰方法
synchronized 修饰方法分为两种情况:
- 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁。
- 修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁。
修饰实例方法
顾名思义就是修饰类中的实例方法,并且默认是当前对象作为锁的对象,而一个对象只有一把锁,所以同一时刻只能有一个线程执行被同步的方法,等到线程执行完方法后,其他线程才能继续执行被同步的方法。实例代码如下:
public class SyncTest implements Runnable{ //静态变量 public static int TEST_INT = 0; //被同步的实例方法 public synchronized void increase(){ TEST_INT++; } @Override public void run() { for(int i=1;i<=100000;i++){ increase(); } } public static void main(String[] args) throws InterruptedException { //实例化对象 SyncTest instance = new SyncTest(); Thread t1=new Thread(instance); Thread t2=new Thread(instance); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(TEST_INT); } }运行上方的程序,结果会是200000,因为main函数中只实例化一个SyncTest对象,所以,两个线程运行的时候只能有一个线程获取到对象的锁,当一个线程获取了该对象的锁之后,其他线程无法获取该对象的锁,所以无法访问该对象的其他synchronized实例方法,当然其他线程还是可以访问该对象的非synchronized方法的。
不过,上面的情况只是针对一个对象实例进行操作,如果有多个对象实例的话,修饰实例方法是无法保证线程安全的,我们可以把main函数的程序修改下:
public static void main(String[] args) throws InterruptedException { //每个线程实例化一个SyncTest对象 Thread t1=new Thread(new SyncTest()); Thread t2=new Thread(new SyncTest()); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(TEST_INT); }运行程序后,会发现结果永远小于200000,说明synchronized没有起到同步的作用了,说明修饰实例方法只能作用实例对象,不能作用到类对象。
修饰静态方法
要想synchronized同步到类对象本身,可以用它修饰类中的静态方法。修改下上述代码中的increase方法为静态方法,并在main函数中新建两条线程:
