Java中,synchronized关键字有2种用法:
- 作为关键字修饰方法
- 修饰一个代码块
[TOC]
线程争用
为了探究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 seconds
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [1]
Thread[Thread-0] sleep for 0.4 seconds
Thread[Thread-1] count for [1]
Thread[Thread-1] sleep for 0.0 seconds
Thread[Thread-1] count for [2]
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-1] count for [3]
Thread[Thread-0] count for [2]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [3]
Thread[Thread-0] sleep for 0.4 seconds
Thread[Thread-1] count for [4]
Thread[Thread-1] sleep for 0.4 seconds
Thread[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 seconds
Thread[Thread-0] count for [1]
Thread[Thread-0] sleep for 0.0 seconds
Thread[Thread-0] count for [2]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [3]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [4]
Thread[Thread-0] sleep for 0.4 seconds
Thread[Thread-1] count for [0]
Thread[Thread-1] sleep for 0.0 seconds
Thread[Thread-1] count for [1]
Thread[Thread-1] sleep for 0.0 seconds
Thread[Thread-1] count for [2]
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-1] count for [3]
Thread[Thread-1] sleep for 0.1 seconds
Thread[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 seconds
Thread[Thread-0] sleep for 0.4 seconds
Thread[Thread-1] count for [1]
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-1] count for [2]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-0] count for [1]
Thread[Thread-0] sleep for 0.4 seconds
Thread[Thread-1] count for [3]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-0] count for [2]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [3]
Thread[Thread-0] sleep for 0.0 seconds
Thread[Thread-0] count for [4]
Thread[Thread-0] sleep for 0.1 seconds
Thread[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;
}
@Override
public 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 seconds
Thread[Thread-0] count for [1]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [2]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [3]
Thread[Thread-0] sleep for 0.0 seconds
Thread[Thread-0] count for [4]
Thread[Thread-0] sleep for 0.0 seconds
Thread[Thread-1] count for [0]
Thread[Thread-1] sleep for 0.0 seconds
Thread[Thread-1] count for [1]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-1] count for [2]
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-1] count for [3]
Thread[Thread-1] sleep for 0.1 seconds
Thread[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 seconds
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-0] count for [1]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-1] count for [1]
Thread[Thread-0] count for [2]
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [3]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-1] count for [2]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-0] count for [4]
Thread[Thread-0] sleep for 0.4 seconds
Thread[Thread-1] count for [3]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-1] count for [4]
Thread[Thread-1] sleep for 0.1 seconds
依旧会起争用,说明synchronized修饰方法,等于 synchronized(this)
。
References:
http://www.cnblogs.com/GnagWang/archive/2011/02/27/1966606.html