乐趣区

关于java:Java中的synchronized关键字

ps:本文是转载文章,浏览原文能够获取源码,文章开端有原文链接

synchronized 翻译过去是“同步”的意思,在多线程应用中相当于一把锁,synchronized 的底层是应用操作系统的 mutex lock 实现的;锁具备内存可见性和操作原子性,内存可见性就是一个线程对共享变量值的批改,可能及时的被其余线程看到;操作原子性就是持有同一个锁的两个同步块或者两个以上只能串行地进入;那线程读取锁或者开释锁,JMM 都是做哪些操作呢?是这样的,当线程开释锁时,JMM 会把该线程对应的本地内存中的共享变量刷新到主内存中;当线程获取锁时,JMM 会把该线程对应的本地内存置为有效。从而使得被监视器爱护的临界区代码必须从主内存中读取共享变量;目前 synchronized 利用场景有 4 种,上面对它进行一一列举。

1、润饰一个代码块,其作用的范畴是大括号括起来的代码,被润饰的代码块也称为同步语句块,它作用的对象是调用同步语句块的对象;假如当一个线程调用以后对象的同步语句块 A 时,其余线程调用以后对象 同步语句块 A ** 或者其余同步语句块 B 时要期待,留神这里说的以后对象是同一个对象。
**

ps:代码是在 eclipse 上写的,编码为 UTF-8

(1)新建一个 MyObject 类:

public class MyObject {public void run(long sleepTime) {synchronized (this) {
 try {Thread.sleep(sleepTime);
 String threadName = Thread.currentThread().getName();
 String log = "以后线程为 ---" + threadName;
 System.out.println(log);
 } catch (InterruptedException e) {e.printStackTrace();
 }
 }
 }
 
 public void run2(long sleepTime) {synchronized (this) {
 try {Thread.sleep(sleepTime);
 String threadName = Thread.currentThread().getName();
 String log = "以后线程为 ---" + threadName;
 System.out.println(log);
 } catch (InterruptedException e) {e.printStackTrace();
 }
 }
 }
}

(2)新建一个线程类 MyThreadA:

public class MyThreadA extends Thread {
 private long mSleepTime = 0;
 private MyObject myObject = null;
 public MyThreadA(String name,long sleepTime,MyObject myObject) {super(name);
 this.mSleepTime = sleepTime;
 this.myObject = myObject;
 }
 
 @Override
 public void run() {super.run();
 if (myObject != null) {myObject.run(mSleepTime);
 }
 }
}

(3)再新建一个线程类 MyThreadB:

public class MyThreadB extends Thread{
 private long mSleepTime = 0;
 private MyObject myObject = null;
 public MyThreadB(String name,long sleepTime,MyObject myObject) {super(name);
 this.mSleepTime = sleepTime;
 this.myObject = myObject;
 }
 
 @Override
 public void run() {super.run();
 if (myObject != null) {myObject.run2(mSleepTime);
 }
 }
}

(4)在主程序入口启动 MyThreadA 和 MyThreadB 线程:

 MyObject myObject = new MyObject();
 MyThreadA myThreadA = new MyThreadA("MyThreadA", 3000, myObject);
 MyThreadB myThreadB = new MyThreadB("MyThreadB", 500, myObject);
 try {myThreadA.start();
 Thread.sleep(100);
 myThreadB.start();} catch (InterruptedException e) {e.printStackTrace();
 }

日志打印如下所示:

以后线程为 ---MyThreadA
以后线程为 ---MyThreadB

这里 MyThreadA 和 MyThreadB 2 个线程调用同一个对象不同的办法中的同步代码块,MyThreadA 比 MyThreadB 优先调用 MyObject 对象的办法,尽管 MyThreadA 调用 MyObject 对象的办法休眠工夫比 MyThreadB 调用 MyObject 对象的办法休眠工夫长,然而 MyThreadA 取得 MyObject 对象锁,所以等 MyObject 对象的 run 中的同步语句块执行完后 MyThreadA 才开释锁,MyThreadB 能力取得锁执行以后线程的同步语句块,所以是 MyThreadA 线程的日志先打印。

2、润饰一个类,作用的这个类的所有对象,其作用范畴是 synchronized 前面括号括起来的局部。

假如润饰的是一个类叫 A,如果一个线程 t1 正在执行 synchronized(A.class){代码段 1}且比拟耗时,另外一个线程 t2 试图执行 synchronized(A.class){代码段 1} 或者 synchronized(A.class){代码段 2} 都要进行期待,等 t1 中 synchronized 润饰的类的代码块执行后开释锁,t2 能力获取锁执行 synchronized 润饰 A 类的代码块。

(1)在 MyObject 类的根底上增加 run3 和 run4 办法:

