一、概念
基本上所有的并发模式在解决线程安全问题时,都采用"序列化访问临界资源"的方案,即在同一时刻,只能有一个线程访问临界资源,也称作同步互斥访问。通常来说,是在访问临界资源的代码前面加上一个锁,当访问完临界资源后释放锁,让其他线程继续访问。
在Java中,提供了两种方式来实现同步互斥访问:synchronized和Lock。
本节学习synchronized。
synchronized关键字:
当用synchronized来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。
互斥锁:
在Java中,每一个对象都拥有一个锁标记(monitor),也称为监视器,多线程同时访问某个对象时,线程只有获取了该对象的锁才能访问。
二、使用
synchronized代码块
被修饰的代码块成为同步代码块,其作用范围是整个代码块,作用对象分两个情况:
- synchronized(object)
锁住的是单个对象,同一个类的不同对象调用该同步代码块并不会受影响。 - synchronized(class)
锁住的是类的所有对象,同一个类的所有对象调用该同步代码块都会受影响。
synchronized普通成员方法
被修饰的方法成为同步方法,其作用范围是整个方法,作用对象是调用这个方法的对象。
synchronized静态成员方法
被修饰的方法成为同步方法,其作用范围是整个方法,作用对象是类的所有对象。
三、注意事项
1.当两个并发线程访问同一个对象中的synchronized代码块时,在同一时刻只能有一个线程得到执行,另一个线程受阻塞,必须等待当前线程执行完这个代码块以后才能执行该代码块。
2.两个线程使用的是同一个对象,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object的非synchronized(this)同步代码块。
3.两个线程使用的是同一个对象,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
四、例子
class MyThread extends Thread {
private MyThread(String name) {
setName(name);
}
@Override
public void run() {
Log.d(TAG, "zwm, thread: " + getName() + " run start");
synchronized (MyThread.class) { //class
Log.d(TAG, "zwm, thread: " + getName() + " enter synchronized");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "zwm, thread: " + getName() + " exit synchronized");
}
Log.d(TAG, "zwm, thread: " + getName() + " run end");
}
}
//测试代码
MyThread thread1 = new MyThread("thread1");
thread1.start();
MyThread thread2 = new MyThread("thread2");
thread2.start();
//输出
2019-02-11 15:49:23.691 zwm, thread: thread2 run start
2019-02-11 15:49:23.691 zwm, thread: thread2 enter synchronized
2019-02-11 15:49:23.691 zwm, thread: thread1 run start
2019-02-11 15:49:28.691 zwm, thread: thread2 exit synchronized
2019-02-11 15:49:28.691 zwm, thread: thread2 run end
2019-02-11 15:49:28.692 zwm, thread: thread1 enter synchronized
2019-02-11 15:49:33.694 zwm, thread: thread1 exit synchronized
2019-02-11 15:49:33.695 zwm, thread: thread1 run end
class MyThread extends Thread {
private MyThread(String name) {
setName(name);
}
@Override
public void run() {
Log.d(TAG, "zwm, thread: " + getName() + " run start");
synchronized (this) { //object
Log.d(TAG, "zwm, thread: " + getName() + " enter synchronized");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "zwm, thread: " + getName() + " exit synchronized");
}
Log.d(TAG, "zwm, thread: " + getName() + " run end");
}
}
//测试代码
MyThread thread1 = new MyThread("thread1");
thread1.start();
MyThread thread2 = new MyThread("thread2");
thread2.start();
//输出
2019-02-11 15:56:45.404 zwm, thread: thread2 run start
2019-02-11 15:56:45.404 zwm, thread: thread2 enter synchronized
2019-02-11 15:56:45.404 zwm, thread: thread1 run start
2019-02-11 15:56:45.404 zwm, thread: thread1 enter synchronized
2019-02-11 15:56:50.404 zwm, thread: thread2 exit synchronized
2019-02-11 15:56:50.405 zwm, thread: thread2 run end
2019-02-11 15:56:50.405 zwm, thread: thread1 exit synchronized
2019-02-11 15:56:50.405 zwm, thread: thread1 run end