疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Java synchronized关键字用法


 

   

  本篇随笔主要介绍 java 中 synchronized 关键字常用法,主要有以下四个方面:

  1、实例方法同步

  2、静态方法同步

  3、实例方法中同步块

  4、静态方法中同步块

  我觉得在学习synchronized关键字之前,我们首先需要知道以下一点:Java 中每个实例对象对应一把锁且每个实例对象只有一把锁,synchronized 关键字是通过对相应的实例对象加锁来实现同步功能的。

  一、实例方法中使用 synchronized 加锁

  实例方法中默认被加锁的对象是调用此方法的实例对象。

  复制代码

  1 class ImmutableValue {

  2 public synchronized void comeIn() throws InterruptedException{

  3 System.out.println(Thread.currentThread().getName() + ": start");

  4 Thread.sleep(5000);

  5 System.out.println(Thread.currentThread().getName() + ": finish");

  6 }

  7 public void synchronized comeInIn() throws InterruptedException {

  8 System.out.println(Thread.currentThread().getName() + ": start");

  9 Thread.sleep(5000);

  10 System.out.println(Thread.currentThread().getName() + ": finish");

  11 }

  12 }

  13 public class TestImmutableValue {

  14 public static void main(String[] args) {

  15 ImmutableValue im = new ImmutableValue();

  16 Thread t1 = new Thread(new Runnable() {

  17

  18 @Override

  19 public void run() {

  20 // TODO Auto-generated method stub

  21 try {

  22 im.comeIn();

  23 } catch (InterruptedException e) {

  24 // TODO Auto-generated catch block

  25 e.printStackTrace();

  26 }

  27 }

  28

  29 }, "t1");

  30 Thread t2 = new Thread(new Runnable() {

  31

  32 @Override

  33 public void run() {

  34 // TODO Auto-generated method stub

  35 try {

  36 im.comeInIn();

  37 } catch (InterruptedException e) {

  38 // TODO Auto-generated catch block

  39 e.printStackTrace();

  40 }

  41 }

  42

  43 }, "t2");

  44 t1.start();

  45 t2.start();

  46 }

  47 }

  复制代码

  在上面的代码中创建了两个线程并分别命名为 t1, t2。调用了同一个对象 im 的两个同步方法 comeIn 和 comeInIn, 执行结果如下:

  在 t1 线程开始执行后,即使 t1 线程睡眠了5s,线程 t2 中的 comeInIn 方法仍然没有得到执行。这是因为 t1 线程先执行的 comeIn 方法,持有了对象 im 的锁,且 comeIn 方法并没有执行完,对象 im 的锁没有被释放,所以 comeInIn 方法无法对对象 im 加锁,就无法继续执行,只能等到 t1 线程中的 comeIn 方法执行完毕,释放对象 im 的锁,comeInIn 方法才能继续执行。

  但是如果 t1 线程调用的是对象 im 的 comeIn 方法,而 t2 线程调用的是我们声明的另外一个 ImmutableValue 对象 im2 对象的 comeInIn 方法,则这两个方法的执行是互不影响的。因为 t1 线程的 comeIn 方法要获得 im 对象的锁,而 t2 线程要获得的是 im2 对象的锁,两个锁并不是同一个锁(Java中每个实例对象都有且只有一个锁),所以这两个方法执行互不影响。

  二、静态方法中使用 synchronized 加锁

  静态方法中默认被加锁的对象是此静态方法所在类的 class 对象。

  复制代码

  1 class staticMethodSynchronized {

  2 public static synchronized void method1() throws InterruptedException {

  3 System.out.println(Thread.currentThread().getName() + ": start");

  4 Thread.sleep(5000);

  5 System.out.println(Thread.currentThread().getName() + ": finish");

  6 }

  7 public static synchronized void method2() throws InterruptedException {

  8 System.out.println(Thread.currentThread().getName() + ": start");

  9 Thread.sleep(5000);

  10 System.out.println(Thread.currentThread().getName() + ": finish");

  11 }

  12 }

  13 public class TestStaticClassSynchronized {

  14 public static void main(String[] args) {

  15 Thread t1 = new Thread(new Runnable() {

  16

  17 @Override

  18 public void run() {

  19 // TODO Auto-generated method stub

  20 try {

  21 staticMethodSynchronized.method1();

  22 } catch (InterruptedException e) {

  23 // TODO Auto-generated catch block

  24 e.printStackTrace();

  25 }

  26 }

  27

  28 }, "t1");

  29 Thread t2 = new Thread(new Runnable() {

  30

  31 @Override

  32 public void run() {

  33 // TODO Auto-generated method stub

  34 try {

  35 staticMethodSynchronized.method2();

  36 } catch (InterruptedException e) {

  37 // TODO Auto-generated catch block

  38 e.printStackTrace();

  39 }

  40 }

  41

  42 }, "t2");

  43 t1.start();

  44 t2.start();

  45 }

  46 }

  复制代码

  在上述代码中创建了两个线程并命名为 t1,t2。 t1,t2 线程调用了 staticMethodSynchronized 类的两个静态同步方法 method1 和 method2。执行结果如下:

  在 t1 线程开始执行后,即使 t1 线程睡眠了5s,线程 t2 中的 method2 方法仍然没有得到执行。这是因为 t1 线程先执行的 method1 方法,持有了staticMethodSynchronized 类对象的锁,且 method1 方法并没有执行完,staticMethodSynchronized 类对象的锁没有被释放,所以 comeInIn 方法无法对staticMethodSynchronized 类对象加锁,就无法继续执行,只能等到 t1 线程中的 method1 方法执行完毕,释放 staticMethodSynchronized 类对象的锁,method2 方法才能继续执行。

  三、实例方法中使用 synchronized 关键字制造同步块

  同步块中默认被加锁的对象是此同步块括号声明中包含的对象。

  复制代码

  1 class ImmutableValue {

  2 public synchronized void comeIn() throws InterruptedException{

  3 System.out.println(Thread.currentThread().getName() + ": start");

  4 Thread.sleep(5000);

  5 System.out.println(Thread.currentThread().getName() + ": finish");

  6 }

  7 public void comeInIn() throws InterruptedException {

  8 System.out.println(Thread.currentThread().getName() + ": start");

  9 synchronized(this) {

  10

  11 }

  12 System.out.println(Thread.currentThread().getName() + ": finish");

  13 }

  14 }

  15 public class TestImmutableValue {

  16 public static void main(String[] args) {

  17 ImmutableValue im = new ImmutableValue();

  18 Thread t1 = new Thread(new Runnable() {

  19

  20 @Override

  21 public void run() {

  22 // TODO Auto-generated method stub

  23 try {

  24 im.comeIn();

  25 } catch (InterruptedException e) {

  26 // TODO Auto-generated catch block

  27 e.printStackTrace();

  28 }

  29 }

  30

  31 }, "t1");

  32 Thread t2 = new Thread(new Runnable() {

  33

  34 @Override

  35 public void run() {

  36 // TODO Auto-generated method stub

  37 try {

  38 im.comeInIn();

  39 } catch (InterruptedException e) {

  40 // TODO Auto-generated catch block

  41 e.printStackTrace();

  42 }

  43 }

  44

  45 }, "t2");

  46 t1.start();

  47 t2.start();

  48 }

  49 }

  复制代码

  由以上代码可以看到: 在 comeInIn 方法中,运用 synchronized(this) 制造同步块,要执行同步块内的代码,就必须获得 this 对象的锁(调用 comeInIn 方法的对象)。

  执行结果可能为:

  由此执行结果可见:t1 线程先执行了 comeIn 方法,获得了对象 im 的锁,之后由于 t1 线程进入睡眠状态,t2 线程得到运行,开始执行 comeInIn 方法,当执行到同步代码块时发现对象 im 已被加锁,无法继续执行。t1 线程睡眠结束之后继续执行,结束后释放对象 im 的锁,t2 线程才能继续执行。

  四、静态方法中使用 synchronized 关键字制造同步块

  同步块中默认被加锁的对象是此同步块括号声明中包含的对象。

  复制代码

  1 class staticMethodSynchronized {

  2 private static final Object OBJ = new Object();

  3 public static void method1() throws InterruptedException {

  4 System.out.println(Thread.currentThread().getName() + ": start");

  5 synchronized(OBJ) {

  6 System.out.println(Thread.currentThread().getName() + ": 获得锁");

  7 System.out.println(Thread.currentThread().getName() + ": 释放锁");

  8 }

  9 System.out.println(Thread.currentThread().getName() + ": finish");

  10 }

  11 public static void method2() throws InterruptedException {

  12 System.out.println(Thread.currentThread().getName() + ": start");

  13 synchronized(OBJ) {

  14 System.out.println(Thread.currentThread().getName() + ": 获得锁");

  15 System.out.println(Thread.currentThread().getName() + ": 释放锁");

  16 }

  17 System.out.println(Thread.currentThread().getName() + ": finish");

  18 }

  19 }

  20 public class TestStaticClassSynchronized {

  21 public static void main(String[] args) {

  22 Thread t1 = new Thread(new Runnable() {

  23

  24 @Override

  25 public void run() {

  26 // TODO Auto-generated method stub

  27 try {

  28 staticMethodSynchronized.method1();

  29 } catch (InterruptedException e) {

  30 // TODO Auto-generated catch block

  31 e.printStackTrace();

  32 }

  33 }

  34

  35 }, "t1");

  36 Thread t2 = new Thread(new Runnable() {

  37

  38 @Override

  39 public void run() {

  40 // TODO Auto-generated method stub

  41 try {

  42 staticMethodSynchronized.method2();

  43 } catch (InterruptedException e) {

  44 // TODO Auto-generated catch block

  45 e.printStackTrace();

  46 }

  47 }

  48

  49 }, "t2");

  50 t1.start();

  51 t2.start();

  52 }

  53 }

  复制代码

  在上述代码中,两个静态方法中的同步块都要获得对象 OBJ 的锁才能继续向下执行,执行结果可能如下:

  若 t1 线程先获得锁,则必须等到 t1 释放锁之后,t2 线程中同步代码块及其之后的代码才能继续执行,t2 线程先获得锁,t1 线程同理。

  总之,我认为我们只需抓住一点:Java 中每个实例对象对应一把锁且每个实例对象只有一把锁,synchronized 关键字是通过对相应的实例对象加锁来实现同步功能的(静态方法为对相应的 class 对象加锁)。在执行 synchronized 方法或 synchronized 同步块之前,我们只需判断其需要获得的对象的锁是否可获得,就可判断此方法或同步块是否可得到执行。