去故就新 Java线程新同步机制

开发 后端
新的Java线程同步机制,主要对可重入锁ReentrantLock,条件对象Condition以及读写锁ReentrantReadWriteLock机制解析。

1、可重入锁ReentrantLock,相当于synchronized块,为临界区提供互斥访问机制。

(1) 相关的接口

创建一个可重入锁

Lock lock = new ReentrantLock(); 
  • 1.

请求锁,如果锁被当前另一个线程持有,则阻塞。

void lock(); 
  • 1.

释放锁

void unlock(); 
  • 1.

非阻塞型lock()

boolean tryLock(); 
  • 1.


(2) 使用基本结构

locker.lock(); 
try
//code here to access the cirtical section 
}finally
locker.unlock(); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

这种结构保证在任何时刻只有一个线程能够进入临界区,如果一个线程锁住了锁对象,其他任何线程在调用lock时,都会被阻塞,直到第一个线程释放锁对象。而且无论try块是否抛出异常,都会执行finally block,解锁locker。

(3) 锁的可重入性

锁是可重入的,线程能够重复地获取它已经拥有的锁。锁对象维护一个持有计数(hold count)来追踪对lock方法的嵌套调用。线程在每次调用lock后都要调用unlock来释放锁。由于这个特性,被一个锁保护的代码可以调用另一个使用相同锁的方法。

(4) 示例代码:

import java.util.concurrent.locks.Lock; 
import java.util.concurrent.locks.ReentrantLock; 
 
class WorkerOne extends Thread{ 
    private Lock locker; 
    public WorkerOne (Lock locker){ 
        this.locker = locker; 
    } 
     
    public void run(){ 
        locker.lock(); 
        try
System.out.println(Thread.currentThread().getName()+":step into critical section"); 
        }finally
            locker.unlock();     
        } 
    } 

 
class WorkerTwo extends Thread{ 
    private Lock locker; 
    public WorkerTwo (Lock locker){ 
        this.locker = locker; 
    } 
     
    public void sayHello(){ 
        locker.lock(); 
        try{    System.out.println(Thread.currentThread().getName()+":call sayHello()"); 
            Thread.sleep(1000); 
        } catch (InterruptedException e) { 
            e.printStackTrace(); 
        }finally
            locker.unlock(); 
        } 
    } 
     
    public void run(){ 
        locker.lock();   
        try{        System.out.println(Thread.currentThread().getName()+":setp into critical section"); 
                        //测试锁的可重入性  
            sayHello(); 
        }finally
            locker.unlock();     
        } 
    } 

 
public class Test5 { 
    public static void main(String[] args) { 
        Lock locker = new ReentrantLock(); 
        WorkerOne wo= new WorkerOne(locker); 
        wo.setName("WorkerOne"); 
        WorkerTwo wt = new WorkerTwo(locker); 
        wt.setName("WorkerTwo"); 
         
        wt.start(); 
        wo.start();  
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.

输出:

WorkerTwo:setp into critical section
WorkerTwo:call sayHello()
WorkerOne:step into critical section
  • 1.
  • 2.
  • 3.

2、条件对象Condition,相当于wait-notify机制,提供一种线程间的等待通知机制,condition中的等待-通知方法是await(),signal(),signalAll(),也需要在互斥环境下被调用。

(1) 相关的接口

创建Condition对象,Condition对象是跟Lock关联在一起的。

Lock locker = new ReentrantLock(); 
Condition cond = locker.newCondition(); 
  • 1.
  • 2.

把此线程放到条件的等待集中。

void await(); 
  • 1.

解除此条件的等待集中所有线程的阻塞状态。

void signalAll(); 
  • 1.

在此条件的等待集中随机选择一个线程,解除其阻塞状态。

void signal(); 
  • 1.


(2) 使用的基本结构:

//初始时ok_to_proceed为false. 
locker.lock() 
try
     while(!ok_to_proceed){ 
//进入等待此条件集中,被阻塞,它维持状态直到另一个线程调用同一个条件上的。 
//signalAll/signal方法时为止。 
       cond.await(); 
     } 
}finally
cker.unlock(); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
locker.lock(); 
   try
      //调用将解除所有等待此条件下的线程的阻塞状态。当线程从等待集中被移走时,它们将再次成为可运行的,调度器将再次激活它们     
      //此时,它们将试图重新进入对象。一旦锁可获得,它们中的某个线程将从await调用返回,从而获得锁并从它被阻塞的地方继续执行。 
      ok_to_proceed = true
      cond.signalAll() or cond.signal(); 
   }finally
       locker.unlock(); 
   } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

