synchronized是一种同步锁,是Java中的关键字,其用法如下:
1 修饰一段代码块(锁定对象)
被synchronized修饰的代码块称之为同步代码块,写法如下,这里锁定的是对象mBook。
synchronized (mBook) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
在Java中,每一个对象都拥有一个锁标记(monitor),也称为监视器,多线程同时访问某个对象时,线程只有获取了该对象的锁才能访问。在同步代码块中,锁定的就是一个对象,就是本例中的mBook。多个线程同时调用锁定mBook的代码块时,只有一个能获得mBook的对象锁,当该线程释放锁之后,另一个线程才能获取锁开始执行同步代码块。
2 修饰一个实例方法(锁定对象)
被synchronized修饰的方法称之为同步方法,修饰一个实例方法的写法如下:
public synchronized void setBookName(String bookName) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.bookName = bookName;
}
修饰实例方法时,和同步代码块中一样,锁定的也是一个对象。线程A调用对象的同步方法时,需要获取对象锁,如果对象锁被另一个线程B持有,线程A会阻塞等待线程B释放锁对象。
3 修饰一个静态方法(锁定类)
synchronized可以修饰一个静态方法,如下:
public synchronized static int getCount() {
mCount++;
return mCount;
}
修饰静态方法时,锁定的不是一个对象,而是这个类。每个类有一个类锁,是用来控制对static成员的并发访问,它和对象锁不是一个东西。例如:线程A调用类T的静态方法getCount(),同时线程B也调用类T的静态方法getCount(),那么只有一个线程能获得T的类锁。
4 修饰一段代码块(锁定类)
synchronized修饰代码块时,也可以锁定类,写法如下:
synchronized (Book.class) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
这里锁定的是类,某个线程调用同步代码块时,获取的是类锁。
5 synchronized用法总结
使用synchronized,可以用来获取对象锁,或者类锁。
每个对象只有一个对象锁,当对象锁被某个线程持有时,其他尝试获得该对象锁的线程都会被阻塞。
每个类只有一个类锁,当类锁被某个线程持有时,其他尝试获得该类锁的线程都会被阻塞。
对象锁和类锁不是一个东西,类锁用来控制对static成员的并发访问。例如:类T有一个实例对象T1,线程A持有对象T1的对象锁时,线程B尝试获取类T的类锁,线程B可以成功获取类锁。线程A和B可以并发执行。
6 synchronized使用实例
定义类Book,声明并实现同步方法,一个为实例同步方法,一个为静态同步方法。
public class Book {
private final String TAG = this.getClass().getSimpleName();
private String bookName;
private static int mCount;
Book(String name) {
bookName = name;
}
public String getBookName() {
return bookName;
}
public synchronized void setBookName(String bookName) {
Log.e(TAG, "setting book name begin");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.bookName = bookName;
Log.e(TAG, "setting book name ends");
}
public synchronized static int getCount() {
Log.e("Book", "getting count begin");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mCount++;
Log.e("Book", "getting count end");
return mCount;
}
}
下面是测试类:
public class SynchronizedTest {
private final String TAG = this.getClass().getSimpleName();
private Book mBook;
public void start() {
mBook = new Book("Little Prince");
//对象锁测试
Task1 task1 = new Task1(1);
Thread thread1 = new Thread(task1);
thread1.start();
Task2 task2 = new Task2(2);
Thread thread2 = new Thread(task2);
thread2.start();
Task3 task3 = new Task3(3);
Thread thread3 = new Thread(task3);
thread3.start();
//类锁测试
Task4 task4 = new Task4(4);
Thread thread4 = new Thread(task4);
thread4.start();
Task5 task5 = new Task5(5);
Thread thread5 = new Thread(task5);
thread5.start();
}
public class Task1 implements Runnable {
int taskId;
byte[] lock = new byte[0];
Task1(int id) {
taskId = id;
}
@Override
public void run() {
synchronized (mBook) {
Log.e(TAG, "task:" + taskId + " begin");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, "task:" + taskId + " end");
}
}
}
public class Task2 implements Runnable {
int taskId;
Task2(int id) {
taskId = id;
}
@Override
public void run() {
synchronized (mBook) {
Log.e(TAG, "task:" + taskId + " begin");
Log.e(TAG, "task:" + taskId + " book name:" + mBook.getBookName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, "task:" + taskId + " end");
}
}
}
public class Task3 implements Runnable {
int taskId;
Task3(int id) {
taskId = id;
}
@Override
public void run() {
//调用实例同步方法,如果该对象的锁被其他线程持有,则阻塞。
mBook.setBookName("Beauty&Beast");
//调用实例同步方法结束,释放锁。
Log.e(TAG, "task:" + taskId + " book name:" + mBook.getBookName());
}
}
public class Task4 implements Runnable {
int taskId;
Task4(int id) {
taskId = id;
}
@Override
public void run() {
//调用静态同步方法,如果该类的锁被其他线程持有,则阻塞。
int count = Book.getCount();
//调用静态同步方法结束,释放锁。
Log.e(TAG, "task:" + taskId + " book count:" + count);
}
}
public class Task5 implements Runnable {
int taskId;
Task5(int id) {
taskId = id;
}
@Override
public void run() {
synchronized (Book.class) {
Log.e(TAG, "task:" + taskId + " begin");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, "task:" + taskId + " end");
}
}
}
}
线程thread1、thread2、thread3执行任务时,需要获取的mBook的对象锁,所以一个线程释放对象锁之后,另一个线程才能获取对象锁并执行同步代码块或同步方法,否则一直阻塞。日志如下:
线程thread4、thread5需要获取的是Book类的类锁,这两个线程中的同步代码块或同步方法是线性执行的。日志如下: