多线程同步中的门道(二)
前言
在涉及到多线程的开发时,线程同步的考虑是不可缺少的,否则很可能会造成各种超出预料的错误结果。以自己的学习经历来说,对于刚开始接触线程同步的人可能会感觉非常简单,在多线程操作可能会造成数据混乱的地方同步一下不就行了嘛,加个synchronized关键字,多简单!可是随着开发的深入,会渐渐的发现仅仅是一个synchronized关键字也不是那么简单,里面的门道和考虑到的情况还是不少。本系列就着循序渐进的程序和大家探讨一下synchronized关键字使用中的各种情形和会造成的各种意料之外和意料之中的结果,欢迎各位大神轻拍。
转载请注明本文地址:http://www.cnblogs.com/hellojava/p/3635336.html
系列文章:多线程同步中的门道(一)
synchronized涉及到同步方法、同步代码块、同步类、同步对象、静态方法等,本系列来挨个探讨。
注:因为考虑到文章篇幅和为了突出我们要分析的关键代码,所以下面程序有可能不会是最优写法。
同步代码块
同步代码块,分为同步对象和同步类两种,下面我们来挨个介绍。
同步对象
我们首先来看看一个同步对象的例子。
[测试程序4.1]
/** * Test case 4.1. synchronized code block.synchronized object. */
public class Test { public static void main(String[] args) { // The same object.
final TestCase test1 = new TestCase(); // final TestCase test2 = new TestCase();
Thread thread1 = new Thread() { @Override public void run() { test1.function(); } }; Thread thread2 = new Thread() { @Override public void run() { test1.function(); } }; thread2.start(); thread1.start(); } } class TestCase { public void function() { // synchronized object.
synchronized (this) { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " executed result: " + i); } } } }
程序中使用了同步代码块,同步的是对象。把function中的方法全部给包括起来了,这样进入这个方法的线程会被同步。注意上面main测试程序中两个线程调用的都是同一个对象的function方法。
运行程序,始终会得到如下结果:
Thread-1 executed result: 0 Thread-1 executed result: 1 Thread-1 executed result: 2 Thread-1 executed result: 3 Thread-1 executed result: 4 Thread-0 executed result: 0 Thread-0 executed result: 1 Thread-0 executed result: 2 Thread-0 executed result: 3 Thread-0 executed result: 4
从运行结果可以看出,两个线程顺序执行。这就意味着对一个对象的同步是成功的,当一个线程进入这个对象的function方法中的同步代码块时,就锁住了这个对象,第二个线程在想进入这个同步代码块时,就要等待第一个线程执行完同步代码块或抛出异常。
但是先别急,我们来看看对不同对象是否还会起作用。
[测试程序4.2]
/** * Test case 4.2. synchronized code block.synchronized object. */
public class Test { public static void main(String[] args) { // The different objects.
final TestCase test1 = new TestCase(); final TestCase test2 = new TestCase(); Thread thread1 = new Thread() { @Override public void run() { test1.function(); } }; Thread thread2 = new Thread() { @Override public void run() { test2.function(); } }; thread2.start(); thread1.start(); } } class TestCase { public void function() { // synchronized object.
synchronized (this) { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " executed result: " + i); } } } }
对4.1的程序稍作修改,演变成了4.2程序。两个程序的区别就是两个线程分别调用了TestCase类的两个不同的对象的function方法。
执行程序,某次运行结果如下所示:
Thread-1 executed result: 0 Thread-1 executed result: 1 Thread-1 executed result: 2 Thread-0 executed result: 0 Thread-0 executed result: 1 Thread-0 executed result: 2 Thread-0 executed result: 3 Thread-1 executed result: 3 Thread-0 executed result: 4 Thread-1 executed result: 4
在同步代码块中,对对象的同步,当两个线程使用不同的对象时,就不会顺序执行了,而是交叉执行。说明同步不了不同的对象。
同步小结
在同步代码块中,对对象同步时
同步类
上面同步对象的方式无法对不同的对象作线程同步,我们就来同步类看看。
[测试程序5.1]
/** * Test case 5.1. synchronized code block.synchronized class. */
public class Test { public static void main(String[] args) { // The different objects.
final TestCase test1 = new TestCase(); final TestCase test2 = new TestCase(); Thread thread1 = new Thread() { @Override public void run() { test1.function(); } }; Thread thread2 = new Thread() { @Override public void run() { test2.function(); } }; thread2.start(); thread1.start(); } } class TestCase { public void function() { // synchronized class.
synchronized (TestCase.class) { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " executed result: " + i); } } } }
上面的程序,在同步代码块那里,可以看出是对类进行同步。并且两个线程是分别执行两个不同对象的function方法。
运行一下,总是输出结果:
Thread-1 executed result: 0 Thread-1 executed result: 1 Thread-1 executed result: 2 Thread-1 executed result: 3 Thread-1 executed result: 4 Thread-0 executed result: 0 Thread-0 executed result: 1 Thread-0 executed result: 2 Thread-0 executed result: 3 Thread-0 executed result: 4
两个线程顺序执行,可以看出,当同步类的时候,即便是不同的线程执行了这个类的不同对象,线程之间也是同步的。至于其他情形,可以自己做测试用例来验证。
同步类小结
在同步代码块中,同步类是对类上锁。当一个线程进入了同步代码块,就对类上锁,其他线程无法进入这个类的其他对象的同步方法。