ok_to_proceed也是为了防止wait-notify出现的问题,即再wait之间,notify()已经给出通知,此时wait只会一直等待下去,这样就保证了signal()线程的通知被await()线程接收到。

(3) 测试代码:

import java.util.concurrent.locks.Condition; 
import java.util.concurrent.locks.Lock; 
import java.util.concurrent.locks.ReentrantLock; 
class GlobalV{ 
    public final static Lock locker = new ReentrantLock(); 
    public final static Condition cond = locker.newCondition(); 
    public static boolean to_proceed = false

 
class Response extends Thread{ 
    public void run(){ 
        while(true){ 
            GlobalV.locker.lock(); 
            try
                while(!GlobalV.to_proceed){ 
                    GlobalV.cond.await(); 
                } 
System.out.println("Response:finish a job"); 
                GlobalV.to_proceed = false
                 
            }catch(Exception e){ 
                e.printStackTrace(); 
            }finally
                GlobalV.locker.unlock(); 
            }    
        } 
    } 

 
class Request extends Thread{ 
    public void run(){ 
        while(true){ 
            GlobalV.locker.lock();   
            try
                GlobalV.to_proceed = true
                GlobalV.cond.signalAll(); 
                System.out.println("Request:send a job to Response");    
            }finally
                GlobalV.locker.unlock(); 
            } 
            try { 
                Thread.sleep(2000); 
            } catch (InterruptedException e) { 
                e.printStackTrace(); 
            } 
        } 
    } 

 
public class Test6 { 
    public static void main(String[] args) { 
        Request req = new Request(); 
        Response res = new Response(); 
        req.start(); 
        res.start(); 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.

输出:

Request:send a job to Response
Response:finish a job
Request:send a job to Response
Response:finish a job
Request:send a job to Response
Response:finish a job
Request:send a job to Response
Response:finish a job
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

#p#

3、读写锁ReentrantReadWriteLock,适用于"读多写少"的多线程应用场景,"读-写"互斥,"写-写"互斥,而读-读可以共享同读锁,即一个线程获取读锁,其它线程可直接进入读,不会被阻塞。

(1) 相关接口

创建读写锁对象

ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); 
  • 1.

获取读锁

Lock readLock = rwLock.readLock(); 
  • 1.

获取写锁

Lock writeLock = rwLock.writeLock(); 
  • 1.

(2).读写锁使用基本结构

//对所有的读操作添加读锁 
readLock.lock(); 
try
//code to read 
}finally
readLock.unlock(); 
}  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
//对所有的写操作添加写锁 
  writeLock.lock();  
   try{  
//code to write  
   }finally{  
    writeLock.unlock();  
   }  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

(3) 测试代码:

import java.util.concurrent.locks.Lock; 
import java.util.concurrent.locks.ReentrantReadWriteLock; 
class Reader extends Thread { 
    private Lock readLock = null
    public Reader(Lock readLock) { 
        this.readLock = readLock; 
    } 
 
    public void run() { 
        while (true) { 
            readLock.lock(); 
            try { 
System.out.println(Thread.currentThread().getName() 
                        + ":read action for 1 seconds-"+ReadWriteLock.testVal); 
            } finally { 
                readLock.unlock(); 
            } 
            try { 
                Thread.sleep(1000); 
            } catch (InterruptedException e) { 
                e.printStackTrace(); 
            } 
        } 
    } 

 
class Writer extends Thread { 
    private Lock writeLock = null
    public Writer(Lock writeLock) { 
        this.writeLock = writeLock; 
    } 
    public void run() { 
        while (true) { 
            writeLock.lock(); 
            try { 
System.out.println(Thread.currentThread().getName() 
                        + ":write action for 2 seconds"); 
if(ReadWriteLock.testVal.equals("1111")) 
                    ReadWriteLock.testVal = "2222"
                else 
                    ReadWriteLock.testVal = "1111"
            } finally { 
                writeLock.unlock(); 
            } 
            try { 
                Thread.sleep(2000); 
            } catch (InterruptedException e) { 
                e.printStackTrace(); 
            } 
        } 
    } 

 
public class ReadWriteLock { 
    public static String  testVal = "Initiation"
    public static void main(String[] args) { 
        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); 
        Lock readLock = lock.readLock(); 
        Lock writeLock = lock.writeLock(); 
        Reader reader1 = new Reader(readLock); 
        reader1.setName("reader1"); 
        Reader reader2 = new Reader(readLock); 
        reader2.setName("reader2"); 
        Reader reader3 = new Reader(readLock); 
        reader3.setName("reader3"); 
        Reader reader4 = new Reader(readLock); 
        reader4.setName("reader4"); 
        Writer writer = new Writer(writeLock); 
        writer.setName("writer1"); 
        reader1.start(); 
        reader2.start(); 
        reader3.start(); 
        reader4.start(); 
        writer.start(); 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.

输出:

reader1:read action for 1 seconds-Initiation
reader3:read action for 1 seconds-Initiation
writer1:write action for 2 seconds
reader2:read action for 1 seconds-1111
reader4:read action for 1 seconds-1111
reader3:read action for 1 seconds-1111
reader1:read action for 1 seconds-1111
reader4:read action for 1 seconds-1111
reader2:read action for 1 seconds-1111
writer1:write action for 2 seconds
reader4:read action for 1 seconds-2222
reader1:read action for 1 seconds-2222
reader3:read action for 1 seconds-2222
reader2:read action for 1 seconds-2222
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

4、总结

Lock接口替代synchronized

Lock接口可以比sychronized提供更广泛的锁定操作,可以提供多把不同的锁,且锁之间互不干涉。

Lock接口提供lock()与unlock()方法,使用明确调用来完成同步的,OO思想好于前者。

Lock可以自由操控同步范围(scope)。

Lock接口支持nested lock(嵌套锁定),并提供了丰富的api。

Lock接口提供了tryLock()方法,支持尝试取得某个object lock。

原文链接:http://yuyiming1986.iteye.com/blog/1272979

【编辑推荐】

  1. Java的ClassLoader机制解析
  2. 精解Java中代理模式的实现
  3. Java中的分形几何:把递归用到极致
  4. Java自带的Future多线程模式
  5. 解析Java finally的神秘面纱
责任编辑:林师授 来源: yuyiming1986的博客
相关推荐

2024-07-05 08:32:36

2010-03-15 16:31:34

Java多线程

2024-06-28 08:45:58

2019-05-27 14:40:43

Java同步机制多线程编程

2017-12-15 10:20:56

MySQLInnoDB同步机制

2024-07-08 12:51:05

2009-08-12 13:37:01

Java synchr

2012-07-27 10:02:39

C#

2012-07-09 09:25:13

ibmdw

2025-03-31 00:01:12

2010-01-21 11:27:30

linux多线程机制线程同步

2019-08-22 14:30:21

技术Redis设计

2021-10-08 20:30:12

ZooKeeper选举机制

2011-06-22 13:47:16

Java多线程

2011-06-22 13:57:54

Java多线程

2016-09-20 15:21:35

LinuxInnoDBMysql

2019-11-22 18:52:31

进程同步机制编程语言

2024-07-25 11:53:53

2017-05-27 20:59:30

Java多线程synchronize

2023-09-01 08:27:34

Java多线程程序
点赞
收藏

51CTO技术栈公众号