public void run3(long sleepTime) {synchronized (MyObject.class) {
 try {Thread.sleep(sleepTime);
 String threadName = Thread.currentThread().getName();
 String log = "以后线程为 ---" + threadName;
 System.out.println(log);
 } catch (InterruptedException e) {e.printStackTrace();
 }
 }
 }
 
 public void run4(long sleepTime) {synchronized (MyObject.class) {
 try {Thread.sleep(sleepTime);
 String threadName = Thread.currentThread().getName();
 String log = "以后线程为 ---" + threadName;
 System.out.println(log);
 } catch (InterruptedException e) {e.printStackTrace();
 }
 }
 }

(2)新建一个线程类 MyThreadA2:

public class MyThreadA2 extends Thread{
 private long mSleepTime = 0;
 private MyObject myObject = null;
 public MyThreadA2(String name,long sleepTime,MyObject myObject) {super(name);
 this.mSleepTime = sleepTime;
 this.myObject = myObject;
 }
 
 @Override
 public void run() {super.run();
 if (myObject != null) {myObject.run3(mSleepTime);
 }
 }
}

(3)新建一个线程类 MyThreadB2:

public class MyThreadB2 extends Thread{
 private long mSleepTime = 0;
 private MyObject myObject = null;
 public MyThreadB2(String name,long sleepTime,MyObject myObject) {super(name);
 this.mSleepTime = sleepTime;
 this.myObject = myObject;
 }
 
 @Override
 public void run() {super.run();
 if (myObject != null) {myObject.run4(mSleepTime);
 }
 }
}

(4)在主程序入口启动 MyThreadA2 和 MyThreadB2 线程:

 MyObject myObject = new MyObject();
 MyObject myObject2 = new MyObject();
 MyThreadA2 myThreadA2 = new MyThreadA2("MyThreadA2", 3000, myObject);
 MyThreadB2 myThreadB2 = new MyThreadB2("MyThreadB2", 100, myObject2);
 try {myThreadA2.start();
 Thread.sleep(100);
 myThreadB2.start();} catch (InterruptedException e) {e.printStackTrace();
 }

日志打印如下所示:

以后线程为 ---MyThreadA2
以后线程为 ---MyThreadB2

这里先用 MyThreadA2 线程调用完 myObject 的 run3 办法,随后再到 MyThreadB2 线程执行 myObject2 的 run4 办法中的代码块;尽管 2 个线程不同对象中不同办法,然而因为它们拜访的办法里都有 synchronized 润饰 MyObject.class,所以 MyThreadB2 执行到 synchronized 润饰 MyObject.class 的代码块时,先由 MyThreadA2 开释锁能力获取锁进行执行 synchronized 润饰 MyObject.class 的代码块。

**3、批改一个动态的办法,其作用的范畴是整个静态方法,作用的对象是这个类的所有对象。
**

如果有一个类名叫 A,它有 2 个静态方法 synchronized static void method1(){} 和 synchronized static void method2(){},一个线程执行 A.method1() 办法时,另一个线程执行 new A().method1() 或者 A.method2() 办法都要进行阻塞。

(1)在 MyObject 类原有的根底上增加 3 个办法 run5、run6 和 runPublic:

 public synchronized static void run5(long sleepTime) {runPlublic(sleepTime);
 }
 
 public synchronized static void run6(long sleepTime) {runPlublic(sleepTime);
 }
 
 private static void runPlublic(long sleepTime) {
 try {Thread.sleep(sleepTime);
 String threadName = Thread.currentThread().getName();
 String log = "以后线程为 ---" + threadName;
 System.out.println(log);
 } catch (InterruptedException e) {e.printStackTrace();
 }
 }

(2)新建一个线程类 MyThreadA3:

public class MyThreadA3 extends Thread{
 private long mSleepTime = 0;
 public MyThreadA3(String name,long sleepTime) {super(name);
 this.mSleepTime = sleepTime;
 }
 
 @Override
 public void run() {super.run();
 MyObject.run5(mSleepTime);
 }
}

(3)新建一个线程类 MyThreadB3:

public class MyThreadB3 extends Thread{
 private long mSleepTime = 0;
 private MyObject myObject = null;
 public MyThreadB3(String name,long sleepTime,MyObject myObject) {super(name);
 this.mSleepTime = sleepTime;
 this.myObject = myObject;
 }
 
 @SuppressWarnings("static-access")
 @Override
 public void run() {super.run();
 if (myObject != null) {myObject.run5(mSleepTime);
 }
 }
}

(4)新建一个线程类 MyThreadC3:

public class MyThreadC3 extends Thread{
 private long mSleepTime = 0;
 public MyThreadC3(String name,long sleepTime) {super(name);
 this.mSleepTime = sleepTime;
 }
 
