• JAVA学习之多线程和synchronized关键字


     

          在使用多线程的时候,如果多条线程运行的代码涉及到一些共享的数据的时候,如对类的私有变量的操作,往往会出现一些数据不符合原设计要求的结果,这就是线程安全性的问题,
    是由于线程的状态不稳定,取得CPU控制权的不可测的原因造成的。比如在线程进入一些判断语句块的时候,某一线程A在对某个数据判断过后,进入了判断语句块内时,
    还没等对数据进行操作的时候失去了对CPU的控制权,这时另一线程B取得了CPU的控制权,并且也要判断语句块内执行里面的代码,
    由于线程A在还没有对数据进行操作的时候就失去了CPU控制权,所以数据还没有改变,线程B也顺利的进入了这判断代码块内,然后顺利执行相应对数据的操作,
    假设数据这时已经处于了边缘状态,这时线程A终于取得了CPU的控制权,然后又对数据进行了操作,这时数据就出现了问题,因为数据已经不符合要求了。
    在JAVA中,synchronized关键字就是用来处理由于多线程而引起的数据安全性的问题的。
    synchronized的使用方法有两种,一种是修饰方法,一种是修饰指定的代码块,其中被修饰的方法叫同步方法,被修饰的代码叫同步代码块。 
    同步方法的语法是在方法的权限修饰符与方法返回类型之前添加上synchronized关键字来修饰这个方法,如下:
    //

    public synchronized void  method(){}

     同步代码块的使用方法是,在要被同步修饰的代码添加如while一样的代码花括号,如下:

    synchronized(object)

    //要同步的代码

     

    其中object是一个对象
    其实同步方法的synchronized锁的也是一个对象,只不过不不像同步代码块一样显式表现而已。
    对于实例的同步方法,synchronized使用的对象是这个同步方法所在的对象本身,即this
    对于静态的同步方法,synchronized使用的对象是这个同步方法所有的类的对象,即类.class
     
    在进一步阐述之前,我们需要明确几点: 
    A.无论synchronized关键字加在方法上还是对象上,他取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。 
    B.每个对象只有一个锁(lock)和之相关联。 
    C.实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。 
    接着来讨论synchronized用到不同地方对代码产生的影响:
    假设P1、P2是同一个类的不同对象,这个类中定义了以下几种情况的同步块或同步方法,P1、P2就都能够调用他们。
    把synchronized当作函数修饰符时,示例代码如下:
    //
    public synchronized void method(){} 
    这时起到的作用与这下面的代码是一样的。
    public void method() 

        synchronized (this)
        { 
        //
        }

    ----------------或者以下代码:
    public class O
    {
        public synchronized void A(){}
        public synchronized void B(){}
    }

    // 

     

     
    这也就是说同步方法synchronized锁定的对象是调用这个同步方法的对象。也就是说,当一个对象object在不同的线程中执行这个实例同步方法时,各个线程之间会形成互斥,达到同步的效果。在类O中,有两个同步方法,而且都是实例方法,由于这两个方法用来锁的对象都是同一个对象,所以当某一线程在访问方法A()的时候,别的线程都不能调用方法B(),而是在等待前一个线程执行完毕。这也再说说明了,synchronized同步是锁的是对象,而不是代码块或者方法当synchronized修饰静态方法时synchronized锁的对象是这个类的class对象。
    //
    Class MyClass

        public synchronized static void method1() // 同步的static 函数 
        { 
            //
        } 
        public void method2() 
        { 
            synchronized(MyClass.class){//}
        } 
    }

    // 

    在以上的代码里,method2()里面的同步代码块的对象锁是MyClass.class,而方法method1()是一个静态同步方法,它的对象锁也是这个类的class对象MyClass.class,所以
    当一个线程执行方法method1(),另外的线程是不能执行方法method2()里面的同步代码的,因为他们锁的是同一对象。
     
    假如一个类中定义了一个synchronized的static函数A,也定义了一个synchronized 的实例函数B,那么这个类的同一对象Obj在多线程中分别访问A和B两个方法时,不会构成同步,因为他们的锁都不相同。A方法的锁是Obj所属的那个Class,而B的锁是Obj所属的这个对象。
    死锁,如下的代码就是死锁的代码,理解了死锁,那么线程同步时synchronized锁的对象就好理解了:

     //

    //
    class OutClass
    {
        private byte[] lock = new byte[0];
        public synchronized void show()
        {
            synchronized (lock)
            {
                //
            }
        }
        public void show2()
        {
            synchronized (lock)
            {
                show();
                //
            }
        }
    }
    //
     
    当一个线程A进入方法show()的时候,另一个线程B正在执行方法show2(),并已经进入了同步代码块中,这时线程A执行到了同步代码块外面,但是却不能进入,因为此时lock对象已经在线程B进入show2()的同步代码块时锁上了,所以线程A执行不下去了,然而线程B现在也不能顺利执行方法show();因为此时线程A正在show()方法内部,并且用得都是这个方法的调用对象的锁,所以就出现两个线程同时等待对方执行完毕的情况,这就是死锁。

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

     
  • 相关阅读:
    win8.1下安装双系统ubuntu14.04.3
    如何使用cmd
    My Test about Mat
    访问Mat矩阵中的元素并为其赋值
    Mat代码操作
    waitKey()
    ASCII码对照表
    vector 中的clear()
    vector 介绍
    Mat的详解
  • 原文地址:https://www.cnblogs.com/fylx/p/3960684.html
Copyright © 2020-2023  润新知