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锁机制的更多相关文章

  1. 转 : 深入解析Java锁机制

    深入解析Java锁机制 https://mp.weixin.qq.com/s?__biz=MzU0OTE4MzYzMw%3D%3D&mid=2247485524&idx=1&s ...

  2. Java 锁机制总结

    锁的种类 独享锁 VS 共享锁 独享锁:锁只能被一个线程持有(synchronized) 共享锁:锁可以被多个程序所持有(读写锁) 乐观锁 VS 悲观锁 乐观锁:每次去拿数据的时候都乐观地认为别人不会 ...

  3. java锁机制的面试题

    java锁机制的面试题 1.ABA问题 2.CAS乐观锁 3.synchronize实现原理 4.synchronize与lock的区别 5.volatile实现原理 6.乐观锁的业务场景及实现方式 ...

  4. Java锁机制深入理解

    Java锁机制 背景知识 指令流水线 ​ CPU的基本工作是执行存储的指令序列,即程序.程序的执行过程实际上是不断地取出指令.分析指令.执行指令的过程. ​ 几乎所有的冯•诺伊曼型计算机的CPU,其工 ...

  5. Java锁机制了解一下

    前言 回顾前面: 多线程三分钟就可以入个门了! Thread源码剖析 多线程基础必要知识点!看了学习多线程事半功倍 只有光头才能变强! 本文章主要讲的是Java多线程加锁机制,有两种: Synchro ...

  6. JAVA锁机制-可重入锁,可中断锁,公平锁,读写锁,自旋锁,

    如果需要查看具体的synchronized和lock的实现原理,请参考:解决多线程安全问题-无非两个方法synchronized和lock 具体原理(百度) 在并发编程中,经常遇到多个线程访问同一个 ...

  7. JAVA锁机制(上)

    在实际开发中经常会用到多线程协作来处理问题,锁是处理线程安全不可缺少的机制.在JAVA中可以通过至少三种方式来实现线程锁. 1.  synchronized修饰符,这种锁机制是虚拟机实现的一种锁. 2 ...

  8. java 锁机制(synchronized 与 Lock)

    在java中,解决同步问题,很多时候都会使用到synchronized和Lock,这两者都是在多线程并发时候常使用的锁机制. synchronized是java中的一个关键字,也就是说是java内置的 ...

  9. 【面试专栏】JAVA锁机制

    1. 悲观锁 / 乐观锁   在Java和数据库中都存在悲观锁和乐观锁的应用.Mysql锁机制中的悲观锁和乐观锁请查看:   Mysql锁机制--悲观锁和乐观锁   悲观锁:在获得数据时先加锁,只到数 ...

随机推荐

  1. MySQL buffer pool中的三种链

    三种page.三种list.LRU控制调优 一.innodb buffer pool中的三种页 1.free page:从未用过的页 2.clean page:干净的页,数据页的数据和磁盘一致 3.d ...

  2. SharePoint 2016 配置工作流环境

    前言 SharePoint 2016 默认创建工作流的时候,工作流平台只包含2010版本,如果想要使用状态机工作流,需要单独安装workflow manager 1.0才可以,下面,我们为大家介绍一下 ...

  3. Git在tortoiseGit以及eclipse的使用方法

    一.Git安装与简单使用 1.安装git,tortoiseGit 2.创建SSH Key 打开Git Bash,然后输入ssh-keygen生成公钥和私钥: 3.生成ppk文件. 打开puttykey ...

  4. jQuery全选、全不选、反选的简洁写法【实例】

    全选方面的功能几乎是每个需要列表展示的网站所必不可少的,当然此功能也有很多种写法,现在介绍一下,比较简洁易懂的写法: <input type="checkbox" name= ...

  5. Red Hat 9.0 Linux 分辨率修改

    Red Hat 9.0 Linux 分辨率修改 我是在VMware Workstation中装了一个红帽的Linux系统,装上之后发现分辨率有点低,是800x600的,看着很不舒服,然后就想着怎么样可 ...

  6. vue指令v-bind示例解析

    1.绑定一个属性 <img id="app" v-bind:src="imageSrc"> <script> var app = Vue ...

  7. Jenkins: 配置信息变更历史

    像 Jenkins 这样的系统,使用的过程就是配置文件变更的过程.如果能够对配置文件的变更进行跟踪管理,将极大的提高系统的可用性.Job Configuration History 插件就是这么一款实 ...

  8. AKOJ -- 1529 -- 寻找最大数

    1529: 寻找最大数 Time Limit: 1 Sec  Memory Limit: 128 MB Submit: 107  Solved: 53 上一题SubmitStatus标签打分编辑题目信 ...

  9. iOS获取用户设备崩溃日志并分析

    项目最近发布,部分用户在内侧使用,正好遇到一些问题,由于用户在其他城市,所以对于用户设备产生的崩溃日志,不好直接拿设备连接电脑. 对于这种情况,我们可以这样: 1.引导用户开启iOS设备设置-> ...

  10. JS之this

    作为JS中比较容易让人迷糊,同时又很重要的难点. 关于this指向问题,我们始终要记住一句话,这句话对于理解this指向很重要.这句话是:this要在执行时才能确认它的值,定义时无法确认. this的 ...