java中synchronized使用方法

java中synchronized使用方法

大家好,又见面了,我是全栈君,祝每个程序员都可以多学几门语言。

synchronized的一个简单样例

public class TextThread
{

 /**
  * @param args
  */
 public static void main(String[] args)
 {
  // TODO 自己主动生成方法存根
        TxtThread tt = new TxtThread();
        new Thread(tt).start();
        new Thread(tt).start();
        new Thread(tt).start();
        new Thread(tt).start();
 }

}
class TxtThread implements Runnable
{
 int num = 100;
 String str = new String();
 public void run()
 {
  while (true)
  {
   synchronized(str)
   {
   if (num>0)
   {
    try
    {
     Thread.sleep(10);
    }
    catch(Exception e)
    {
     e.getMessage();
    }
    System.out.println(Thread.currentThread().getName()+ “this is “+ num–);
   }
   }
  }
 }
}

上面的样例中为了制造一个时间差,也就是出错的机会,使用了Thread.sleep(10)

Java对多线程的支持与同步机制深受大家的喜爱,似乎看起来使用了synchronizedkeyword就能够轻松地解决多线程共享数据同步问题。究竟怎样?――还得对synchronizedkeyword的作用进行深入了解才可定论。

总的说来,synchronizedkeyword能够作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。假设再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。

在进一步阐述之前,我们须要明白几点:

A.不管synchronizedkeyword加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――并且同步方法非常可能还会被其它线程的对象訪问。

B.每一个对象仅仅有一个锁(lock)与之相关联。

C.实现同步是要非常大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

接着来讨论synchronized用到不同地方对代码产生的影响:

 

如果P1P2是同一个类的不同对象,这个类中定义了下面几种情况的同步块或同步方法,P1P2就都能够调用它们。

 

1.  synchronized当作函数修饰符时,演示样例代码例如以下:

Public synchronized void methodAAA()

{

//….

}

这也就是同步方法,那这时synchronized锁定的是哪个对象呢?它锁定的是调用这个同步方法对象。也就是说,当一个对象P1在不同的线程中运行这个同步方法时,它们之间会形成相互排斥,达到同步的效果。可是这个对象所属的Class所产生的还有一对象P2却能够随意调用这个被加了synchronizedkeyword的方法。

上边的演示样例代码等同于例如以下代码:

public void methodAAA()

{

synchronized (this)      //  (1)

{

       //…..

}

}

 (1)处的this指的是什么呢?它指的就是调用这种方法的对象,如P1。可见同步方法实质是将synchronized作用于object reference。――那个拿到了P1对象锁的线程,才干够调用P1的同步方法,而对P2而言,P1这个锁与它毫不相干,程序也可能在这样的情形下摆脱同步机制的控制,造成数据混乱:(

2.同步块,演示样例代码例如以下:

            public void method3(SomeObject so)

              {

                     synchronized(so)

{

       //…..

}

}

这时,锁就是so这个对象,谁拿到这个锁谁就能够执行它所控制的那段代码。当有一个明白的对象作为锁时,就能够这样敲代码,但当没有明白的对象作为锁,仅仅是想让一段代码同步时,能够创建一个特殊的instance变量(它得是一个对象)来充当锁:

class Foo implements Runnable

{

       private byte[] lock = new byte[0];  // 特殊的instance变量

    Public void methodA()

{

       synchronized(lock) { //… }

}

//…..

}

注:零长度的byte数组对象创建起来将比不论什么对象都经济――查看编译后的字节码:生成零长度的byte[]对象仅仅需3条操作码,而Object lock = new Object()则须要7行操作码。

3.将synchronized作用于static 函数,演示样例代码例如以下:

      Class Foo

{

public synchronized static void methodAAA()   // 同步的static 函数

{

//….

}

public void methodBBB()

{

       synchronized(Foo.class)   //  class literal(类名称字面常量)

}

       }

   代码中的methodBBB()方法是把class literal作为锁的情况,它和同步的static函数产生的效果是一样的,取得的锁非常特别,是当前调用这种方法的对象所属的类(Class,而不再是由这个Class产生的某个详细对象了)。

记得在《Effective Java》一书中看到过将 Foo.class P1.getClass()用于作同步锁还不一样,不能用P1.getClass()来达到锁这个Class的目的。P1指的是由Foo类产生的对象。

能够判断:假设一个类中定义了一个synchronizedstatic函数A,也定义了一个synchronized instance函数B,那么这个类的同一对象Obj在多线程中分别訪问AB两个方法时,不会构成同步,由于它们的锁都不一样。A方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class

 

小结例如以下:

搞清楚synchronized锁定的是哪个对象,就能帮助我们设计更安全的多线程程序。

 

另一些技巧能够让我们对共享资源的同步訪问更加安全:

1.  定义private instance变量+它的 get方法,而不要定义public/protectedinstance变量。假设将变量定义为public,对象在外界能够绕过同步方法的控制而直接取得它,并修改它。这也是JavaBean的标准实现方式之中的一个。

2.  假设instance变量是一个对象,如数组或ArrayList什么的,那上述方法仍然不安全,由于当外界对象通过get方法拿到这个instance对象的引用后,又将其指向还有一个对象,那么这个private变量也就变了,岂不是非常危急。 这个时候就须要将get方法也加上synchronized同步,而且,仅仅返回这个private对象的clone()――这样,调用端得到的就是对象副本的引用了。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/118674.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • java tcp粘包_socket拆包与组班

    java tcp粘包_socket拆包与组班importjava.nio.ByteBuffer;importio.netty.bootstrap.ServerBootstrap;importio.netty.buffer.ByteBuf;importio.netty.buffer.Unpooled;importio.netty.channel.ChannelFuture;importio.netty.channel.Channe…

  • 关于String转jsonArray,jsonArray转json,json写入实体类

    关于String转jsonArray,jsonArray转json,json写入实体类工作要写个接收数据的接口,基于springMVC的,不了解,补充学习下json的用法1用的是这个jar包,虽然用的时候要导6个包,但用起来很方便。importnet.sf.json.JSONObject;2单个的jsonResult实体类对应json的字段Stringstr="{\"result\":\"success\",\"message\":\"成功!\"}…

  • 解决:java.lang.AbstractMethodError: null

    解决:java.lang.AbstractMethodError: nullspringboot2.x整合sqlserver使用jtds连接池连数据库的时候出现异常2017-02-1512:12:23.955WARN14844—[main]ationConfigEmbeddedWebApplicationContext:Exceptionencounteredduringcontextinitializ…

  • 用PMML实现机器学习模型的跨平台上线

    用PMML实现机器学习模型的跨平台上线在机器学习用于产品的时候,我们经常会遇到跨平台的问题。比如我们用Python基于一系列的机器学习库训练了一个模型,但是有时候其他的产品和项目想把这个模型集成进去,但是这些产品很多只支持某些特定的生产环

  • java websocket client_前端和后端哪个累

    java websocket client_前端和后端哪个累一.WebSocket简单介绍随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了。近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通

  • Java-重载与重写的区别[通俗易懂]

    Java-重载与重写的区别[通俗易懂]重写(Override)规则:当子继承父类的方法时,此方法不适用于子的程序执行,需要进行重写。参数列表必须完全与被重写方法相同返回类型必须与被重写方法的返回类型相同子类重写的方法访问权限不能小于被重写的方法。父类的成员方法只能被它的子类重写声明为static和private的方法不能被重写,但是能够被再次声明重写(Override)和重载(Overload)的区别参数列表重写:参数列表必须与被重写的方法相同。重载:参数列表必须不同发生的位置重写:发生在子类中重载:发生在一个类

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号