`

java锁机制

阅读更多
2.4 锁机制
       临界区是指,使用同一个锁控制的同一段代码区或多段代码区之间,在同一时间内最多只能有一个线程在执行操作。这个概念与传统的临界区有略微的差别,这里不想强调这些概念上的差别,临界区的这样定义有利于我们对后面内容的理解。几乎所有设计到多线程的语言都会涉及到临界区和锁的概念,java也不例外,在java中可以有多种方式实现临界区语义,最常使用的是锁机制和Synchronized(Synchronized也是一种锁)关键字,有时也可以用阻塞队列达到同样的效果。(这里,我们不会对临界区的概念进行过多解释,相信已经有足够的书籍对这个概念进行了深入的探讨)。
2.4.1 Lock/Condition锁机制
       Lock/Condition模式临界区控制是比较复杂的,却是使用范围最广的。在稍后我们会看到使用Lock/Condition方式保护临界区在某些情况下是唯一选择。但是由于Lock/Condition模式的复杂性,很容易造成死锁,所以使用起来也要特别慎重。
       2.4.1 Lock
       可以在临界区代码开始的位置执行Lock类的lock方法,为代码块加锁,而在临界区的出口使用相同Lock实例的unlock方法,释放临界区资源。
Demo2-12中,主线程先创建了一个lockTest对象test,然后将相同的test对象交给两个不同的线程执行。子线程1获取到了lock后,开始执行before sleep输出语句,遇到sleep后,线程1阻塞将会放弃执行权,这时线程2可以获取执行权,当线程2执行lock方法时,发现锁已经被别的线程获取,所以线程2阻塞等待lock的释放。线程1从sleep中被唤醒后,将继续执行after  sleep语句,之后释放了锁,此时线程2从锁等待中被唤醒,执行临近区的内容,因此Demo2-12的输出是先线程1的两条语句,之后才输出线程2的两条语句。而Demo2-13在没有锁的保护下,程序无法保证先将线程1的两条语句输出后再执行线程2的输出,因此,Demo2-13的输出结果是交叉的。
Demo2-12和Demo2-13中使用的sleep语句使得当前线程放弃执行权一段时间,其他线程可以获取执行机会,此外,临界区是相对于同一个锁对象而言的,一般而言,被同一个锁锁住的同一个或者不同的临界区不能同时被两个以上线程同时执行。临界区的概念与前面提到的同步概念不同,同步强调不同线程间协调进度,而锁更强调的是保护代码不会被多个线程冲突执行。Demo2-14给出了这点说明。
       Demo2-14中,两个线程调用了不同的执行方法,但是这两个方法使用相同的锁进行加锁和解锁,因此他们属于线程冲突的。所以临界区是相对于锁对象来说的。同一时刻,有且仅有一个线程对同一个锁的一个或多个临界区进行操作。
Demo2-12 有锁的多线程临界区
package com.upc.upcgrid.guan.advancedJava.chapter02;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


class lockTest implements Runnable{
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
       lock.lock();
       try{
           System.err.println(Thread.currentThread().getName() + " before Sleep ");
           try {
              Thread.sleep(10);
           } catch (InterruptedException e) {}
           System.err.println(Thread.currentThread().getName() + " after sleep ");
       }finally{
           lock.unlock();
         
       }
    }
  
}
public class LockSyn { 
    public static void main(String[] args) throws InterruptedException {
       lockTest test = new lockTest();
       new Thread(test).start();
       new Thread(test).start();
     
    }
}
Demo 2-12 执行结果:
Thread-0 before Sleep
Thread-0 after sleep
Thread-1 before Sleep
Thread-1 after sleep
Demo1-13 没有锁的临界区
package com.upc.upcgrid.guan.advancedJava.chapter02;

class NolockTest implements Runnable{
    @Override
    public void run() {
           System.err.println(Thread.currentThread().getName() + " before Sleep ");
           try {
              Thread.sleep(10);
           } catch (InterruptedException e) {}
           System.err.println(Thread.currentThread().getName() + " after sleep ");    
    }
  
}

public class NoLockSyn {
    public static void main(String[] args) throws InterruptedException {
       NolockTest test = new NolockTest();
       new Thread(test).start();
       new Thread(test).start();
     
    }
}
Demo2-13的执行结果:
Thread-0 before Sleep
Thread-1 before Sleep
Thread-0 after sleep
Thread-1 after sleep
Demo2-14 临界区是相对锁
package com.upc.upcgrid.guan.advancedJava.chapter02;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class lockTestMuti implements Runnable{
    Lock lock = new ReentrantLock();
    volatile boolean isFunction1 = false;
  
    private void function1()
    {
       lock.lock();
       try{
           System.err.println("function1");
           System.err.println(Thread.currentThread().getName() + " before Sleep ");
           try {
              Thread.sleep(10);
           } catch (InterruptedException e) {}
           System.err.println(Thread.currentThread().getName() + " after sleep ");
       }finally{
           lock.unlock();
         
       }
    } 
    private void function2()
    {
       lock.lock();
       try{
           System.err.println("function2");
           System.err.println(Thread.currentThread().getName() + " before Sleep ");
           try {
              Thread.sleep(10);
           } catch (InterruptedException e) {}
           System.err.println(Thread.currentThread().getName() + " after sleep ");
       }finally{
           lock.unlock();
         
       }
    }
  
    @Override
    public void run() {
       isFunction1 = !isFunction1;
       if(isFunction1)
       {
           function1();
       }else
       {
           function2();
       }
    }
  
}

public class LockSynMutiMethods {
    public static void main(String[] args) throws InterruptedException {
       lockTestMuti test = new lockTestMuti();
       new Thread(test).start();
       new Thread(test).start();
     
    }
}
Demo2-14 执行结果
function1
Thread-0 before Sleep
Thread-0 after sleep
function2
Thread-1 before Sleep
Thread-1 after sleep
2.4.2 可重入锁
       ReentrantLock是实现Lock接口的一个锁的实现,被称为可重入锁。所谓锁的可重入性是指,同一个线程可以多次获取相同的锁对象。ReentrantLock锁内部维护一个关于锁的计数,每次调用lock方法,这个计数加一,每次调用一次unlock方法,这个计数减一,直至ReentrantLock中的计数值减少为0时,锁才会真正被释放。
       Demo2-15中,主线程开启了一个子线程,在子线程中创建了一个可重入锁,线程在执行function1时获取了锁,在function1调用function2时又需要获取锁,这时lock的计数应当是2,当离开function2时执行了unlock方法,此时的计数是1,当离开function1时,调用了unlock方法,此时的锁计数为0,锁被释放。Demo2-16给出了可重入锁的模拟实现,在这个示例中,通过一个阻塞队列完成锁的阻塞功能,代码原理请读者自行分析。
Demo2-15
package com.upc.upcgrid.guan.advancedJava.chapter02;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockSyn {
    public static void main(String[] args) {
       new Thread(new Runnable() {
           Lock lock = new  ReentrantLock();
           private void function1()
           {
              lock.lock();
              try{
                  System.err.println(Thread.currentThread().getName());
                  function2();
              }finally{
                  lock.unlock();
              }
           }
         
           private void function2()
           {
              lock.lock();
              try{
                  System.err.println(Thread.currentThread().getName());
              }finally{
                  lock.unlock();
              }
           }
           @Override
           public void run() {
              function1();
           }
       }).start();
    }
}
输出结果:
Thread-0
Thread-0

Demo2-16 模拟可重入锁
package com.upc.upcgrid.guan.advancedJava.chapter02;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

class LockElement{
    public LockElement(Thread thread) {
       this.thread = thread;
    }
    public int count = 1;
    public Thread thread;
}

public class MyReentrantLock {

    private BlockingQueue<LockElement> queue = new ArrayBlockingQueue<LockElement>(1);
    public void lock() throws InterruptedException
    {
       LockElement element = queue.peek();
       if(element != null && element.thread == Thread.currentThread())//锁的重入
           ++element.count;
       else if(element != null)
           queue.put(element);//队列空,直接获取锁
       else
       {
           queue.put(new LockElement(Thread.currentThread()));//队列非空,需要阻塞等待放入新锁
       }
    }
  
    public void unlock() throws InterruptedException
    {
       LockElement element = queue.peek();
       if(element != null && element.thread == Thread.currentThread())//重入锁释放
       {
           if(--element.count <= 0)//计数减到0,释放锁资源,否则仅对计数减少1
              queue.take();
       }else{//这是异常,正常情况下不能出现
           System.err.println("锁释放错误");
       }
    }
  

}
2.4 锁机制
       临界区是指,使用同一个锁控制的同一段代码区或多段代码区之间,在同一时间内最多只能有一个线程在执行操作。这个概念与传统的临界区有略微的差别,这里不想强调这些概念上的差别,临界区的这样定义有利于我们对后面内容的理解。几乎所有设计到多线程的语言都会涉及到临界区和锁的概念,java也不例外,在java中可以有多种方式实现临界区语义,最常使用的是锁机制和Synchronized(Synchronized也是一种锁)关键字,有时也可以用阻塞队列达到同样的效果。(这里,我们不会对临界区的概念进行过多解释,相信已经有足够的书籍对这个概念进行了深入的探讨)。
2.4.1 Lock/Condition锁机制
       Lock/Condition模式临界区控制是比较复杂的,却是使用范围最广的。在稍后我们会看到使用Lock/Condition方式保护临界区在某些情况下是唯一选择。但是由于Lock/Condition模式的复杂性,很容易造成死锁,所以使用起来也要特别慎重。
       2.4.1 Lock
       可以在临界区代码开始的位置执行Lock类的lock方法,为代码块加锁,而在临界区的出口使用相同Lock实例的unlock方法,释放临界区资源。
Demo2-12中,主线程先创建了一个lockTest对象test,然后将相同的test对象交给两个不同的线程执行。子线程1获取到了lock后,开始执行before sleep输出语句,遇到sleep后,线程1阻塞将会放弃执行权,这时线程2可以获取执行权,当线程2执行lock方法时,发现锁已经被别的线程获取,所以线程2阻塞等待lock的释放。线程1从sleep中被唤醒后,将继续执行after  sleep语句,之后释放了锁,此时线程2从锁等待中被唤醒,执行临近区的内容,因此Demo2-12的输出是先线程1的两条语句,之后才输出线程2的两条语句。而Demo2-13在没有锁的保护下,程序无法保证先将线程1的两条语句输出后再执行线程2的输出,因此,Demo2-13的输出结果是交叉的。
Demo2-12和Demo2-13中使用的sleep语句使得当前线程放弃执行权一段时间,其他线程可以获取执行机会,此外,临界区是相对于同一个锁对象而言的,一般而言,被同一个锁锁住的同一个或者不同的临界区不能同时被两个以上线程同时执行。临界区的概念与前面提到的同步概念不同,同步强调不同线程间协调进度,而锁更强调的是保护代码不会被多个线程冲突执行。Demo2-14给出了这点说明。
       Demo2-14中,两个线程调用了不同的执行方法,但是这两个方法使用相同的锁进行加锁和解锁,因此他们属于线程冲突的。所以临界区是相对于锁对象来说的。同一时刻,有且仅有一个线程对同一个锁的一个或多个临界区进行操作。
Demo2-12 有锁的多线程临界区
package com.upc.upcgrid.guan.advancedJava.chapter02;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


class lockTest implements Runnable{
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
       lock.lock();
       try{
           System.err.println(Thread.currentThread().getName() + " before Sleep ");
           try {
              Thread.sleep(10);
           } catch (InterruptedException e) {}
           System.err.println(Thread.currentThread().getName() + " after sleep ");
       }finally{
           lock.unlock();
         
       }
    }
  
}
public class LockSyn { 
    public static void main(String[] args) throws InterruptedException {
       lockTest test = new lockTest();
       new Thread(test).start();
       new Thread(test).start();
     
    }
}
Demo 2-12 执行结果:
Thread-0 before Sleep
Thread-0 after sleep
Thread-1 before Sleep
Thread-1 after sleep
Demo1-13 没有锁的临界区
package com.upc.upcgrid.guan.advancedJava.chapter02;

class NolockTest implements Runnable{
    @Override
    public void run() {
           System.err.println(Thread.currentThread().getName() + " before Sleep ");
           try {
              Thread.sleep(10);
           } catch (InterruptedException e) {}
           System.err.println(Thread.currentThread().getName() + " after sleep ");    
    }
  
}

public class NoLockSyn {
    public static void main(String[] args) throws InterruptedException {
       NolockTest test = new NolockTest();
       new Thread(test).start();
       new Thread(test).start();
     
    }
}
Demo2-13的执行结果:
Thread-0 before Sleep
Thread-1 before Sleep
Thread-0 after sleep
Thread-1 after sleep
Demo2-14 临界区是相对锁
package com.upc.upcgrid.guan.advancedJava.chapter02;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class lockTestMuti implements Runnable{
    Lock lock = new ReentrantLock();
    volatile boolean isFunction1 = false;
  
    private void function1()
    {
       lock.lock();
       try{
           System.err.println("function1");
           System.err.println(Thread.currentThread().getName() + " before Sleep ");
           try {
              Thread.sleep(10);
           } catch (InterruptedException e) {}
           System.err.println(Thread.currentThread().getName() + " after sleep ");
       }finally{
           lock.unlock();
         
       }
    } 
    private void function2()
    {
       lock.lock();
       try{
           System.err.println("function2");
           System.err.println(Thread.currentThread().getName() + " before Sleep ");
           try {
              Thread.sleep(10);
           } catch (InterruptedException e) {}
           System.err.println(Thread.currentThread().getName() + " after sleep ");
       }finally{
           lock.unlock();
         
       }
    }
  
    @Override
    public void run() {
       isFunction1 = !isFunction1;
       if(isFunction1)
       {
           function1();
       }else
       {
           function2();
       }
    }
  
}

public class LockSynMutiMethods {
    public static void main(String[] args) throws InterruptedException {
       lockTestMuti test = new lockTestMuti();
       new Thread(test).start();
       new Thread(test).start();
     
    }
}
Demo2-14 执行结果
function1
Thread-0 before Sleep
Thread-0 after sleep
function2
Thread-1 before Sleep
Thread-1 after sleep
2.4.2 可重入锁
       ReentrantLock是实现Lock接口的一个锁的实现,被称为可重入锁。所谓锁的可重入性是指,同一个线程可以多次获取相同的锁对象。ReentrantLock锁内部维护一个关于锁的计数,每次调用lock方法,这个计数加一,每次调用一次unlock方法,这个计数减一,直至ReentrantLock中的计数值减少为0时,锁才会真正被释放。
       Demo2-15中,主线程开启了一个子线程,在子线程中创建了一个可重入锁,线程在执行function1时获取了锁,在function1调用function2时又需要获取锁,这时lock的计数应当是2,当离开function2时执行了unlock方法,此时的计数是1,当离开function1时,调用了unlock方法,此时的锁计数为0,锁被释放。Demo2-16给出了可重入锁的模拟实现,在这个示例中,通过一个阻塞队列完成锁的阻塞功能,代码原理请读者自行分析。
Demo2-15
package com.upc.upcgrid.guan.advancedJava.chapter02;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockSyn {
    public static void main(String[] args) {
       new Thread(new Runnable() {
           Lock lock = new  ReentrantLock();
           private void function1()
           {
              lock.lock();
              try{
                  System.err.println(Thread.currentThread().getName());
                  function2();
              }finally{
                  lock.unlock();
              }
           }
         
           private void function2()
           {
              lock.lock();
              try{
                  System.err.println(Thread.currentThread().getName());
              }finally{
                  lock.unlock();
              }
           }
           @Override
           public void run() {
              function1();
           }
       }).start();
    }
}
输出结果:
Thread-0
Thread-0

Demo2-16 模拟可重入锁
package com.upc.upcgrid.guan.advancedJava.chapter02;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

class LockElement{
    public LockElement(Thread thread) {
       this.thread = thread;
    }
    public int count = 1;
    public Thread thread;
}

public class MyReentrantLock {

    private BlockingQueue<LockElement> queue = new ArrayBlockingQueue<LockElement>(1);
    public void lock() throws InterruptedException
    {
       LockElement element = queue.peek();
       if(element != null && element.thread == Thread.currentThread())//锁的重入
           ++element.count;
       else if(element != null)
           queue.put(element);//队列空,直接获取锁
       else
       {
           queue.put(new LockElement(Thread.currentThread()));//队列非空,需要阻塞等待放入新锁
       }
    }
  
    public void unlock() throws InterruptedException
    {
       LockElement element = queue.peek();
       if(element != null && element.thread == Thread.currentThread())//重入锁释放
       {
           if(--element.count <= 0)//计数减到0,释放锁资源,否则仅对计数减少1
              queue.take();
       }else{//这是异常,正常情况下不能出现
           System.err.println("锁释放错误");
       }
    }
  

}
2.4 锁机制
       临界区是指,使用同一个锁控制的同一段代码区或多段代码区之间,在同一时间内最多只能有一个线程在执行操作。这个概念与传统的临界区有略微的差别,这里不想强调这些概念上的差别,临界区的这样定义有利于我们对后面内容的理解。几乎所有设计到多线程的语言都会涉及到临界区和锁的概念,java也不例外,在java中可以有多种方式实现临界区语义,最常使用的是锁机制和Synchronized(Synchronized也是一种锁)关键字,有时也可以用阻塞队列达到同样的效果。(这里,我们不会对临界区的概念进行过多解释,相信已经有足够的书籍对这个概念进行了深入的探讨)。
2.4.1 Lock/Condition锁机制
       Lock/Condition模式临界区控制是比较复杂的,却是使用范围最广的。在稍后我们会看到使用Lock/Condition方式保护临界区在某些情况下是唯一选择。但是由于Lock/Condition模式的复杂性,很容易造成死锁,所以使用起来也要特别慎重。
       2.4.1 Lock
       可以在临界区代码开始的位置执行Lock类的lock方法,为代码块加锁,而在临界区的出口使用相同Lock实例的unlock方法,释放临界区资源。
Demo2-12中,主线程先创建了一个lockTest对象test,然后将相同的test对象交给两个不同的线程执行。子线程1获取到了lock后,开始执行before sleep输出语句,遇到sleep后,线程1阻塞将会放弃执行权,这时线程2可以获取执行权,当线程2执行lock方法时,发现锁已经被别的线程获取,所以线程2阻塞等待lock的释放。线程1从sleep中被唤醒后,将继续执行after  sleep语句,之后释放了锁,此时线程2从锁等待中被唤醒,执行临近区的内容,因此Demo2-12的输出是先线程1的两条语句,之后才输出线程2的两条语句。而Demo2-13在没有锁的保护下,程序无法保证先将线程1的两条语句输出后再执行线程2的输出,因此,Demo2-13的输出结果是交叉的。
Demo2-12和Demo2-13中使用的sleep语句使得当前线程放弃执行权一段时间,其他线程可以获取执行机会,此外,临界区是相对于同一个锁对象而言的,一般而言,被同一个锁锁住的同一个或者不同的临界区不能同时被两个以上线程同时执行。临界区的概念与前面提到的同步概念不同,同步强调不同线程间协调进度,而锁更强调的是保护代码不会被多个线程冲突执行。Demo2-14给出了这点说明。
       Demo2-14中,两个线程调用了不同的执行方法,但是这两个方法使用相同的锁进行加锁和解锁,因此他们属于线程冲突的。所以临界区是相对于锁对象来说的。同一时刻,有且仅有一个线程对同一个锁的一个或多个临界区进行操作。
Demo2-12 有锁的多线程临界区
package com.upc.upcgrid.guan.advancedJava.chapter02;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


class lockTest implements Runnable{
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
       lock.lock();
       try{
           System.err.println(Thread.currentThread().getName() + " before Sleep ");
           try {
              Thread.sleep(10);
           } catch (InterruptedException e) {}
           System.err.println(Thread.currentThread().getName() + " after sleep ");
       }finally{
           lock.unlock();
         
       }
    }
  
}
public class LockSyn { 
    public static void main(String[] args) throws InterruptedException {
       lockTest test = new lockTest();
       new Thread(test).start();
       new Thread(test).start();
     
    }
}
Demo 2-12 执行结果:
Thread-0 before Sleep
Thread-0 after sleep
Thread-1 before Sleep
Thread-1 after sleep
Demo1-13 没有锁的临界区
package com.upc.upcgrid.guan.advancedJava.chapter02;

class NolockTest implements Runnable{
    @Override
    public void run() {
           System.err.println(Thread.currentThread().getName() + " before Sleep ");
           try {
              Thread.sleep(10);
           } catch (InterruptedException e) {}
           System.err.println(Thread.currentThread().getName() + " after sleep ");    
    }
  
}

public class NoLockSyn {
    public static void main(String[] args) throws InterruptedException {
       NolockTest test = new NolockTest();
       new Thread(test).start();
       new Thread(test).start();
     
    }
}
Demo2-13的执行结果:
Thread-0 before Sleep
Thread-1 before Sleep
Thread-0 after sleep
Thread-1 after sleep
Demo2-14 临界区是相对锁
package com.upc.upcgrid.guan.advancedJava.chapter02;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class lockTestMuti implements Runnable{
    Lock lock = new ReentrantLock();
    volatile boolean isFunction1 = false;
  
    private void function1()
    {
       lock.lock();
       try{
           System.err.println("function1");
           System.err.println(Thread.currentThread().getName() + " before Sleep ");
           try {
              Thread.sleep(10);
           } catch (InterruptedException e) {}
           System.err.println(Thread.currentThread().getName() + " after sleep ");
       }finally{
           lock.unlock();
         
       }
    } 
    private void function2()
    {
       lock.lock();
       try{
           System.err.println("function2");
           System.err.println(Thread.currentThread().getName() + " before Sleep ");
           try {
              Thread.sleep(10);
           } catch (InterruptedException e) {}
           System.err.println(Thread.currentThread().getName() + " after sleep ");
       }finally{
           lock.unlock();
         
       }
    }
  
    @Override
    public void run() {
       isFunction1 = !isFunction1;
       if(isFunction1)
       {
           function1();
       }else
       {
           function2();
       }
    }
  
}

public class LockSynMutiMethods {
    public static void main(String[] args) throws InterruptedException {
       lockTestMuti test = new lockTestMuti();
       new Thread(test).start();
       new Thread(test).start();
     
    }
}
Demo2-14 执行结果
function1
Thread-0 before Sleep
Thread-0 after sleep
function2
Thread-1 before Sleep
Thread-1 after sleep
2.4.2 可重入锁
       ReentrantLock是实现Lock接口的一个锁的实现,被称为可重入锁。所谓锁的可重入性是指,同一个线程可以多次获取相同的锁对象。ReentrantLock锁内部维护一个关于锁的计数,每次调用lock方法,这个计数加一,每次调用一次unlock方法,这个计数减一,直至ReentrantLock中的计数值减少为0时,锁才会真正被释放。
       Demo2-15中,主线程开启了一个子线程,在子线程中创建了一个可重入锁,线程在执行function1时获取了锁,在function1调用function2时又需要获取锁,这时lock的计数应当是2,当离开function2时执行了unlock方法,此时的计数是1,当离开function1时,调用了unlock方法,此时的锁计数为0,锁被释放。Demo2-16给出了可重入锁的模拟实现,在这个示例中,通过一个阻塞队列完成锁的阻塞功能,代码原理请读者自行分析。
Demo2-15
package com.upc.upcgrid.guan.advancedJava.chapter02;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockSyn {
    public static void main(String[] args) {
       new Thread(new Runnable() {
           Lock lock = new  ReentrantLock();
           private void function1()
           {
              lock.lock();
              try{
                  System.err.println(Thread.currentThread().getName());
                  function2();
              }finally{
                  lock.unlock();
              }
           }
         
           private void function2()
           {
              lock.lock();
              try{
                  System.err.println(Thread.currentThread().getName());
              }finally{
                  lock.unlock();
              }
           }
           @Override
           public void run() {
              function1();
           }
       }).start();
    }
}
输出结果:
Thread-0
Thread-0

Demo2-16 模拟可重入锁
package com.upc.upcgrid.guan.advancedJava.chapter02;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

class LockElement{
    public LockElement(Thread thread) {
       this.thread = thread;
    }
    public int count = 1;
    public Thread thread;
}

public class MyReentrantLock {

    private BlockingQueue<LockElement> queue = new ArrayBlockingQueue<LockElement>(1);
    public void lock() throws InterruptedException
    {
       LockElement element = queue.peek();
       if(element != null && element.thread == Thread.currentThread())//锁的重入
           ++element.count;
       else if(element != null)
           queue.put(element);//队列空,直接获取锁
       else
       {
           queue.put(new LockElement(Thread.currentThread()));//队列非空,需要阻塞等待放入新锁
       }
    }
  
    public void unlock() throws InterruptedException
    {
       LockElement element = queue.peek();
       if(element != null && element.thread == Thread.currentThread())//重入锁释放
       {
           if(--element.count <= 0)//计数减到0,释放锁资源,否则仅对计数减少1
              queue.take();
       }else{//这是异常,正常情况下不能出现
           System.err.println("锁释放错误");
       }
    }
  

}
2.4 锁机制
       临界区是指,使用同一个锁控制的同一段代码区或多段代码区之间,在同一时间内最多只能有一个线程在执行操作。这个概念与传统的临界区有略微的差别,这里不想强调这些概念上的差别,临界区的这样定义有利于我们对后面内容的理解。几乎所有设计到多线程的语言都会涉及到临界区和锁的概念,java也不例外,在java中可以有多种方式实现临界区语义,最常使用的是锁机制和Synchronized(Synchronized也是一种锁)关键字,有时也可以用阻塞队列达到同样的效果。(这里,我们不会对临界区的概念进行过多解释,相信已经有足够的书籍对这个概念进行了深入的探讨)。
2.4.1 Lock/Condition锁机制
       Lock/Condition模式临界区控制是比较复杂的,却是使用范围最广的。在稍后我们会看到使用Lock/Condition方式保护临界区在某些情况下是唯一选择。但是由于Lock/Condition模式的复杂性,很容易造成死锁,所以使用起来也要特别慎重。
       2.4.1 Lock
       可以在临界区代码开始的位置执行Lock类的lock方法,为代码块加锁,而在临界区的出口使用相同Lock实例的unlock方法,释放临界区资源。
Demo2-12中,主线程先创建了一个lockTest对象test,然后将相同的test对象交给两个不同的线程执行。子线程1获取到了lock后,开始执行before sleep输出语句,遇到sleep后,线程1阻塞将会放弃执行权,这时线程2可以获取执行权,当线程2执行lock方法时,发现锁已经被别的线程获取,所以线程2阻塞等待lock的释放。线程1从sleep中被唤醒后,将继续执行after  sleep语句,之后释放了锁,此时线程2从锁等待中被唤醒,执行临近区的内容,因此Demo2-12的输出是先线程1的两条语句,之后才输出线程2的两条语句。而Demo2-13在没有锁的保护下,程序无法保证先将线程1的两条语句输出后再执行线程2的输出,因此,Demo2-13的输出结果是交叉的。
Demo2-12和Demo2-13中使用的sleep语句使得当前线程放弃执行权一段时间,其他线程可以获取执行机会,此外,临界区是相对于同一个锁对象而言的,一般而言,被同一个锁锁住的同一个或者不同的临界区不能同时被两个以上线程同时执行。临界区的概念与前面提到的同步概念不同,同步强调不同线程间协调进度,而锁更强调的是保护代码不会被多个线程冲突执行。Demo2-14给出了这点说明。
       Demo2-14中,两个线程调用了不同的执行方法,但是这两个方法使用相同的锁进行加锁和解锁,因此他们属于线程冲突的。所以临界区是相对于锁对象来说的。同一时刻,有且仅有一个线程对同一个锁的一个或多个临界区进行操作。
Demo2-12 有锁的多线程临界区
package com.upc.upcgrid.guan.advancedJava.chapter02;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


class lockTest implements Runnable{
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
       lock.lock();
       try{
           System.err.println(Thread.currentThread().getName() + " before Sleep ");
           try {
              Thread.sleep(10);
           } catch (InterruptedException e) {}
           System.err.println(Thread.currentThread().getName() + " after sleep ");
       }finally{
           lock.unlock();
         
       }
    }
  
}
public class LockSyn { 
    public static void main(String[] args) throws InterruptedException {
       lockTest test = new lockTest();
       new Thread(test).start();
       new Thread(test).start();
     
    }
}
Demo 2-12 执行结果:
Thread-0 before Sleep
Thread-0 after sleep
Thread-1 before Sleep
Thread-1 after sleep
Demo1-13 没有锁的临界区
package com.upc.upcgrid.guan.advancedJava.chapter02;

class NolockTest implements Runnable{
    @Override
    public void run() {
           System.err.println(Thread.currentThread().getName() + " before Sleep ");
           try {
              Thread.sleep(10);
           } catch (InterruptedException e) {}
           System.err.println(Thread.currentThread().getName() + " after sleep ");    
    }
  
}

public class NoLockSyn {
    public static void main(String[] args) throws InterruptedException {
       NolockTest test = new NolockTest();
       new Thread(test).start();
       new Thread(test).start();
     
    }
}
Demo2-13的执行结果:
Thread-0 before Sleep
Thread-1 before Sleep
Thread-0 after sleep
Thread-1 after sleep
Demo2-14 临界区是相对锁
package com.upc.upcgrid.guan.advancedJava.chapter02;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class lockTestMuti implements Runnable{
    Lock lock = new ReentrantLock();
    volatile boolean isFunction1 = false;
  
    private void function1()
    {
       lock.lock();
       try{
           System.err.println("function1");
           System.err.println(Thread.currentThread().getName() + " before Sleep ");
           try {
              Thread.sleep(10);
           } catch (InterruptedException e) {}
           System.err.println(Thread.currentThread().getName() + " after sleep ");
       }finally{
           lock.unlock();
         
       }
    } 
    private void function2()
    {
       lock.lock();
       try{
           System.err.println("function2");
           System.err.println(Thread.currentThread().getName() + " before Sleep ");
           try {
              Thread.sleep(10);
           } catch (InterruptedException e) {}
           System.err.println(Thread.currentThread().getName() + " after sleep ");
       }finally{
           lock.unlock();
         
       }
    }
  
    @Override
    public void run() {
       isFunction1 = !isFunction1;
       if(isFunction1)
       {
           function1();
       }else
       {
           function2();
       }
    }
  
}

public class LockSynMutiMethods {
    public static void main(String[] args) throws InterruptedException {
       lockTestMuti test = new lockTestMuti();
       new Thread(test).start();
       new Thread(test).start();
     
    }
}
Demo2-14 执行结果
function1
Thread-0 before Sleep
Thread-0 after sleep
function2
Thread-1 before Sleep
Thread-1 after sleep
2.4.2 可重入锁
       ReentrantLock是实现Lock接口的一个锁的实现,被称为可重入锁。所谓锁的可重入性是指,同一个线程可以多次获取相同的锁对象。ReentrantLock锁内部维护一个关于锁的计数,每次调用lock方法,这个计数加一,每次调用一次unlock方法,这个计数减一,直至ReentrantLock中的计数值减少为0时,锁才会真正被释放。
       Demo2-15中,主线程开启了一个子线程,在子线程中创建了一个可重入锁,线程在执行function1时获取了锁,在function1调用function2时又需要获取锁,这时lock的计数应当是2,当离开function2时执行了unlock方法,此时的计数是1,当离开function1时,调用了unlock方法,此时的锁计数为0,锁被释放。Demo2-16给出了可重入锁的模拟实现,在这个示例中,通过一个阻塞队列完成锁的阻塞功能,代码原理请读者自行分析。
Demo2-15
package com.upc.upcgrid.guan.advancedJava.chapter02;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockSyn {
    public static void main(String[] args) {
       new Thread(new Runnable() {
           Lock lock = new  ReentrantLock();
           private void function1()
           {
              lock.lock();
              try{
                  System.err.println(Thread.currentThread().getName());
                  function2();
              }finally{
                  lock.unlock();
              }
           }
         
           private void function2()
           {
              lock.lock();
              try{
                  System.err.println(Thread.currentThread().getName());
              }finally{
                  lock.unlock();
              }
           }
           @Override
           public void run() {
              function1();
           }
       }).start();
    }
}
输出结果:
Thread-0
Thread-0

Demo2-16 模拟可重入锁
package com.upc.upcgrid.guan.advancedJava.chapter02;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

class LockElement{
    public LockElement(Thread thread) {
       this.thread = thread;
    }
    public int count = 1;
    public Thread thread;
}

public class MyReentrantLock {

    private BlockingQueue<LockElement> queue = new ArrayBlockingQueue<LockElement>(1);
    public void lock() throws InterruptedException
    {
       LockElement element = queue.peek();
       if(element != null && element.thread == Thread.currentThread())//锁的重入
           ++element.count;
       else if(element != null)
           queue.put(element);//队列空,直接获取锁
       else
       {
           queue.put(new LockElement(Thread.currentThread()));//队列非空,需要阻塞等待放入新锁
       }
    }
  
    public void unlock() throws InterruptedException
    {
       LockElement element = queue.peek();
       if(element != null && element.thread == Thread.currentThread())//重入锁释放
       {
           if(--element.count <= 0)//计数减到0,释放锁资源,否则仅对计数减少1
              queue.take();
       }else{//这是异常,正常情况下不能出现
           System.err.println("锁释放错误");
       }
    }
  

}

分享到:
评论

相关推荐

    Java锁机制详解.pdf

    Java锁机制是Java多线程编程中的核心概念之一,其主要目的是确保在多线程环境下,多个线程能够安全地访问共享资源,避免数据不一致的问题。Java锁机制的发展历经了多个版本的改进,尤其是Java 5.0引入的显示锁...

    java锁机制Synchronizedjava锁机制Synchronized

    "Java 锁机制 Synchronized" Java 锁机制 Synchronized 是 Java 语言中的一种同步机制,用于解决多线程并发访问共享资源时可能出现的一些问题。 Java 锁机制 Synchronized 的概念 在 Java 中,每个对象都可以被...

    java锁机制详解.pdf

    Java锁机制是多线程编程中的关键概念,用于控制对共享资源的并发访问。在Java中,主要的锁机制包括`synchronized`关键字和`Lock`接口(如`ReentrantLock`)。下面将详细讲解这两种锁机制及其应用。 1. `...

    面向Java锁机制的字节码自动重构框架.zip

    Java锁机制是多线程编程中的关键组成部分,用于控制对共享资源的访问,确保并发环境下的数据一致性。本文将深入探讨Java锁机制,并基于提供的"面向Java锁机制的字节码自动重构框架"来讨论其背后的原理和应用。 在...

    彻底理解Java中的各种锁.pdf

    通过以上对Java锁机制的详细介绍,可以看出Java在并发控制方面具有丰富的工具和策略,它们能够帮助开发者在多线程编程中处理好资源竞争和线程协作的问题,从而编写出高效且线程安全的应用程序。

    面向Java锁机制的字节码自动重构框架.pdf

    根据提供的文件内容,以下是关于“面向Java锁机制的字节码自动重构框架”的详细知识点解析: 1. Java锁机制 Java语言提供了多种锁机制,包括同步锁(synchronized),可重入锁(ReentrantLock)和读写锁(ReadWriteLock)...

    java锁机制Synchronized.pdf

    java锁机制Synchronized.pdf

    java锁机制Synchronized参考.pdf

    java锁机制Synchronized参考.pdf

    java锁机制Synchronized[归纳].pdf

    java锁机制Synchronized[归纳].pdf

    java锁详解.pdf

    5. 重量级锁:重量级锁是 synchronized 锁的一种实现方式,使用互斥量来实现锁机制。 二、ReentrantLock 锁 1. 锁的原理:ReentrantLock 锁是基于 AQS(AbstractQueuedSynchronizer)机制来实现的。 2. 锁的分类:...

    Java的锁机制的学习和使用

    #### 一、Java锁机制概览 Java中的锁机制主要用于解决多线程环境下的资源竞争问题。在并发编程中,为了保证数据一致性与正确性,通常需要采用各种锁来控制对共享资源的访问。Java语言提供了多种锁机制,包括`...

    Java锁机制Lock用法示例

    Java锁机制Lock用法示例 Java锁机制Lock是Java并发编程中的一种同步机制,用于解决多线程安全问题。Lock机制是Java 1.5以后引入的显示锁机制,相比于传统的synchronized隐式锁机制,Lock机制提供了更灵活和高效的...

    java进阶提高学习教程-14锁机制.pptx

    Java 锁机制详解 Java 中的锁机制是多线程同步机制的核心,用于控制多个线程访问共享资源。锁机制可以防止多个线程同时访问共享资源,避免线程之间的干扰和冲突。 锁定义 锁是一种用来管理控制多线程访问共享资源...

Global site tag (gtag.js) - Google Analytics