配色: 字号:
java基本教程之synchronized关键字 java多线程教程
2016-12-22 | 阅:  转:  |  分享 
  
java基本教程之synchronized关键字java多线程教程

这篇文章主要介绍了java的synchronized原理、synchronized基本规则、synchronized方法和synchronized代码块、实例锁和全局锁



本章,会对synchronized关键字进行介绍。涉及到的内容包括:

1.synchronized原理

2.synchronized基本规则

3.synchronized方法和synchronized代码块

4.实例锁和全局锁



1.synchronized原理



在java中,每一个对象有且仅有一个同步锁。这也意味着,同步锁是依赖于对象而存在。

当我们调用某对象的synchronized方法时,就获取了该对象的同步锁。例如,synchronized(obj)就获取了“obj这个对象”的同步锁。

不同线程对同步锁的访问是互斥的。也就是说,某时间点,对象的同步锁只能被一个线程获取到!通过同步锁,我们就能在多线程中,实现对“对象/方法”的互斥访问。例如,现在有两个线程A和线程B,它们都会访问“对象obj的同步锁”。假设,在某一时刻,线程A获取到“obj的同步锁”并在执行一些操作;而此时,线程B也企图获取“obj的同步锁”——线程B会获取失败,它必须等待,直到线程A释放了“该对象的同步锁”之后线程B才能获取到“obj的同步锁”从而才可以运行。





2.synchronized基本规则



我们将synchronized的基本规则总结为下面3条,并通过实例对它们进行说明。

第一条:当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞。

第二条:当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块。

第三条:当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的其他的“synchronized方法”或者“synchronized代码块”的访问将被阻塞。



第一条



当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞。

下面是“synchronized代码块”对应的演示程序。





复制代码代码如下:





classMyRunableimplementsRunnable{



@Override

publicvoidrun(){

synchronized(this){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);//休眠100ms

System.out.println(Thread.currentThread().getName()+"loop"+i);

}

}catch(InterruptedExceptionie){

}

}

}

}



publicclassDemo1_1{



publicstaticvoidmain(String[]args){

Runnabledemo=newMyRunable();//新建“Runnable对象”



Threadt1=newThread(demo,"t1");//新建“线程t1”,t1是基于demo这个Runnable对象

Threadt2=newThread(demo,"t2");//新建“线程t2”,t2是基于demo这个Runnable对象

t1.start();//启动“线程t1”

t2.start();//启动“线程t2”

}

}







运行结果:





复制代码代码如下:





t1loop0

t1loop1

t1loop2

t1loop3

t1loop4

t2loop0

t2loop1

t2loop2

t2loop3

t2loop4







结果说明:

run()方法中存在“synchronized(this)代码块”,而且t1和t2都是基于"demo这个Runnable对象"创建的线程。这就意味着,我们可以将synchronized(this)中的this看作是“demo这个Runnable对象”;因此,线程t1和t2共享“demo对象的同步锁”。所以,当一个线程运行的时候,另外一个线程必须等待“运行线程”释放“demo的同步锁”之后才能运行。



如果你确认,你搞清楚这个问题了。那我们将上面的代码进行修改,然后再运行看看结果怎么样,看看你是否会迷糊。修改后的源码如下:





复制代码代码如下:





classMyThreadextendsThread{



publicMyThread(Stringname){

super(name);

}



@Override

publicvoidrun(){

synchronized(this){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);//休眠100ms

System.out.println(Thread.currentThread().getName()+"loop"+i);

}

}catch(InterruptedExceptionie){

}

}

}

}



publicclassDemo1_2{



publicstaticvoidmain(String[]args){

Threadt1=newMyThread("t1");//新建“线程t1”

Threadt2=newMyThread("t2");//新建“线程t2”

t1.start();//启动“线程t1”

t2.start();//启动“线程t2”

}

}







代码说明:

比较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一样呢?

运行结果:





复制代码代码如下:





t1loop0

t2loop0

t1loop1

t2loop1

t1loop2

t2loop2

t1loop3

t2loop3

t1loop4

t2loop4







结果说明:

如果这个结果一点也不令你感到惊讶,那么我相信你对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代码块”对应的演示程序。





复制代码代码如下:





classCount{



//含有synchronized同步块的方法

publicvoidsynMethod(){

synchronized(this){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);//休眠100ms

System.out.println(Thread.currentThread().getName()+"synMethodloop"+i);

}

}catch(InterruptedExceptionie){

}

}

}



//非同步的方法

publicvoidnonSynMethod(){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);

System.out.println(Thread.currentThread().getName()+"nonSynMethodloop"+i);

}

}catch(InterruptedExceptionie){

}

}

}



publicclassDemo2{



publicstaticvoidmain(String[]args){

finalCountcount=newCount();

//新建t1,t1会调用“count对象”的synMethod()方法

Threadt1=newThread(

newRunnable(){

@Override

publicvoidrun(){

count.synMethod();

}

},"t1");



//新建t2,t2会调用“count对象”的nonSynMethod()方法

Threadt2=newThread(

newRunnable(){

@Override

publicvoidrun(){

count.nonSynMethod();

}

},"t2");





t1.start();//启动t1

t2.start();//启动t2

}

}