 @Override
 public void run() {super.run();
 MyObject.run6(mSleepTime);
 }
}

(5)在程序入口调用 MyThreadA3、MyThreadB3 和 MyThreadC3:

 MyObject myObject = new MyObject();
 Thread myThreadA3 = new MyThreadA3("MyThreadA3", 3000);
 Thread myThreadB3 = new MyThreadB3("MyThreadB3", 500,myObject);
 Thread myThreadC3 = new MyThreadC3("MyThreadC3", 100);
 try {myThreadA3.start();
 Thread.sleep(100);
 myThreadB3.start();
 Thread.sleep(100);
 myThreadC3.start();} catch (InterruptedException e) {e.printStackTrace();
 }

日志打印如下所示:

以后线程为 ---MyThreadA3
以后线程为 ---MyThreadC3
以后线程为 ---MyThreadB3

这里先开启线程 MyThreadA3 而后每距离 100 ms 开启 MyThreadB3 和 MyThreadC3;线程 MyThreadA3 调用的是 MyObject.run5 办法,休眠工夫是最长的,尽管 MyThreadB3 是用 MyObject 对象调用 run5 办法,但对象也属于 MyObject 类,所以 MyThreadB3 会被阻塞;而 MyThreadC3 调用的是 MyObject.run6 办法,该办法用 synchronized 润饰,属于 MyObject 的静态方法,所以 MyThreadC3 线程也会被阻塞。

4、润饰一个办法,被润饰的办法称为同步办法,其作用的范畴是整个办法,作用的对象是调用这个办法的对象。

如果有一个类名叫 A,咱们把它进行实例化 A a = new A(),它有 2 个办法 synchronized void method1(){} 和 synchronized void method2(){},当一个线程调用 a.method1() 办法时,另一个线程调用 a.method1() 或者 a.method2() 办法都会被阻塞。

(1)在 MyObject 类的根底上增加 2 个办法,名叫 run7 和 run8:

 public synchronized void run7(long sleepTime) {runPlublic(sleepTime);
 }
 
 public synchronized void run8(long sleepTime) {runPlublic(sleepTime);
 }

(2)新建一个线程类 MyThreadA4:

public class MyThreadA4 extends Thread{
 private long mSleepTime = 0;
 private MyObject myObject = null;
 public MyThreadA4(String name,long sleepTime,MyObject myObject) {super(name);
 this.mSleepTime = sleepTime;
 this.myObject = myObject;
 }
 
 @Override
 public void run() {super.run();
 if (myObject != null) {myObject.run7(mSleepTime);
 }
 }
}

(3)新建一个线程类 MyThreadB4:

public class MyThreadB4 extends Thread{
 private long mSleepTime = 0;
 private MyObject myObject = null;
 public MyThreadB4(String name,long sleepTime,MyObject myObject) {super(name);
 this.mSleepTime = sleepTime;
 this.myObject = myObject;
 }
 
 @Override
 public void run() {super.run();
 if (myObject != null) {myObject.run7(mSleepTime);
 }
 }
}

(4)新建一个线程类 MyThreadC4:

public class MyThreadC4 extends Thread{
 private long mSleepTime = 0;
 private MyObject myObject = null;
 public MyThreadC4(String name,long sleepTime,MyObject myObject) {super(name);
 this.mSleepTime = sleepTime;
 this.myObject = myObject;
 }
 
 @Override
 public void run() {super.run();
 if (myObject != null) {myObject.run8(mSleepTime);
 }
 }
}

(5)在程序入口调用 MyThreadA4、MyThreadB4 和 MyThreadC4:

 MyObject myObject = new MyObject();
 Thread myThreadA4 = new MyThreadA4("MyThreadA4", 3000,myObject);
 Thread myThreadB4 = new MyThreadB4("MyThreadB4", 500,myObject);
 Thread myThreadC4 = new MyThreadC4("MyThreadC4", 100,myObject);
 try {myThreadA4.start();
 Thread.sleep(100);
 myThreadB4.start();
 Thread.sleep(100);
 myThreadC4.start();} catch (InterruptedException e) {e.printStackTrace();
 }

日志打印如下所示:

以后线程为 ---MyThreadA4
以后线程为 ---MyThreadC4
以后线程为 ---MyThreadB4

这里一开始先启动 MyThreadA4 线程,而后每距离 100ms 启动 MyThreadB4 和 MyThreadC4;MyThreadB4 和 MyThreadA4 调用的是同一个对象的同一个 synchronized 润饰的办法,MyThreadB4 启动在后,所以被阻塞;尽管 MyThreadC4 和 MyThreadA4 调用的是同一个对象的不同的 synchronized 润饰的办法,但因为 synchronized 作用的是同一个对象,MyThreadC4 又启动在后,所以也被阻塞。

退出移动版