Java多线程学习篇(三)Lock

Lock 是Java多线程的一个同步机制,用来控制线程对共享资源的访问。线程在执行同步方法或者代码块之前必须先获得一个锁。

Lock 的 lock() 和 unlock() 方法;

  lock():获得一个锁,如果锁不可用,则当前线程将因线程调度目的而被禁用,并在获得锁之前处于休眠状态。

  unlock():释放掉获得的锁。

Lock的作用范围:

  1. 若 Lock 是静态的,则作用范围是整个类。

    public class Test {
        public static void main(String[] args) {
            Thread thread_one = new Thread(new Account(), "Thread_ONE");
            Thread thread_two = new Thread(new Account(), "Thread_Tow");
            thread_one.start();
            thread_two.start();
        }
    }
    class Account implements Runnable{
        static Lock lock = new ReentrantLock();  // 静态的Lock
        static int Count = 0;
        @Override
        public void run(){
            runTest();
        }
        public void runTest() {
            lock.lock();
            try{
                for(int i = 0; i < 5; ++i)
                {
                    Count++;
                    System.out.println(Thread.currentThread().getName() + " " + Count);
                }
            }
            finally {
                lock.unlock();
            }
        }
    }
    //输出:
    //Thread_ONE 1
    //Thread_ONE 2
    //Thread_ONE 3
    //Thread_ONE 4
    //Thread_ONE 5
    //Thread_Tow 6
    //Thread_Tow 7
    //Thread_Tow 8
    //Thread_Tow 9
    //Thread_Tow 10

    静态的Lock

  2. 若 Lock 是非静态的,则范围是整个对象。

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Test1 {
        public static void main(String[] args) {
            Thread thread_one = new Thread(new Account(), "Thread_ONE");
            Thread thread_two = new Thread(new Account(), "Thread_Tow");
            thread_one.start();
            thread_two.start();
        }
    }
    class Account implements Runnable{
        Lock lock = new ReentrantLock();  // 非静态的Lock
        static int Count = 0;
        @Override
        public void run(){
            runTest();
        }
        public void runTest() {
            lock.lock();
            try{
                for(int i = 0; i < 5; ++i)
                {
                    Count++;
                    System.out.println(Thread.currentThread().getName() + " " + Count);
                }
            }
            finally {
                lock.unlock();
            }
        }
    }
    //输出:
    //Thread_Tow 2
    //Thread_ONE 2
    //Thread_Tow 3
    //Thread_ONE 4
    //Thread_Tow 5
    //Thread_ONE 6
    //Thread_Tow 7
    //Thread_ONE 8
    //Thread_Tow 9
    //Thread_ONE 10

    非静态的Lock

Lock的一个简单的实现

public class Lock{

    private boolean isLocked = false;

    public synchronized void lock()throws InterruptedException{
        while(isLocked){ // 使用whlie而不是if
            wait(); // 终止线程并且释放对象的锁,当线程被通知后重新启动,锁就会自动被重新获取
        }
        isLocked = true;
    }
    public synchronized void unlock(){
        isLocked = false;
        notify();//通知一个等待的线程重新获取锁并且恢复执行
    }
}

这里的方法lock(),若当前的锁被其它线程获取而锁住,这调用wait()方法,让线程等待,直到其他线程调用unlock()中的notify()方法而重新启动。这里用的是while而不是if,防止wait()退出后锁还是被其它线程锁住,(比如锁又被其它线程抢走),(lock() 和 notify()、notifyAll() 是Object对象中的方法)

ReentrantLock 是 Lock接口 的一种实现。
public class ReentrantLock implements Lock, java.io.Serializable{ }
ReentrantLock 具有可重入性。什么是可可重入性呢?先举个例子。
public class Reentrant2{

    Lock lock = new Lock(); // 这里的Lock用的是上面写的 public class Lock { }

    public outer(){
        lock.lock();
        inner();
        lock.unlock();
    }

    public synchronized inner(){
        lock.lock();
        //do something
        lock.unlock();
    }
}
假设某个线程调用了outer()方法,这时,该线程调用了lock(),获取了锁,然后再调用inner(),但是inner()内再次调用 lock() 获取锁,可是这锁已经被第一次调用而锁上,所以会进入while调用wait()从而导致死锁。可重入性可以让线程调用自身持有的锁(如果该锁是可重入的)。

改写一下Lock的代码
public class Lock{

    boolean isLocked = false;
    Thread  lockedBy = null;
    int     lockedCount = 0;

    public synchronized void lock()
            throws InterruptedException{
        Thread callingThread = Thread.currentThread();
        while(isLocked && lockedBy != callingThread){  // 若锁被不是自己持有的锁锁住
            wait();
        }
        isLocked = true;
        lockedCount++;
        lockedBy = callingThread;
    }

    public synchronized void unlock(){
        if(Thread.curentThread() == this.lockedBy){
            lockedCount--;

            if(lockedCount == 0){
                isLocked = false;
                notify();
            }
        }
    }
  ...
}
while 循环中加了一个判断 lockedBy != callingThread,这样就不会被自己的锁锁住,并且用 lockedCount 计数一下,只有当 lockedCount 为 0 时,才会调用notify()方法。