运行结果:





复制代码代码如下:





t1synMethodloop0

t2nonSynMethodloop0

t1synMethodloop1

t2nonSynMethodloop1

t1synMethodloop2

t2nonSynMethodloop2

t1synMethodloop3

t2nonSynMethodloop3

t1synMethodloop4

t2nonSynMethodloop4







结果说明:

主线程中新建了两个子线程t1和t2。t1会调用count对象的synMethod()方法,该方法内含有同步块;而t2则会调用count对象的nonSynMethod()方法,该方法不是同步方法。t1运行时,虽然调用synchronized(this)获取“count的同步锁”;但是并没有造成t2的阻塞,因为t2没有用到“count”同步锁。



第三条



当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的其他的“synchronized方法”或者“synchronized代码块”的访问将被阻塞。

我们将上面的例子中的nonSynMethod()方法体的也用synchronized(this)修饰。修改后的源码如下:





复制代码代码如下:





classCount{



//含有synchronized同步块的方法

publicvoidsynMethod(){

synchronized(this){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);//休眠100ms

System.out.println(Thread.currentThread().getName()+"synMethodloop"+i);

}

}catch(InterruptedExceptionie){

}

}

}



//也包含synchronized同步块的方法

publicvoidnonSynMethod(){

synchronized(this){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);

System.out.println(Thread.currentThread().getName()+"nonSynMethodloop"+i);

}

}catch(InterruptedExceptionie){

}

}

}

}



publicclassDemo3{



publicstaticvoidmain(String[]args){

finalCountcount=newCount();

//新建t1,t1会调用“count对象”的synMethod()方法

Threadt1=newThread(

newRunnable(){

@Override

publicvoidrun(){

count.synMethod();

}

},"t1");



//新建t2,t2会调用“count对象”的nonSynMethod()方法

Threadt2=newThread(

newRunnable(){

@Override

publicvoidrun(){

count.nonSynMethod();

}

},"t2");





t1.start();//启动t1

t2.start();//启动t2

}

}







(某一次)执行结果:





复制代码代码如下:





synMethod():11

synBlock():3







4.实例锁和全局锁

实例锁--锁在某一个实例对象上。如果该类是单例,那么该锁也具有全局锁的概念。

实例锁对应的就是synchronized关键字。

全局锁--该锁针对的是类,无论实例多少个对象,那么线程都共享该锁。

全局锁对应的就是staticsynchronized(或者是锁在该类的class或者classloader对象上)。



关于“实例锁”和“全局锁”有一个很形象的例子:





复制代码代码如下:





pulbicclassSomething{

publicsynchronizedvoidisSyncA(){}

publicsynchronizedvoidisSyncB(){}

publicstaticsynchronizedvoidcSyncA(){}

publicstaticsynchronizedvoidcSyncB(){}

}







假设,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)的同步锁!





复制代码代码如下:





//LockTest2.java的源码

classSomething{

publicsynchronizedvoidisSyncA(){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);//休眠100ms

System.out.println(Thread.currentThread().getName()+":isSyncA");

}

}catch(InterruptedExceptionie){

}

}

publicsynchronizedvoidisSyncB(){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);//休眠100ms

System.out.println(Thread.currentThread().getName()+":isSyncB");

}

}catch(InterruptedExceptionie){

}

}

publicstaticsynchronizedvoidcSyncA(){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);//休眠100ms

System.out.println(Thread.currentThread().getName()+":cSyncA");

}

}catch(InterruptedExceptionie){

}

}

publicstaticsynchronizedvoidcSyncB(){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);//休眠100ms

System.out.println(Thread.currentThread().getName()+":cSyncB");

}

}catch(InterruptedExceptionie){

}

}

}



publicclassLockTest2{



Somethingx=newSomething();

Somethingy=newSomething();



//比较(02)x.isSyncA()与y.isSyncA()

privatevoidtest2(){

//新建t21,t21会调用x.isSyncA()

Threadt21=newThread(

newRunnable(){

@Override

publicvoidrun(){

x.isSyncA();

}

},"t21");



//新建t22,t22会调用x.isSyncB()

Threadt22=newThread(

newRunnable(){

@Override

publicvoidrun(){

y.isSyncA();

}

},"t22");





t21.start();//启动t21

t22.start();//启动t22

}



publicstaticwww.visa158.commain(String[]args){

LockTest2demo=newLockTest2();



demo.test2();

}

}







运行结果:





复制代码代码如下:





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的同步锁。





复制代码代码如下:





//LockTest2.java的源码

classSomething{

publicsynchronizedvoidisSyncA(){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);//休眠100ms

System.out.println(Thread.currentThread().getName()+":isSyncA");

}

}catch(InterruptedExceptionie){

}

}

publicsynchronizedvoidisSyncB(){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);//休眠100ms

