多线程同步中的门道(一)html
在涉及到多线程的开发时,线程同步的考虑是不可缺乏的,不然极可能会形成各类超出预料的错误结果。以本身的学习经从来说,对于刚开始接触线程同步的人可能会感受很是简单,在多线程操做可能会形成数据混乱的地方同步一下不就好了嘛,加个synchronized关键字,多简单!但是随着开发的深刻,会渐渐的发现仅仅是一个synchronized关键字也不是那么简单,里面的门道和考虑到的状况仍是很多。本系列就着按部就班的程序和你们探讨一下synchronized关键字使用中的各类情形和会形成的各类意料以外和意料之中的结果,欢迎各位大神轻拍。java
转载请注明本文地址:http://www.cnblogs.com/hellojava/p/3630395.html安全
synchronized涉及到同步方法、同步代码块、同步类、同步对象、静态方法等,本系列来挨个探讨。多线程
注:由于考虑到文章篇幅和为了突出咱们要分析的关键代码,因此下面程序有可能不会是最优写法。ide
未做线程同步学习
咱们先来看看,在多线程运行下,未做线程同步的程序。测试
[测试程序1]spa
/** * Test case 1. * There is no thread synchronization. */
public class Test { public static void main(String[] args) { final TestCase test = new TestCase(); Thread thread1 = new Thread() { @Override public void run() { test.function(); } }; Thread thread2 = new Thread() { @Override public void run() { test.function(); } }; thread1.start(); thread2.start(); } } class TestCase { public void function() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " executed result: " + i); } } }
上面的测试程序很简单,定义了一个测试用例类,类中有一个循环输出5次”线程名+输出次数”的方法。而后设置了两个线程,启动这两个线程跑这个测试用例对象的方法,查看会有什么样的输出结果。后面的测试程序基本都是在此程序上修改变化而出,用来测试不一样状况。线程
运行程序,某次运行的结果可能以下:code
Thread-0 executed result: 0 Thread-1 executed result: 0 Thread-1 executed result: 1 Thread-0 executed result: 1 Thread-1 executed result: 2 Thread-1 executed result: 3 Thread-1 executed result: 4 Thread-0 executed result: 2 Thread-0 executed result: 3 Thread-0 executed result: 4
从程序输出结果能够看出,Thread-0和Thread-1是无规则交叉输出的,也就意味着在未做线程同步的状况下,两个线程同时执行着TestCase的function方法,这种是属于线程不安全的。
同步方法
咱们对上面的程序进行一下修改,加一个synchronized关键字用来同步方法。
[测试程序2.1]
/** * Test case 2.1. synchronized method. */
public class Test { public static void main(String[] args) { final TestCase test = new TestCase(); Thread thread1 = new Thread() { @Override public void run() { test.function(); } }; Thread thread2 = new Thread() { @Override public void run() { test.function(); } }; thread1.start(); thread2.start(); } } class TestCase { public synchronized void function() {// add synchronized keyword.
for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " executed result: " + i); } } }
运行程序,获得的输出结果老是以下:
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 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-1进入function方法后,线程Thread-2在方法外等待,等Thread-1执行完后释放锁,Thread-2才进入方法执行。
可是咱们对上面的代码稍做修改,看看同步方法,对于不一样的对象状况下是否都有线程同步的效果。
[测试程序2.2]
/** * Test case 2.2. synchronized method but different objects. */
public class Test { public static void main(String[] args) { // There are two 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(); } }; thread1.start(); thread2.start(); } } class TestCase { public synchronized void function() {// add synchronized keyword.
for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " executed result: " + i); } } }
执行程序,某次的运行结果以下:
Thread-0 executed result: 0 Thread-0 executed result: 1 Thread-1 executed result: 0 Thread-1 executed result: 1 Thread-0 executed result: 2 Thread-0 executed result: 3 Thread-0 executed result: 4 Thread-1 executed result: 2 Thread-1 executed result: 3 Thread-1 executed result: 4
从以上结果能够看出,同步方法时,当一个线程进入一个对象的这个同步方法时,另外一个线程能够进入这个类的别的对象的同一个同步方法。
同步方法小结
在多线程中,同步方法时:
对静态方法的同步
上面是对普通的方法进行同步,发现只能锁对象。那么此次咱们试着同步静态方法看会有什么结果,与上面的[测试程序2.2]来进行对比。
[测试程序3.1]
/** * Test case 3.1. synchronized static method. */
public class Test { public static void main(String[] args) { // There are two 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(); } }; thread1.start(); thread2.start(); } } class TestCase { public synchronized static void function() {// add static keyword.
for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " executed result: " + i); } } }
执行程序,运行结果老是以下:
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 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
从结果能够看出,两个线程顺序执行。而上面的[测试程序2.2]两个线程是交叉输出的。为何同步静态方法会起到两个对象也能同步的了呢?
由于被static修饰的方法是静态方法,也被称为类方法,代表这个方法是属于类的。与普通方法的区别是,在类被加载进内存时,就分配了方法的入口地址,而普通方法要实例化对象后才分配方法的入口地址。
因此对静态方法进行同步,至关因而锁住了类,当一个线程进入这个静态方法时,其余线程不管使用这个类的哪一个对象都没法进入这个静态方法,直到上一个线程执行完毕释放锁。
一样的当一个线程进入这个静态方法时,其余线程也进不去这个类的其余被同步的静态方法,由于只要是静态方法都是属于类的嘛。可是能够进入其余未同步的方法(包括静态方法)。这些能够本身来测试,就不上例子了。
可是这种方式,与彻底的同步类又有些区别,咱们能够继续看下面的程序。
[测试程序3.2]
/** * Test case 3.2. synchronized static method. */
public class Test { public static void main(String[] args) { // There are two objects.
final TestCase test = new TestCase(); Thread thread1 = new Thread() { @Override public void run() { test.function1(); } }; Thread thread2 = new Thread() { @Override public void run() { test.function2(); } }; thread1.start(); thread2.start(); } } class TestCase { public synchronized static void function1() {// add static keyword.
for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " executed result: " + i); } } public synchronized void function2() {// no static keyword.
for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " executed result: " + i); } } }
执行程序,某次的运行结果以下:
Thread-0 executed result: 0 Thread-1 executed result: 0 Thread-1 executed result: 1 Thread-1 executed result: 2 Thread-0 executed result: 1 Thread-0 executed result: 2 Thread-1 executed result: 3 Thread-1 executed result: 4 Thread-0 executed result: 3 Thread-0 executed result: 4
从以上结果能够看出,虽然静态方法和普通方法都被同步,虽然是对同一个对象,可是两个线程仍然交叉执行。说明当一个线程进入了类的静态同步方法,其余线程能够进入这个类的非静态的同步方法。
同步静态方法小结
在多线程中,同步静态方法时: