java 线程(三) 互斥和同步

来源:互联网 发布:手机全透明软件 编辑:程序博客网 时间:2024/05/29 17:38

线程的同步

    多线程的安全问题来自于当不同线程切换时,对同一数据的操作引起的混乱。例如两个线程并发同时对同一个文件修改时就有可能造成异常。对于线程的同步,Java提供了如下几种方式解决:

    a. 同步代码块

    Java的多线程支持引入同步监视器来解决这个问题。

 

  1. synchronized(obj){  //括号中的obj就是同步监视器
  2.  
  3.      ...  
  4.      //此处的代码就是同步代码块  
  5. //上述代码的含义就是,线程开始执行同步代码块之前,必须先获得对同步监视器的锁定。通常推荐使用可能被并发访问的共享资源充当同步监视器。

 

    这样一来,任何想要修改制定资源的资源执行时,首先说该资源加锁,在加锁期间其它进程无法修改该资源,而在其修改完成之后便释放对该资源的锁定。从而使得同一时刻只有一条线程处于临界区内,保证了安全。

    需要注意的是,同步监视器一般可以由任何对象充当,只要其唯一恒定就可以,通常推荐使用可能被并发访问的共享资源充当同步监视器

    b. 同步方法

    在Java中的多线程中,使用synchronized关键字修饰的方法成为同步方法。对于同步方法,就无需指定监视器,该对象本身就是监视器。在同步方法执行的时候,就会首先锁定同步监视器this,synchronized保证了只有一条线程对资源的访问。所以,应该把同步方法定义在需要被独占访问的对象类内部。

  c. 同步锁

    同步锁是线程同步的另一种机制。使用被声明为final的同步锁作为同步监视器,在方法体执行前首先显式加锁,执行完后在显式的解锁,由于使用Lock对象时每个Lock对象都对应一个被访问的对象,其实也就等同于同步方法的实现机制,保证了线程的同步。

 

 
 

  1. class X{90 
  2. //定义锁对象  
  3. private final ReentrantLock lock=new ReentrantLock();  
  4. //...  
  5. //定义需要保证线程安全的方法  
  6. public void m(){  
  7. //加锁  
  8. lock.lock();  
  9. try{  
  10. //方法体  
  11. //.....  
  12. }finally{  
  13. //解锁  
  14. lock.unlock();  
  15.       }  
  16.     


    以上的各种线程同步机制,其实就是以一个始终恒定不变的对象作为同步监视器。在同步块代码(synchronized(obj))中,虽然方法定义在线程类的内部,但是作为同步监视器的被访问对象是在初始化之后从外部传递到其内部的,具有不变性。而在同步方法(public synchronized void xx))机制中,同步监视器就是this对象,当对象初始化时,已经恒定了对象,所以其同步监视器也是不变的。Lock机制和同步方法的机制类似,只不过其监视器是lock,因为当初始化独占访问对象时也会初始化lock,所以也是恒定的。

线程通信

  a. 控制线程协调运行的机制(1)

    Java中有内置的三种方法对线程进行协调控制,他们属于Object类,但调用的时候要使用同步监视器来调用需要和synchronized 结合使用

    wait()方法使当前线程等待,释放对该同步器的锁定。

    notify()方法唤醒该同步器上等待的单个线程,对于唤醒的选择是任意的。

    notifyAll()唤醒全部等待的线程。

  b. 控制线程协调运行的机制(2)

    在使用了Lock对象保证同步的的程序中,同步监视器并非this对象本身,就要使用Condition对象的的方法保证同步。   

 
 

  1. //显式的获得Lock对象  
  2. private final Lock lock=new() ReentrantLock();  
  3. //获得指定Lock对象的条件变量  
  4. private final Condition cond=lock.newCondition();  
  5. //.....  
  6. cond.await();  // 是await 而不是 wait
  7. //.....  
  8. cond.signal();  // 是signal 而不是 notify
  9. //.....  
  10. cond.signalAll();  

    这三个方法的功能同上面的三个方法功能。

读写锁

java.util.concurrent.lock包定义了两个锁类,即ReetrantLock(普通锁) 和 ReetrantReadWriteLock(读写锁).如果需要读线程不用互斥(因为度线程不会改变数据),而写线程需要互斥,则用 ReetrantReadWriteLock很方便。 使用步骤:

1)创建一个 ReetrantReadWriteLock对象

ReetrantReadWriteLock rwl = new ReetrantReadWriteLock();

2)抽取读锁和写锁

private Lock readlock = rwl.readLock();

private Lock writeLock = rwl.writeLock();

3)对所有访问者加读锁

...

readLock.lock();

try{

..

}

finally{

readLock.unlock();

}

4)对所有修改者加写锁

writeLock.lock();

try{

...

}finally{

writeLock.unlock();

}



0 0
原创粉丝点击