System.out.println(Thread.currentThread().getName()+":isSyncB");

}

}catch(InterruptedExceptionie){

}

}

publicstaticsynchronizedvoidcSyncA(){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);//休眠100ms

System.out.println(Thread.currentThread().getName()+":cSyncA");

}

}catch(InterruptedExceptionie){

}

}

publicstaticsynchronizedvoidcSyncB(){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);//休眠100ms

System.out.www.hunanwang.netprintln(Thread.currentThread().getName()+":cSyncB");

}

}catch(InterruptedExceptionie){

}

}

}



publicclassLockTest2{



Somethingx=newSomething();

Somethingy=newSomething();



//比较(02)x.isSyncA()与y.isSyncA()

privatevoidtest2(){

//新建t21,t21会调用x.isSyncA()

Threadt21=newThread(

newRunnable(){

@Override

publicvoidrun(){

x.isSyncA();

}

},"t21");



//新建t22,t22会调用x.isSyncB()

Threadt22=newThread(

newRunnable(){

@Override

publicvoidrun(){

y.isSyncA();

}

},"t22");





t21.start();//启动t21

t22.start();//启动t22

}



publicstaticvoidmain(String[]args){

LockTest2demo=newLockTest2();



demo.test2();

}

}







运行结果:





复制代码代码如下:





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(),因此它们共用一个同步锁,不能被同时反问。





复制代码代码如下:





//LockTest3.java的源码

classSomething{

publicsynchronizedvoidisSyncA(){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);//休眠100ms

System.out.println(Thread.currentThread().getName()+":isSyncA");

}

}catch(InterruptedExceptionie){

}

}

publicsynchronizedvoidisSyncB(){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);//休眠100ms

System.out.println(Thread.currentThread().getName()+":isSyncB");

}

}catch(InterruptedExceptionie){

}

}

publicstaticsynchronizedvoidcSyncA(){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);//休眠100ms

System.out.println(Thread.currentThread().getName()+":cSyncA");

}

}catch(InterruptedExceptionie){

}

}

publicstaticsynchronizedvoidcSyncB(){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);//休眠100ms

System.out.println(Thread.currentThread().getName()+":cSyncB");

}

}catch(InterruptedExceptionie){

}

}

}



publicclassLockTest3{



Somethingx=newSomething();

Somethingy=newSomething();



//比较(03)x.cSyncA()与y.cSyncB()

privatevoidtest3(){

//新建t31,t31会调用x.isSyncA()

Threadt31=newThread(

newRunnable(){

@Override

publicvoidrun(){

x.cSyncA();

}

},"t31");



//新建t32,t32会调用x.isSyncB()

Threadt32=newThread(

newRunnable(){

@Override

publicvoidrun(){

y.cSyncB();

}

},"t32");





t31.start();//启动t31

t32.start();//启动t32

}



publicstaticvoidmain(String[]args){

LockTest3demo=newLockTest3();



demo.test3();

}

}







运行结果:





复制代码代码如下:





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()可以理解对使用的是“类的锁”。因此,它们是可以被同时访问的。





复制代码代码如下:





//LockTest4.java的源码

classSomething{

publicsynchronizedvoidisSyncA(){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);//休眠100ms

System.out.println(Thread.currentThread().getName()+":isSyncA");

}

}catch(InterruptedExceptionie){

}

}

publicsynchronizedvoidisSyncB(){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);//休眠100ms

System.out.println(Thread.currentThread().getName()+":isSyncB");

}

}catch(InterruptedExceptionie){

}

}

publicstaticsynchronizedvoidcSyncA(){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);//休眠100ms

System.out.println(Thread.currentThread().getName()+":cSyncA");

}

}catch(InterruptedExceptionie){

}

}

publicstaticsynchronizedvoidcSyncB(){

try{

for(inti=0;i<5;i++){

Thread.sleep(100);//休眠100ms

System.out.println(Thread.currentThread().getName()+":cSyncB");

}

}catch(InterruptedExceptionie){

}

}

}



publicclassLockTest4{



Somethingx=newSomething();

Somethingy=newSomething();



//比较(04)x.isSyncA()与Something.cSyncA()

privatevoidtest4(){

//新建t41,t41会调用x.isSyncA()

Threadt41=newThread(

newRunnable(){

@Override

publicvoidrun(){

x.isSyncA();

}

},"t41");



//新建t42,t42会调用x.isSyncB()

Threadt42=newThread(

newRunnable(){

@Override

publicvoidrun(){

Something.cSyncA();

}

},"t42");





t41.start();//启动t41

t42.start();//启动t42

}



publicstaticvoidmain(String[]args){

LockTest4demo=newLockTest4();



demo.test4();

}

}





运行结果:





复制代码代码如下:





t41:isSyncA

t42:cSyncA

t41:isSyncA

t42:cSyncA

t41:isSyncA

t42:cSyncA

t41:isSyncA

t42:cSyncA

t41:isSyncA

t42:cSyncA





















献花(0)
+1
(本文系白狐一梦首藏)