本文主要介绍Sychronized基本用法以及规则,然后介绍下对象锁和类所
sychronized基本用法
sychronized主要由俩种用法,一种是用来修饰方法,另外一种是用来修饰代码块。
synchronized方法示例
1 2 3
| public synchronized void foo1() { System.out.println("synchronized methoed"); }
|
synchronized代码块
1 2 3 4 5
| public void foo2() { synchronized (this) { System.out.println("synchronized methoed"); } }
|
synchronized代码块中的this是指当前对象。也可以将this替换成其他对象,例如将this替换成obj,则foo2()在执行synchronized(obj)时就获取的是obj的同步锁。
synchronized代码块可以更精确的控制冲突限制访问区域,有时候表现更高效率(并不是在所有情况下是最好的)。下面通过一个示例来演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class Demo4 {
public synchronized void synMethod() { for (int i = 0; i < 1000000; i++) ; }
public void synBlock() { synchronized (this) { for (int i = 0; i < 1000000; i++) ; } }
public static void main(String[] args) { Demo4 demo = new Demo4();
long start, diff; start = System.currentTimeMillis(); demo.synMethod(); diff = System.currentTimeMillis() - start; System.out.println("synMethod() : " + diff);
start = System.currentTimeMillis(); demo.synBlock(); diff = System.currentTimeMillis() - start; System.out.println("synBlock() : " + diff); } }
|
下面是在我电脑上运行的结果:
1 2
| synMethod() : 2 synBlock() : 3
|
sychronized基本规则
我们将synchronized的基本规则总结为下面3条,并通过实例对它们进行说明。
第一条:当一个线程访问某对象的synchronized方法或者synchronized代码块时,其他线程对该对象的该synchronized方法或者synchronized代码块的访问将被阻塞。
下面是synchronized代码块对应的演示程序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| class MyRunable implements Runnable {
@Override public void run() { synchronized (this) { try { for (int i = 0; i < 5; i++) { Thread.sleep(100); System.out.println(Thread.currentThread().getName() + " loop " + i); } } catch (InterruptedException ie) { } } } }
public class Demo1_1 {
public static void main(String[] args) { Runnable demo = new MyRunable();
Thread t1 = new Thread(demo, "t1");
Thread t2 = new Thread(demo, "t2"); t1.start(); t2.start(); } }
|
运行结果
1 2 3 4 5 6 7 8 9 10
| t1 loop 0 t1 loop 1 t1 loop 2 t1 loop 3 t1 loop 4 t2 loop 0 t2 loop 1 t2 loop 2 t2 loop 3 t2 loop 4
|
结果说明:
run()方法中存在synchronized(this)代码块,而且t1和t2都是基于demo这个Runnable对象创建的线程。这就意味着,我们可以将synchronized(this)中的this看作是demo这个Runnable对象;因此,线程t1和t2共享demo对象的同步锁。所以,当一个线程运行的时候,另外一个线程必须等待运行线程释放demo的同步锁之后才能运行。
如果你确认,你搞清楚这个问题了。那我们将上面的代码进行修改,然后再运行看看结果怎么样,看看你是否会迷糊。修改后的源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class MyThread extends Thread { public MyThread(String name) { super(name); }
@Override public void run() { synchronized(this) { try { for (int i = 0; i < 5; i++) { Thread.sleep(100); System.out.println(Thread.currentThread().getName() + " loop " + i); } } catch (InterruptedException ie) { } } } }
public class Demo1_2 {
public static void main(String[] args) { Thread t1 = new MyThread("t1"); Thread t2 = new MyThread("t2"); t1.start(); t2.start(); } }
|
代码说明: 比较Demo1_2 和 Demo1_1,我们发现,Demo1_2中的MyThread类是直接继承于Thread,而且t1和t2都是MyThread子线程。 幸运的是,在Demo1_2的run()方法也调用了synchronized(this),正如Demo1_1的run()方法也调用了synchronized(this)一样! 那么,Demo1_2的执行流程是不是和Demo1_1一样呢?
运行结果
1 2 3 4 5 6 7 8 9 10
| t1 loop 0 t2 loop 0 t1 loop 1 t2 loop 1 t1 loop 2 t2 loop 2 t1 loop 3 t2 loop 3 t1 loop 4 t2 loop 4
|
结果说明:
如果这个结果一点也不令你感到惊讶,那么我相信你对synchronized和this的认识已经比较深刻了。否则的话,请继续阅读这里的分析。
synchronized(this)中的this是指当前的类对象,即synchronized(this)所在的类对应的当前对象。它的作用是获取当前对象的同步锁。
对于Demo1_2中,synchronized(this)中的this代表的是MyThread对象,而t1和t2是两个不同的MyThread对象,因此t1和t2在执行synchronized(this)时,获取的是不同对象的同步锁。对于Demo1_1对而言,synchronized(this)中的this代表的是MyRunable对象;t1和t2共同一个MyRunable对象,因此,一个线程获取了对象的同步锁,会造成另外一个线程等待。
第二条: 当一个线程访问某对象的synchronized方法或者synchronized代码块时,其他线程仍然可以访问该对象的非同步代码块。 下面是synchronized代码块对应的演示程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| class Count {
public void synMethod() { synchronized(this) { try { for (int i = 0; i < 5; i++) { Thread.sleep(100); System.out.println(Thread.currentThread().getName() + " synMethod loop " + i); } } catch (InterruptedException ie) { } } }
public void nonSynMethod() { try { for (int i = 0; i < 5; i++) { Thread.sleep(100); System.out.println(Thread.currentThread().getName() + " nonSynMethod loop " + i); } } catch (InterruptedException ie) { } } }
public class Demo2 {
public static void main(String[] args) { final Count count = new Count(); Thread t1 = new Thread( new Runnable() { @Override public void run() { count.synMethod(); } }, "t1");
Thread t2 = new Thread( new Runnable() { @Override public void run() { count.nonSynMethod(); } }, "t2");
t1.start(); t2.start(); } }
|
运行结果:
1 2 3 4 5 6 7 8 9 10
| t1 synMethod loop 0 t2 nonSynMethod loop 0 t1 synMethod loop 1 t2 nonSynMethod loop 1 t1 synMethod loop 2 t2 nonSynMethod loop 2 t1 synMethod loop 3 t2 nonSynMethod loop 3 t1 synMethod loop 4 t2 nonSynMethod loop 4
|
结果说明: 主线程中新建了两个子线程t1和t2。t1会调用count对象的synMethod()方法,该方法内含有同步块;而t2则会调用count对象的nonSynMethod()方法,该方法不是同步方法。t1运行时,虽然调用synchronized(this)获取count的同步锁;但是并没有造成t2的阻塞,因为t2没有用到count同步锁。
第三条:当一个线程访问某对象的synchronized方法或者synchronized代码块时,其他线程对该对象的其他的synchronized方法或者synchronized代码块的访问将被阻塞。 我们将上面的例子中的nonSynMethod()方法体的也用synchronized(this)修饰。修改后的源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| class Count {
public void synMethod() { synchronized(this) { try { for (int i = 0; i < 5; i++) { Thread.sleep(100); System.out.println(Thread.currentThread().getName() + " synMethod loop " + i); } } catch (InterruptedException ie) { } } }
public void nonSynMethod() { synchronized(this) { try { for (int i = 0; i < 5; i++) { Thread.sleep(100); System.out.println(Thread.currentThread().getName() + " nonSynMethod loop " + i); } } catch (InterruptedException ie) { } } } }
public class Demo3 {
public static void main(String[] args) { final Count count = new Count(); Thread t1 = new Thread( new Runnable() { @Override public void run() { count.synMethod(); } }, "t1");
Thread t2 = new Thread( new Runnable() { @Override public void run() { count.nonSynMethod(); } }, "t2");
t1.start(); t2.start(); } }
|
运行结果:
1 2 3 4 5 6 7 8 9 10
| t1 synMethod loop 0 t1 synMethod loop 1 t1 synMethod loop 2 t1 synMethod loop 3 t1 synMethod loop 4 t2 nonSynMethod loop 0 t2 nonSynMethod loop 1 t2 nonSynMethod loop 2 t2 nonSynMethod loop 3 t2 nonSynMethod loop 4
|
结果说明: 主线程中新建了两个子线程t1和t2。t1和t2运行时都调用synchronized(this),这个this是Count对象(count),而t1和t2共用count。因此,在t1运行时,t2会被阻塞,等待t1运行释放count对象的同步锁,t2才能运行。
对象锁和类索
对象锁 – 锁在某一个实例对象上。如果该类是单例,那么该锁也和类锁具有相同的效果。
类锁 – 该锁针对的是类,无论实例多少个对象,那么线程都共享该锁。 类锁对应的就是static synchronized(或者是锁在该类的class或者classloader对象上)。
关于实例锁和全局锁有一个很形象的例子:
1 2 3 4 5 6
| pulbic class Something { public synchronized void isSyncA(){} public synchronized void isSyncB(){} public static synchronized void cSyncA(){} public static synchronized void cSyncB(){} }
|
假设,Something有两个实例x和y。分析下面4组表达式获取的锁的情况。
(01) x.isSyncA()与x.isSyncB()
(02) x.isSyncA()与y.isSyncA()
(03) x.cSyncA()与y.cSyncB()
(04) x.isSyncA()与Something.cSyncA()
(01) 不能被同时访问:因为isSyncA()和isSyncB()都是访问同一个对象(对象x)的同步锁!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| class Something { public synchronized void isSyncA(){ try { for (int i = 0; i < 5; i++) { Thread.sleep(100); System.out.println(Thread.currentThread().getName()+" : isSyncA"); } }catch (InterruptedException ie) { } } public synchronized void isSyncB(){ try { for (int i = 0; i < 5; i++) { Thread.sleep(100); System.out.println(Thread.currentThread().getName()+" : isSyncB"); } }catch (InterruptedException ie) { } } }
public class LockTest1 {
Something x = new Something(); Something y = new Something();
private void test1() { Thread t11 = new Thread( new Runnable() { @Override public void run() { x.isSyncA(); } }, "t11");
Thread t12 = new Thread( new Runnable() { @Override public void run() { x.isSyncB(); } }, "t12");
t11.start(); t12.start(); }
public static void main(String[] args) { LockTest1 demo = new LockTest1(); demo.test1(); } }
|
运行结果:
1 2 3 4 5 6 7 8 9 10
| t11 : isSyncA t11 : isSyncA t11 : isSyncA t11 : isSyncA t11 : isSyncA t12 : isSyncB t12 : isSyncB t12 : isSyncB t12 : isSyncB t12 : isSyncB
|
(02) 可以同时被访问。因为访问的不是同一个对象的同步锁,x.isSyncA()访问的是x的同步锁,而y.isSyncA()访问的是y的同步锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| class Something { public synchronized void isSyncA(){ try { for (int i = 0; i < 5; i++) { Thread.sleep(100); System.out.println(Thread.currentThread().getName()+" : isSyncA"); } }catch (InterruptedException ie) { } } public synchronized void isSyncB(){ try { for (int i = 0; i < 5; i++) { Thread.sleep(100); System.out.println(Thread.currentThread().getName()+" : isSyncB"); } }catch (InterruptedException ie) { } } }
public class LockTest1 {
Something x = new Something(); Something y = new Something();
private void test1() { Thread t11 = new Thread( new Runnable() { @Override public void run() { x.isSyncA(); } }, "t11");
Thread t12 = new Thread( new Runnable() { @Override public void run() { x.isSyncB(); } }, "t12");
t11.start(); t12.start(); }
public static void main(String[] args) { LockTest1 demo = new LockTest1(); demo.test1(); } }
|
运行结果:
1 2 3 4 5 6 7 8 9 10
| t21 : isSyncA t22 : isSyncA t21 : isSyncA t22 : isSyncA t21 : isSyncA t22 : isSyncA t21 : isSyncA t22 : isSyncA t21 : isSyncA t22 : isSyncA
|
(03) 不能被同时访问。因为cSyncA()和cSyncB()都是static类型,x.cSyncA()相当于Something.isSyncA(),y.cSyncB()相当于Something.isSyncB(),因此它们共用一个同步锁,不能被同时反问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| class Something { public synchronized void isSyncA(){ try { for (int i = 0; i < 5; i++) { Thread.sleep(100); System.out.println(Thread.currentThread().getName()+" : isSyncA"); } }catch (InterruptedException ie) { } } public synchronized void isSyncB(){ try { for (int i = 0; i < 5; i++) { Thread.sleep(100); System.out.println(Thread.currentThread().getName()+" : isSyncB"); } }catch (InterruptedException ie) { } } public static synchronized void cSyncA(){ try { for (int i = 0; i < 5; i++) { Thread.sleep(100); System.out.println(Thread.currentThread().getName()+" : cSyncA"); } }catch (InterruptedException ie) { } } public static synchronized void cSyncB(){ try { for (int i = 0; i < 5; i++) { Thread.sleep(100); System.out.println(Thread.currentThread().getName()+" : cSyncB"); } }catch (InterruptedException ie) { } } }
public class LockTest3 {
Something x = new Something(); Something y = new Something();
private void test3() { Thread t31 = new Thread( new Runnable() { @Override public void run() { x.cSyncA(); } }, "t31");
Thread t32 = new Thread( new Runnable() { @Override public void run() { y.cSyncB(); } }, "t32");
t31.start(); t32.start(); }
public static void main(String[] args) { LockTest3 demo = new LockTest3();
demo.test3(); } }
|
运行结果:
1 2 3 4 5 6 7 8 9 10
| t31 : cSyncA t31 : cSyncA t31 : cSyncA t31 : cSyncA t31 : cSyncA t32 : cSyncB t32 : cSyncB t32 : cSyncB t32 : cSyncB t32 : cSyncB
|
(04) 可以被同时访问。因为isSyncA()是实例方法,x.isSyncA()使用的是对象x的锁;而cSyncA()是静态方法,Something.cSyncA()可以理解对使用的是类的锁。因此,它们是可以被同时访问的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| class Something { public synchronized void isSyncA(){ try { for (int i = 0; i < 5; i++) { Thread.sleep(100); System.out.println(Thread.currentThread().getName()+" : isSyncA"); } }catch (InterruptedException ie) { } } public synchronized void isSyncB(){ try { for (int i = 0; i < 5; i++) { Thread.sleep(100); System.out.println(Thread.currentThread().getName()+" : isSyncB"); } }catch (InterruptedException ie) { } } public static synchronized void cSyncA(){ try { for (int i = 0; i < 5; i++) { Thread.sleep(100); System.out.println(Thread.currentThread().getName()+" : cSyncA"); } }catch (InterruptedException ie) { } } public static synchronized void cSyncB(){ try { for (int i = 0; i < 5; i++) { Thread.sleep(100); System.out.println(Thread.currentThread().getName()+" : cSyncB"); } }catch (InterruptedException ie) { } } }
public class LockTest4 {
Something x = new Something(); Something y = new Something();
private void test4() { Thread t41 = new Thread( new Runnable() { @Override public void run() { x.isSyncA(); } }, "t41");
Thread t42 = new Thread( new Runnable() { @Override public void run() { Something.cSyncA(); } }, "t42");
t41.start(); t42.start(); }
public static void main(String[] args) { LockTest4 demo = new LockTest4();
demo.test4(); } }**运行结果**:
|
总结
其实你可以将类锁和对象锁相同看待,我这里的相同看待是指,将他们看成俩个不同的对象的形式,其实每个class都有一个监视对象。用来管理类的创建等任务。Synchronized关键字也是锁住了这个监视器对象,所以可以类比着看。