上一篇中我们讨论了线程安全问题,了解了解决线程安全问题的主要两种方式,今天来探讨一下通过同步互斥方式,在java中提供了两种方式来实现同步互斥访问:synchronized和Lock,今天我们来探讨一下synchronized方式。
.1)synchronized方法
被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象。
public class SyncTest {
public void print(Thread thread) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
class SyncThread extends Thread {
@Override
public void run() {
print(Thread.currentThread());
}
}
public static void main(String[] args) {
SyncTest test = new SyncTest();
SyncThread thread1 = test.new SyncThread();
SyncThread thread2 = test.new SyncThread();
thread1.start();
thread2.start();
}
}
结果:
Thread-0:0
Thread-1:0
Thread-0:1
Thread-1:1
Thread-0:2
Thread-1:2
Thread-0:3
Thread-1:3
Thread-0:4
Thread-1:4
我们在方法print 上加上synchronized
public synchronized void print(Thread thread) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
结果集:
Thread-0:0
Thread-0:1
Thread-0:2
Thread-0:3
Thread-0:4
Thread-1:0
Thread-1:1
Thread-1:2
Thread-1:3
Thread-1:4
说明 Thread-1 在 Thread-0执行完之后方开始执行。
现在我们将测试方法修改一下:
public static void main(String[] args) {
SyncThread thread1 = new SyncTest().new SyncThread();
SyncThread thread2 = new SyncTest().new SyncThread();
thread1.start();
thread2.start();
}
我们会发现结果集如下:
Thread-1:0
Thread-0:0
Thread-1:1
Thread-0:1
Thread-1:2
Thread-0:2
Thread-1:3
Thread-0:3
Thread-0:4
Thread-1:4
为什么方法已经synchronized了,却没有我们预期的结果呢,这是因为synchronized只锁定对象(而非代码),每个对象只有一个锁(lock)与之相关联,上面的测试用例中产生了两个SyncTest对象,代码可以这样理解:
public static void main(String[] args) {
SyncTest t1=new SyncTest();
SyncTest t2=new SyncTest();
SyncThread thread1 = t1.new SyncThread();
SyncThread thread2 = t2.new SyncThread();
thread1.start();
thread2.start();
}
thread1访问的是t1的synchronized print方法,thread2访问的是t2的synchronized print方法,synchronized锁定的分别是t1.t2,这两把锁是互不干扰的。
synchronized也可以修饰静态方法
其作用的范围是整个静态方法,作用的对象是这个类的所有对象
在上面的例子中,如果我们将print方法添加修饰符static方法,如下:
public synchronized static void print(Thread thread) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
就会达到我们预期的效果,原因是静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象,虽然t1跟t2是不同对象,但是synchronized对这两个对象都起作用。
.2)synchronized 代码块
被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象
synchronized的形式如下:
synchronized(synObject) {
}
当在某个线程中执行这段代码块,该线程会获取对象synObject的锁,从而使得其他线程无法同时访问该代码块。
synObject可以是this,代表获取当前对象的锁,也可以是类中的一个属性,代表获取该属性的锁。
我们的代码可以调整为:public void print(Thread thread) {
synchronized(this){
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
关于synchronized方法,以下几点需要注意:
(1)当一个线程正在访问一个对象的synchronized方法,那么其他线程能访问该对象的非synchronized方法。
(2)同步加锁的是对象,而不是代码。
(3)实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
参考资料:
http://www.cnblogs.com/dolphin0520/p/3923737.html
http://blog.csdn.net/luoweifu/article/details/46613015