Synchronized(对象锁)和Static Synchronized(类锁)的区别

热点专题 阅读(1025)

同步和静态同步的区别通过对这两种用法的分析,我们可以理解java中锁的概念。一种是实例锁(锁定在实例对象上;如果类是单个实例,那么锁也有全局锁的概念),另一个是全局锁(锁是针对一个类的,不管实例中有多少对象,线程都共享锁)。实例锁对应于synchronized关键字,而类锁(全局锁)对应于静态synchronized(或类的类或类加载器对象上的锁)。下面的文章给出了一个很好的结论:

1 . synchronized的区别是锁定类的当前实例(当前对象),以防止其他线程同时访问类实例的所有同步块。请注意,这是“类的当前实例”,对于类的两个不同实例没有这样的限制。

那么静态同步就是控制类的所有实例的并发访问。静态同步是为了限制多线程中类的所有实例同时访问与jvm中的类相对应的代码块。事实上,如果一个类的方法或代码块中存在同步,则在生成该类的实例后,该实例还具有一个监视块,以防止线程同时访问该实例的同步保护块,而静态同步是该类的所有实例共有的监视块,这是它们之间的区别。换句话说,同步相当于同步,而静态同步相当于某种东西。

pulbic?班级?公开的东西?同步?无效?isSyncA(){}

public?同步?无效?isSyncB(){}

public?静电?同步?无效?cSyncA(){}

public?静电?同步?无效?cSyncB(){}

所以,如果有两个实例X和Y,当多线程同时访问以下方法组时会发生什么?

所以,如果有两个实例X和Y,当多线程同时访问以下方法组时会发生什么?

b?X.isSyncA()和y.isSyncA()

public?同步?无效?isSyncB(){}

d?X.isSyncA()和某物. cSyncA()

?这里,很明显要判断:

如果在多个线程中访问x.isSyncA(),则不能在多个线程中同时访问它,因为它仍然是同一个实例,并且同一个方法被锁定。(在多线程中访问X的同一个同步域不能同时被访问)

b是针对不同的实例,所以它可以同时被访问(对象锁对不同的对象实例没有锁约束)

那么,D呢?书中的?鸢缚梢酝北换袢 4鸢傅脑蚴莝ynchronize id的实例方法不同于synchronize id的类方法,原因是锁。

个人分析意味着同步和静态同步相当于两个组,每个组运行自己的事务,因此它们之间没有限制,可以同时访问。

例如:

Java代码

public?班级?同步测试?

int?我?=?5 .

public?同步?无效?test1()?

int?我?=?5 .

int?我?=?5 .

while(?我?0)?

int?我?=?5 .

?System.out.println(线程.当前线程)。getName()?“:”?㈠;

?尝试?

int?我?=?5 .

线程睡眠(500);

?}?

?接住。(中断例外?ie)?

?{

?{

?接住。(中断例外?ie)?

public?静电?同步?无效?test2()?

所以,如果有两个实例X和Y,当多线程同时访问以下方法组时会发生什么?

所以,如果有两个实例X和Y,当多线程同时访问以下方法组时会发生什么?

?而(?我?0)?

int?我?=?5 .

System.out.println(线程当前线程()。getName()?“:”?㈠;

尝试?

?接住。(中断例外?ie)?

?线程睡眠(500);

}?

int?我?=?5 .

public?静电?无效?主要(字符串[)?args)?

public?静电?无效?主要(字符串[)?args)?

?}

int?我?=?5 .

所以,如果有两个实例X和Y,当多线程同时访问以下方法组时会发生什么?

public?静电?同步?无效?test2()?

所以,如果有两个实例X和Y,当多线程同时访问以下方法组时会发生什么?

?线?测试1?=?新的?线程(新的?Runnable()?{公共?无效?run()?{ myt 2 . test1();}},test 1’);

int?我?=?5 .

?test 1 . start();

?test 2 . start();

//?TestRunnable?tr=新的?testRunnable();

//?线?test3=新?螺纹(tr).

//?测试3。start();

}?

}

爪哇代码

test1?4

test2?4

test1?3

test2?3

test2?2

test1?2

test2?1

test1?1

test1?0

test2?0

上面代码同步的同时修饰静态方法和实例方法,但是运行结果是交替进行的,这证明了类锁和对象锁是两个不一样的锁,控制着不同的区域,它们是互不干扰的。同样,线程获得对象锁的同时,也可以获得该类锁,即同时获得两个锁,这是允许的

结论:A:同步静态是某个类的范围,同步静态cSync{}防止多个线程中多个实例同时访问这个 类中的同步静态方法。它可以对类的所有对象实例起作用

B:已同步是某实例的范围,同步isSync(){}防止多个线程中这一个实例同时访问这个类的同步的方法

其实总结起来很简单

一个锁的是类对象,一个锁的是实例对象

若类对象被锁,则类对象的所有同步方法全被锁定;

若实例对象被锁,则该实例对象的所有同步方法全被锁。

2。已同步方法与同步的代码快的区别

