在Java代码中,多线程的环境下很多时候需要用到synchronized来控制线程同步的。
synchronized是Java中的关键字,是一种同步锁。它的用法主要分为一下几种:
1. 修饰一个普通方法:
被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的this对象;
测试:
public class BaseBean {/*** synchronized修饰普通方法*/public synchronized void test(String name){System.out.println(name + ",普通test方法:进入锁");try {Thread.currentThread().sleep(5*1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name + ",普通test方法:离开锁");}/*** synchronized修饰普通方法*/public synchronized void putongtest(String name){System.out.println(name + ",普通putongtest方法:进入");try {Thread.currentThread().sleep(3*1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name + ",普通putongtest方法:离开");}out.println(name + ",普通方法:离开");}
}
测试方法:
public static void main(String[] args ){BaseBean baseBean = new BaseBean();Thread thread1 = new Thread("线程1"){@Overridepublic void run() {baseBean.test("线程1");}};BaseBean baseBean1 = new BaseBean();Thread thread2 = new Thread("线程2"){@Overridepublic void run() {baseBean1.test("线程2");//baseBean1.test("线程2");//baseBean.putongtest("线程2");}};thread1.start();thread2.start();}
首先测试不同线程同一个对象调用同一个同步方法;再测试不同线程不同对象调用同一个同步方法;最后再测试不同线程同一个对象调用不同的同步方法。得到测试结果如下:
线程同一个对象调用同一个同步方法。线程1,普通test方法:进入锁线程1,普通test方法:离开锁线程2,普通test方法:进入锁线程2,普通test方法:离开锁不同线程不同对象调用同一个同步方法。线程1,普通test方法:进入锁线程2,普通test方法:进入锁线程1,普通test方法:离开锁线程2,普通test方法:离开锁不同线程同一个对象调用不同的同步方法线程1,普通test方法:进入锁线程1,普通test方法:离开锁线程2,普通putongtest方法:进入线程2,普通putongtest方法:离开
可以得到结论:同一个对象的所有synchronized修饰的普通方法都公用一个this对象。这些方法再多线程环境中是线程串行地执行,同一时间只能运行一个线程,其他线程处于等待状态。当我们多次new不同的对象时,每个方法的this对象不一致。可并行运行。
2. 修饰代码块
被修饰的代码块称为同步代码块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象this;synchronized修饰普通方法和synchronized修饰this代码块的处理方式其实是一致的。
/*** synchronized修饰代码块,对象是this*/public void test2(String name){synchronized(this){System.out.println(name + ",synchronized修饰test2代码块,对象是this:进入锁");try {Thread.currentThread().sleep(3 * 1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name + ",synchronized修饰test2代码块:离开锁");}}/*** synchronized修饰代码块,对象是this*/public void putongtest2(String name){synchronized(this){System.out.println(name + ",synchronized修饰putongtest2代码块,对象是this:进入锁");try {Thread.currentThread().sleep(3 * 1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name + ",synchronized修饰putongtest2代码块:离开锁");}}
这里就不展示测试结果了。synchronized修饰普通方法和synchronized修饰this代码块的处理方式其实是一致的。
在某些时候synchronized(obj)修饰的代码块,而且obj自己的方法中包含了 synchronized(this)代码块或者synchronized修饰的普通方法。那么这些代码公用一个obj对象锁,这些代码都是同步的。
测试方法如下;
构建一个类里面有不同的synchronized方法和代码块。
public class TestObject {synchronized public void test1(){System.out.println("TestObject-test1方法执行");try {Thread.currentThread().sleep(10* 1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("TestObject-test1方法结束");}synchronized public void test2(){System.out.println("TestObject-test2方法执行");try {Thread.currentThread().sleep(10* 1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("TestObject-test2方法结束");}public void test3(){synchronized (this) {System.out.println("TestObject-test3方法执行");try {Thread.currentThread().sleep(10* 1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("TestObject-test3方法结束");}}
}
在一个线程中分别执行这些方法。
public class TestThread extends Thread {TestObject testObject;String msg;public TestThread(TestObject testObject, String msg){this.testObject = testObject;this.msg = msg;}@Overridepublic void run(){try {if ("1".equals(msg)) {testObject.test1();} else if ("2".equals(msg)) {testObject.test2();} else if ("3".equals(msg)) {testObject.test3();} else {synchronized (testObject) {System.out.println("TestThread方法执行");try {Thread.currentThread().sleep(10* 1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("TestThread方法结束");}}}catch (Exception e){e.printStackTrace();}}
}
多线程进行测试:
public static void main(String[] args){TestObject testObject = new TestObject();TestThread testThread = new TestThread(testObject, "1");TestThread testThread1 = new TestThread(testObject, "2");TestThread testThread2 = new TestThread(testObject, "3");TestThread testThread3 = new TestThread(testObject, "4");testThread.start();testThread1.start();testThread2.start();testThread3.start();}
测试结果如下:
TestThread方法执行
TestThread方法结束
TestObject-test1方法执行
TestObject-test1方法结束
TestObject-test2方法执行
TestObject-test2方法结束
TestObject-test3方法执行
TestObject-test3方法结束
可以看到这些方法和代码块都是同步的。
3. 修改一个静态的方法:
其作用的范围是整个静态方法,作用的对象是这个类的所有对象,也就是class对象。
/*** synchronized修饰静态方法*/public synchronized static void staticTest(String name){System.out.println(name + ",静态方法staticTest:进入锁");try {Thread.currentThread().sleep(5*1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name + ",普通方法staticTest:离开锁");}/*** synchronized修饰静态方法*/public synchronized static void staticTest2(String name){System.out.println(name + ",静态方法staticTest2:进入锁");try {Thread.currentThread().sleep(5*1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name + ",普通方法staticTest2:离开锁");}
测试方式:
public static void main(String[] args ){BaseBean baseBean = new BaseBean();Thread thread1 = new Thread("线程1"){@Overridepublic void run() {baseBean.staticTest("线程1");}};BaseBean baseBean1 = new BaseBean();Thread thread2 = new Thread("线程2"){@Overridepublic void run() {//baseBean.staticTest2("线程2");//baseBean1.staticTest2("线程2");baseBean.staticTest2("线程2");}};thread1.start();thread2.start();}
首先测试不同线程同一个对象调用同一个同步方法;再测试不同线程不同对象调用同一个同步方法;最后再测试不同线程同一个对象调用不同的同步方法。得到测试结果如下:
测试 不同线程 同一个对象 调用 同一个同步代码块:
线程2,静态方法staticTest:进入锁
线程2,普通方法staticTest:离开锁
线程1,静态方法staticTest:进入锁
线程1,普通方法staticTest:离开锁测试 不同线程 同一个对象 调用 不同的同步代码块:
线程2,静态方法staticTest:进入锁
线程2,普通方法staticTest:离开锁
线程1,静态方法staticTest:进入锁
线程1,普通方法staticTest:离开锁测试 不同线程 不同对象 调用 不同的同步代码块:
线程2,静态方法staticTest2:进入锁
线程2,普通方法staticTest2:离开锁
线程1,静态方法staticTest:进入锁
线程1,普通方法staticTest:离开锁测试 不同线程 同一个对象调用 不同的同步代码块:
线程1,静态方法staticTest:进入锁
线程1,普通方法staticTest:离开锁
线程2,静态方法staticTest2:进入锁
线程2,普通方法staticTest2:离开锁
可以得到结论:同一个bean的所有对象的所有synchronized修饰的静态方法都公用一个class对象。这些方法再多线程环境中是线程串行地执行,同一时间只能运行一个线程,其他线程处于等待状态。当我们多次new不同的对象时,每个方法的class对象不变。同一时间只能运行一个线程,其他线程处于等待状态。
4. 修改一个类(class)/静态成员变量:
作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象,即class对象。这个结果和synchronized修饰静态方法是同样的结果,这里就不再重复测试了。
synchronized修饰的代码中内层抛出异常,跳出synchronized代码块,会释放锁。所以其他的线程可以继续执行synchronized修饰的代码。
synchronized虽然是用来来定义方法,但synchronized并不属于方法定义的一部分。因此,synchronized关键字不能被继承。如果在父类中的某个方法使用了synchronized关键字,而在子类中如果重写方法,在子类中的这个方法如果需要同步就必须加上synchronized关键字才可以。