最后说一点,使用锁的时候最好使用try,以防一些想不到的问题导致锁没有释放。
lock.lock();
try{
  //do critical section code, which may throw exception
} finally {
  lock.unlock();
}
参考:http://tutorials.jenkov.com/java-concurrency/locks.html


原文地址:https://www.cnblogs.com/lkcc/p/8254105.html

时间: 2024-10-28 10:56:01

Java多线程学习篇(三)Lock的相关文章

java多线程学习(三)——线程栈

一.线程栈模型 线程栈模型是理解线程调度原理以及线程执行过程的基础.线程栈是指某时刻时内存中线程调度的栈信息,当前调用的方法总是位于栈顶,线程栈的内容是随着线程的运行状态变化而变化的,研究线程栈必须选择一个运行的时刻(指代码运行到什么地方) 上图中的栈A是主线程main的运行栈信息,当执行new JavaThreadDemo().threadMethod();方法时,threadMethod方法位于主线程栈中的栈顶,在threadMethod方法中运行的start()方法新建立了一个线程,此时,

Java多线程学习篇(一)

多线程的目的:更高效的利用CPU 创建任务和线程 一个任务类必须实现Runnable接口,任务必须从线程运行. 实现Runnable接口 // 任务类 public class TaskClass implements Runnable { public TaskClass(){ } @Override // 实现Runnable中的run方法 public void run() { // 将要运行的任务 } } 创建并运行任务 public class Client { public void

Java多线程学习篇(二)synchronized

synchronized 有二种修饰方法: 修饰一个方法 synchronized public void runTest{ /**/ } 修饰一个代码块 public void runTest{ synchronized( /*某一对象或某一类*/ ){ /* 代码块 */ } } synchronized 的作用范围分为修饰一个类和修饰一共对象当修饰一个对象时,不同线程的同一对象调用相同代码会发生堵塞当修饰一个类时,不同线程的同一类调用相同代码会发生堵塞修饰静态方法相当于修饰类 定义一个类(

JAVA Web学习篇--Servlet

Servlet由来 做过BS项目的人都知道,浏览器可以依据HTML静态标记语言来显示各式各样的网页.可是假设我们须要在网页上完毕一些业务逻辑:比方登陆验证.或者说网页显示的内容在server的数据库中.假设是这样,除了负责显示的HTML标记之外,必须还要有完毕这些业务功能的代码存在.这样的网页我们就叫做动态网页. 对于静态网页而言,server上存在的是一个个纯HTML文件.当client浏览器发出HTTP请求时,server能够依据请求的URL找到相应的HTML文件,并将HTML代码返回给cl

[转]Java多线程学习(总结很详细!!!)

Java多线程学习(总结很详细!!!) 此文只能说是java多线程的一个入门,其实Java里头线程完全可以写一本书了,但是如果最基本的你都学掌握好,又怎么能更上一个台阶呢? 本文主要讲java中多线程的使用方法.线程同步.线程数据传递.线程状态及相应的线程函数用法.概述等.首先让我们来了解下在操作系统中进程和线程的区别: 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程.(进程是资源分配的最小单位) 线程:同一类线程共享代码和数据空间,

java多线程学习(3)

1)竞争条件 在实际的多线程应用中,通常会有两个或多个线程需要对共同的对象进行共享访问,如果两个线程访问相同的对象,而且每一个都调用了一个会改变对象状态的方法, 那么,线程就会相互倾轧.根据各个线程访问数据的不同顺序,可能会产生腐蚀现象.这种情况通常称为竞争条件. 2)同步 为了多个线程对共享数据的腐蚀,就需要对数据的存取实现同步:常用的同步方法有3种: 1.Reenlock 用Reenlock保护代码块的基本机构如下: 1 Lock myLock=new ReenLock; 2 3 myLoc

java多线程学习(2)

1)Callable和Future Runnable封装一个异步运行的任务:可以当成一个没有任何参数和返回值的异步方法,Callable和 Runnable类似,但是它有返回值和参数. Callable接口是一个参数化的类型,只有一个方法call. 1 public interface Callable<V> 2 3 { 4 5 V call()throws Exception; 6 7 } 类型参数v是指返回值的类型,例如Callable<Integer>代表最终返回一个Inte

java多线程学习--java.util.concurrent

CountDownLatch,api 文档:http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes. 假设我们要打印1-100,最

Java深入学习11:Lock锁详解

Java深入学习11:Lock锁详解 一.Lock锁是什么 java.util.concurrent.locks包下常用的类与接口(lock是jdk 1.5后新增的) Lock 接口支持那些语义不同(重入.公平等)的锁规则,可以在非阻塞式结构的上下文(包括 hand-over-hand 和锁重排算法)中使用这些规则.主要的实现是 ReentrantLock. Lock 实现提供了比 synchronized 关键字 更广泛的锁操作,它能以更优雅的方式处理线程同步问题.也就是说,Lock提供了比s