目录
Java中,synchronized关键字有2种用法:
- 作为关键字修饰方法
- 修饰一个代码块
线程争用
为了探究synchronized的具体用法,可以用一个简单的程序来说明:
package fc.learn.java.synchronize;import java.util.Random;public class LearningSynchronized { public enum SyncTypeTag { OBJ_METHOD, CLASS_METHOD, KEYWARD_METHOD, NORMAL_METHOD } static class MyRunnable implements Runnable{ private void counting(){ for (int i = 0; i< 5; i++){ System.out.println(String.format("Thread[%s] count for [%s]", Thread.currentThread().getName(), i)); int sleepSeconds = new Random().nextInt(); sleepSeconds = sleepSeconds % 10; sleepSeconds = sleepSeconds * sleepSeconds; sleepSeconds = sleepSeconds % 5; System.out.println(String.format("Thread[%s] sleep for 0.%s seconds", Thread.currentThread().getName(), sleepSeconds)); try { Thread.sleep(sleepSeconds * 100); } catch (InterruptedException e) { e.printStackTrace(); } } } private void synchronizeObjectMethod() { synchronized (this){ counting(); } } private void synchronizeClasstMethod(){ synchronized (MyRunnable.class){ counting(); } } private synchronized void synchronizedMethod(){ counting(); } private void normalMethod(){ counting(); } private SyncTypeTag synType; public MyRunnable(SyncTypeTag type){ this.synType = type; } @Override public void run() { if (this.synType == SyncTypeTag.OBJ_METHOD) { synchronizeObjectMethod(); } else if (this.synType == SyncTypeTag.CLASS_METHOD) { synchronizeClasstMethod(); } else if (this.synType == SyncTypeTag.KEYWARD_METHOD) { synchronizedMethod(); } else if (this.synType == SyncTypeTag.NORMAL_METHOD) { normalMethod(); } } } public static void main(String[] args) { Runnable r1 = new MyRunnable(SyncTypeTag.NORMAL_METHOD); Runnable r2 = new MyRunnable(SyncTypeTag.NORMAL_METHOD); Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); t2.start(); }}
运行代码,可以获得结果如下:
Thread[Thread-1] count for [0]Thread[Thread-0] count for [0]Thread[Thread-1] sleep for 0.4 secondsThread[Thread-0] sleep for 0.1 secondsThread[Thread-0] count for [1]Thread[Thread-0] sleep for 0.4 secondsThread[Thread-1] count for [1]Thread[Thread-1] sleep for 0.0 secondsThread[Thread-1] count for [2]Thread[Thread-1] sleep for 0.1 secondsThread[Thread-1] count for [3]Thread[Thread-0] count for [2]Thread[Thread-1] sleep for 0.4 secondsThread[Thread-0] sleep for 0.1 secondsThread[Thread-0] count for [3]Thread[Thread-0] sleep for 0.4 secondsThread[Thread-1] count for [4]Thread[Thread-1] sleep for 0.4 secondsThread[Thread-0] count for [4]Thread[Thread-0] sleep for 0.1 seconds
也就是说,普通情况下,counting()方法会被线程争用
类锁 synchronized(class)
那么我们加锁试试呢?首先试试对class加锁:
// 之前代码太多不再赘述,此处只展示main方法public static void main(String[] args) { Runnable r1 = new MyRunnable(SyncTypeTag.CLASS_METHOD); Runnable r2 = new MyRunnable(SyncTypeTag.CLASS_METHOD); Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); t2.start();}
可以得到如下结果:
Thread[Thread-0] count for [0]Thread[Thread-0] sleep for 0.1 secondsThread[Thread-0] count for [1]Thread[Thread-0] sleep for 0.0 secondsThread[Thread-0] count for [2]Thread[Thread-0] sleep for 0.1 secondsThread[Thread-0] count for [3]Thread[Thread-0] sleep for 0.1 secondsThread[Thread-0] count for [4]Thread[Thread-0] sleep for 0.4 secondsThread[Thread-1] count for [0]Thread[Thread-1] sleep for 0.0 secondsThread[Thread-1] count for [1]Thread[Thread-1] sleep for 0.0 secondsThread[Thread-1] count for [2]Thread[Thread-1] sleep for 0.1 secondsThread[Thread-1] count for [3]Thread[Thread-1] sleep for 0.1 secondsThread[Thread-1] count for [4]Thread[Thread-1] sleep for 0.1 seconds
此时,线程不再争用,目的达到。
对象锁 synchronized(object)
那么试试对象锁:
// 仔细观察,这里用的 this 也就是说,每个线程用的不是同一把锁private void synchronizeObjectMethod() { synchronized (this){ counting(); }}public static void main(String[] args) { Runnable r1 = new MyRunnable(SyncTypeTag.CLASS_METHOD); Runnable r2 = new MyRunnable(SyncTypeTag.CLASS_METHOD); Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); t2.start();}
运行代码:
Thread[Thread-1] count for [0]Thread[Thread-0] count for [0]Thread[Thread-1] sleep for 0.0 secondsThread[Thread-0] sleep for 0.4 secondsThread[Thread-1] count for [1]Thread[Thread-1] sleep for 0.1 secondsThread[Thread-1] count for [2]Thread[Thread-1] sleep for 0.4 secondsThread[Thread-0] count for [1]Thread[Thread-0] sleep for 0.4 secondsThread[Thread-1] count for [3]Thread[Thread-1] sleep for 0.4 secondsThread[Thread-0] count for [2]Thread[Thread-0] sleep for 0.1 secondsThread[Thread-0] count for [3]Thread[Thread-0] sleep for 0.0 secondsThread[Thread-0] count for [4]Thread[Thread-0] sleep for 0.1 secondsThread[Thread-1] count for [4]Thread[Thread-1] sleep for 0.0 seconds
可以发现,锁并不起作用。这个原因也很容易理解,因为synchronizeObjectMethod
方法中用的synchronized(this)
进行加锁,而我们有2个进程对象在对counting()
方法进行操作,所以会发生争用。如果代码修改为这样:
// synchronizedObjectMethod修改成这样:private void synchronizeObjectMethod(Object globalLock) { synchronized (globalLock){ counting(); }}// 构造方法和成员变量:private SyncTypeTag synType;private Object globalLock;public MyRunnable(SyncTypeTag type, Object lock){ this.synType = type; this.globalLock = lock;}@Overridepublic void run() { if (this.synType == SyncTypeTag.OBJ_METHOD) { synchronizeObjectMethod(this.globalLock); } else if (this.synType == SyncTypeTag.CLASS_METHOD) { synchronizeClasstMethod(); } else if (this.synType == SyncTypeTag.KEYWARD_METHOD) { synchronizedMethod(); } else if (this.synType == SyncTypeTag.NORMAL_METHOD) { normalMethod(); }}// main 方法:public static void main(String[] args) { Object globalLock = new Object(); Runnable r1 = new MyRunnable(SyncTypeTag.OBJ_METHOD, globalLock); Runnable r2 = new MyRunnable(SyncTypeTag.OBJ_METHOD, globalLock); Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); t2.start();}
运行代码:
Thread[Thread-0] count for [0]Thread[Thread-0] sleep for 0.1 secondsThread[Thread-0] count for [1]Thread[Thread-0] sleep for 0.1 secondsThread[Thread-0] count for [2]Thread[Thread-0] sleep for 0.1 secondsThread[Thread-0] count for [3]Thread[Thread-0] sleep for 0.0 secondsThread[Thread-0] count for [4]Thread[Thread-0] sleep for 0.0 secondsThread[Thread-1] count for [0]Thread[Thread-1] sleep for 0.0 secondsThread[Thread-1] count for [1]Thread[Thread-1] sleep for 0.4 secondsThread[Thread-1] count for [2]Thread[Thread-1] sleep for 0.1 secondsThread[Thread-1] count for [3]Thread[Thread-1] sleep for 0.1 secondsThread[Thread-1] count for [4]Thread[Thread-1] sleep for 0.4 seconds
争用消失。
关键字 synchronized method()
接下来再试试synchronized关键字直接修饰方法:
public static void main(String[] args) { Object globalLock = new Object(); Runnable r1 = new MyRunnable(SyncTypeTag.KEYWARD_METHOD, globalLock); Runnable r2 = new MyRunnable(SyncTypeTag.KEYWARD_METHOD, globalLock); Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); t2.start();}
运行代码:
Thread[Thread-0] count for [0]Thread[Thread-1] count for [0]Thread[Thread-0] sleep for 0.0 secondsThread[Thread-1] sleep for 0.1 secondsThread[Thread-0] count for [1]Thread[Thread-0] sleep for 0.1 secondsThread[Thread-1] count for [1]Thread[Thread-0] count for [2]Thread[Thread-1] sleep for 0.1 secondsThread[Thread-0] sleep for 0.1 secondsThread[Thread-0] count for [3]Thread[Thread-0] sleep for 0.1 secondsThread[Thread-1] count for [2]Thread[Thread-1] sleep for 0.4 secondsThread[Thread-0] count for [4]Thread[Thread-0] sleep for 0.4 secondsThread[Thread-1] count for [3]Thread[Thread-1] sleep for 0.4 secondsThread[Thread-1] count for [4]Thread[Thread-1] sleep for 0.1 seconds
依旧会起争用,说明synchronized修饰方法,等于 synchronized(this)
。
References: