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锁定的是哪个对象,就能帮助我们设计更安全的多线程程式。

 

 

 

 

 

 
posted @ 2014-09-07 19:31  枫叶孤星  阅读(201)  评论(0编辑  收藏  举报