?同步方法(){}与已同步(此){}之间没有什么区别,只是同步方法(){}便于阅读理解,而已同步(此){}可以更精确的控制冲突限制访问区域,有时候表现更高效率

两种方式效率比较:

1,同步块,代码如下:

[爪哇?查看plaincopy

package?请访问。BJT。贝伦;

import?Java。util。并发。倒计时;

import?Java。util。并发。执行服务;

import?Java。util。并发。执行者;

public?班级?同步测试?{

public?静电?无效?主要(字符串[)?args)?{

ExecutorService?服务?=?执行者。NewcachedThreadpool();

final?CountDownLatch?cdOrder?=?新的?计数下行通道(1);

final?CountDownLatch?回答?=?新的?计数下行通道(3);

final?同步类?sc?=?新的?synchonizedClass();

for(int?I=0;i3;i ){

Runnable?跑步吗?=?新的?Runnable(){

public?无效?run()?{

请尝试{

CdOrder。wait();

sc。start();

CDanswer。Documentum();

}捕捉(异常?e){

e . PrintStackTrace();

}

}

};

服务。执行(可运行);

}

请尝试{

线程睡眠((长时间)?(数学随机()* ));

System.out.println('线程?Thread.currentThread().getName()?"

"发布执行命令);

CdOrder。DownLoad();

长?开始时间?=?系统。current itemmellis();

System.out.println('线程?Thread.currentThread().getName()?"

"已经发送命令,正在等待结果);

CDanswer。wait();

System.out.println('线程?Thread.currentThread().getName()?"

"已收到所有响应结果,所用时间为:'?(系统。currentTimeMillis()-BeginTime));

}捕捉(异常?e){

e . PrintStackTrace();

}

服务。关闭();

}

}

类?同步类{

public?无效?start()?投掷?中断例外{

thread。睡眠(100);//执行其它逻辑消耗时间

已同步(此){

?System.out.println('我运行使用了?十岁ms’);

}

}

}

运行结果如下:

线程主要的发布执行命令

线程主要的已经发送命令,正在等待结果

我运行使用了10毫秒

我运行使用了10毫秒

我运行使用了10毫秒

线程主要的已收到所有响应结果,所用时间为:110

同步方法,代码如下:

[爪哇?查看plaincopy

package?请访问。BJT。贝伦;

import?Java。util。并发。倒计时;

import?Java。util。并发。执行服务;

import?Java。util。并发。执行者;

public?班级?同步测试?{

public?静电?无效?主要(字符串[)?args)?{

ExecutorService?服务?=?执行者。NewcachedThreadpool();

final?CountDownLatch?cdOrder?=?新的?计数下行通道(1);

final?CountDownLatch?回答?=?新的?计数下行通道(3);

final?同步类?sc?=?新的?synchonizedClass();

用于(int?I=0;i3;i ){

Runnable?跑步吗?=?新的?Runnable(){

public?无效?run()?{

请尝试{

CdOrder . wait();

sc . start();

CDanswer . Documentum();

}捕捉(异常?e){

e . PrintStackTrace();

public?同步?无效?start()?投掷?中断例外{

public?同步?无效?start()?投掷?中断例外{

};

service . execute(runnable);

public?同步?无效?start()?投掷?中断例外{

请尝试{

Thread.sleep((长时间)?(数学随机()* ));Out.println(“线程”?Thread.currentThread()。getName()?

“发出执行命令”);

CdOrder . DownLoad();

长?开始时间?=?system . CurrentItemMellis();Out.println(“线程”?Thread.currentThread()。getName()?

'命令已发送,等待结果');

“发出执行命令”);

'已收到所有响应结果,正在接受:'?(System . CurrentiMemillis()-BeginTime));

}捕捉(异常?e){

“发出执行命令”);

public?同步?无效?start()?投掷?中断例外{

}捕捉(异常?e){

e . PrintStackTrace();

public?同步?无效?start()?投掷?中断例外{

类?同步类{

public?同步?无效?start()?投掷?中断例外{

public?同步?无效?start()?投掷?中断例外{

//同步(这){

?我运行了吗?十岁。ms’);

/}

thread main发出了一个执行命令

操作的结果如下:

public?同步?无效?start()?投掷?中断例外{

public?同步?无效?start()?投掷?中断例外{

10 ms用于我的运行

10 ms用于我的运行

thread main已经收到了332

中的所有响应结果,差异为222 ms,

?比较表明,同步码块比同步方法更有效。

?比较表明,同步码块比同步方法更有效。

?比较表明,同步码块比同步方法更有效。

1。同步关键字有两个作用域:

1)是对象实例中的同步方法,它阻止多个线程同时访问对象(如果一个对象有多个同步方法,只要一个线程访问一个同步方法,其他线程就不能同时访问对象中的任何同步方法)。此时,不同对象实例的同步方法没有干扰。换句话说,其他线程仍然可以同时访问同一类的另一个对象实例中的同步方法。

10 ms用于我的运行

2。除了在方法之前使用synchronized关键字之外,synchronized关键字还可以在方法中的某个块中使用,这意味着只对该块的资源实现独占访问。用法是: synchronized(this){}(或synchronized(obj){}),其作用域是当前对象;

10 ms用于我的运行

对synchronized(this)的一些理解(很好地解释了对象锁,注意这个关键字)

10 ms用于我的运行

2。然而,当一个线程访问对象的同步(这个)代码块时,另一个线程仍然可以访问对象中的非同步(这个)代码块。

3。尤其重要的是,当一个线程访问对象的一个同步代码块时,其他线程对对象中所有其他同步代码块的访问将被阻止。

第四个和第三个例子也适用于其他同步码块。换句话说,当一个线程访问一个对象的同步(这个)代码块时,它获得了对象的对象锁。因此,其他线程对对象对象的所有同步代码部分的访问被暂时阻止。

5。上述规则也适用于其他对象锁

添加一段代码以方便测试synchronized关键字(简单修改)

public classtestsynchronized

inti=5;

public void testt 1()

10 ms用于我的运行

synchronized(this )

inti=5;

inti=5;

while(I-0)

inti=5;

System . out . println(Thread . CurrentThread()。getName()' : ' I);

inti=5;

inti=5;

试试

thread . sleep(500);

catch(Interrupted Exception ie)

catch(Interrupted Exception ie)

int I=5;

thread . sleep(500);

catch(Interrupted Exception ie)

catch(Interrupted Exception ie)

catch(Interrupted Exception ie)

catch(Interrupted Exception ie)

int I=5;

thread . sleep(500);

while(I-0)

System . out . println(Thread . CurrentThread()。getName()' : ' I);

thread . sleep(500);

试试

thread . sleep(500);

catch(Interrupted Exception ie)

catch(Interrupted Exception ie)

int I=5;

thread . sleep(500);

catch(Interrupted Exception ie)

catch(Interrupted Exception ie)

catch(Interrupted Exception ie)

int I=5;

thread . sleep(500);

while(I-0)

System . out . println(Thread . CurrentThread()。getName()' : ' I);

thread . sleep(500);

试试

thread . sleep(500);

catch(Interrupted Exception ie)

catch(Interrupted Exception ie)

int I=5;

thread . sleep(500);

catch(Interrupted Exception ie)

catch(Interrupted Exception ie)

catch(Interrupted Exception ie)

final TestSynchronized my T2=new TestSynchronized();

thread . sleep(500);

final TestSynchronized my T3=new TestSynchronized();

Thread test1=new Thread(new Runnable(){ public void run(){ myt 2 . test2();} },‘test1’);

Thread test2=new Thread(new Runnable(){ public void run(){ myt 2 . test3();} },‘test3’);

test 1 . start();

test 2 . start();

catch(Interrupted Exception ie)

catch(Interrupted Exception ie)

运行结果:

运行结果:

test 1 : 3

test 1 : 2

test 1 : 1

test 1 : 0

test 3 : 4

test 3 3: 3

test 3 3: 2

test 3 3: 1

test 3 3: 0 0

运行结果:

synchronized关键字,它包括两种用法:同步方法和同步块。

1。同步方法:通过向方法声明中添加synchronized关键字来声明同步方法。例如:

public synchronized voice access val(int new val);

synchronized方法控制对类成员变量的访问:每个类实例对应一个锁,每个同步的方法必须获得调用该方法的类实例的锁才能执行,否则它所属的线程将被阻塞。一旦方法被执行,锁是独占的,直到它从方法返回。此后,被阻塞的线程可以获得锁并重新进入可执行状态。该机制确保同时为每个类实例声明同步的所有成员函数中最多有一个处于可执行状态(因为最多只有一个可以获得对应于该类实例的锁),从而有效避免类成员变量的访问冲突(只要访问类成员变量的所有可能方法都声明同步)。

在Java中,不仅是类实例,每个类也对应一个锁,所以我们也可以将类的静态成员函数声明为静态的?同步以控制其对类的静态成员变量的访问。

synchronized方法的缺陷:如果一个大的方法被声明为同步,效率将会受到很大的影响。通常,如果线程类的方法run()被声明为同步,它将永远无法成功调用该类的任何同步方法,因为它在线程的整个生命周期中都在运行。当然,我们可以通过将访问类成员变量的代码放入一个特殊的方法中,声明它是同步的,并在主方法中调用它来解决这个问题,但是Java为我们提供了一个更好的解决方案,那就是同步块。

2。同步块:通过synchronized关键字声明同步块。语法如下:

catch(Interrupted Exception ie)

运行结果:

在使用synchronized关键字时,应该尽可能避免在synchronized方法或synchronized块中使用sleep或yield方法,因为synchronized块占用了对象锁,所以其他线程只能在您休息时执行,直到您醒来并完成执行。不仅严重影响效率,而且不合逻辑。

同样,调用yeild方法来释放同步块中的CPU资源是没有意义的,因为您占用了锁,其他互斥的线程仍然不能访问同步块。当然,与同步块无关的线程可以获得更多的执行时间。