美文网首页
Java中线程同步(四):ReentrantLock

Java中线程同步(四):ReentrantLock

作者: DON_1007 | 来源:发表于2017-03-08 17:08 被阅读0次

在包java.util.concurrent.locks下面可以看到两个接口Lock、ReadWriteLock,分别对应着两种实现方式ReentrantLock、ReentrantReadWriteLockLock中定义了下面一组方法

  • void lock();
  • void lockInterruptibly() throws InterruptedException;
  • boolean tryLock();
  • boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
  • void unlock();
  • Condition newCondition();

其中Condition newCondition();涉及到Condition,后面单独记录
上面是Lock接口中定义的所有方法,除了Condition之外只有两类,加锁和解锁。其中加锁方法4个

  • lock(),无返回值,获取锁成功代码向下执行,否则阻塞线程直到获取到锁
  • lockInterruptibly(),同上,但是如果在别处调用了当前线程的interrupt()函数终止线程,使用这个方法可以及时得到这个状态,lock()则不会
  • tryLock(),有返回值,立即返回获取锁状态,成功true,失败false
  • tryLock(long time, TimeUnit unit),有返回值,若获取锁成功就直接返回;获取锁失败就阻塞设置的时间,如果设置的时间内获取锁成功就返回true,超时之后返回false

解锁方法1个:unlock(),一次加锁对应着一次解锁,不能少也不能多

1、代码演示:lock()

package com.hpplay.threadcommunicationdemo;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;

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

/**
 * Created by DON on 2017/3/7.
 */
public class ReentrantLockFragment extends Fragment {

    private static final String TAG = "ReentrantLockFragment";
    private Lock lock = new ReentrantLock();
    private ThreadA threadA = new ThreadA();
    private ThreadB threadB = new ThreadB();

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        RelativeLayout relativeLayout = new RelativeLayout(getActivity());
        return relativeLayout;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        //程序入口

        threadA.start();
        threadB.start();

    }

    private class ThreadA extends Thread {
        @Override
        public void run() {
            super.run();

            Log.e(TAG, this.getClass().getSimpleName() + "   before");
            lock.lock();
            Log.e(TAG, this.getClass().getSimpleName() + "   start");
            try {
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            Log.e(TAG, this.getClass().getSimpleName() + "   end");
            lock.unlock();

        }
    }

    private class ThreadB extends Thread {
        @Override
        public void run() {
            super.run();

            Log.e(TAG, this.getClass().getSimpleName() + "   before");
            lock.lock();
            Log.e(TAG, this.getClass().getSimpleName() + "   start");
            try {
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            Log.e(TAG, this.getClass().getSimpleName() + "   end");
            lock.unlock();

        }
    }


}

代码很简单,就不赘述了,分析打印的log

03-08 15:18:27.297 6461-6606/? E/ReentrantLockFragment: ThreadA   before 
//A线程开始执行
03-08 15:18:27.297 6461-6606/? E/ReentrantLockFragment: ThreadA   start  
//A线程加锁成功, A线程的任务开始执行
03-08 15:18:27.297 6461-6607/? E/ReentrantLockFragment: ThreadB   before 
//B线程开始执行,由于A线程加锁成功,B线程阻塞等待
03-08 15:18:29.298 6461-6606/? E/ReentrantLockFragment: ThreadA   end    
//A线程任务执行完毕,解锁
03-08 15:18:29.299 6461-6607/? E/ReentrantLockFragment: ThreadB   start  
//B线程加锁成功,开始执行任务
03-08 15:18:31.301 6461-6607/? E/ReentrantLockFragment: ThreadB   end    
//B线程任务执行完毕,解锁

2、代码演示:lockInterruptibly()

对上述代码做一些修改,将线程B的加锁方式改为lockInterruptibly(),同时在线程A中调用线程B的 interrupt()方法,修改后的代码如下

private class ThreadA extends Thread {
        @Override
        public void run() {
            super.run();

            Log.e(TAG, this.getClass().getSimpleName() + "   before");
            lock.lock();
            Log.e(TAG, this.getClass().getSimpleName() + "   start");
            try {
                Thread.sleep(500);

                threadB.interrupt();//终止B线程

                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            Log.e(TAG, this.getClass().getSimpleName() + "   end");
            lock.unlock();

        }
    }

    private class ThreadB extends Thread {
        @Override
        public void run() {
            super.run();

            Log.e(TAG, this.getClass().getSimpleName() + "   before");
            try {
                lock.lockInterruptibly();
                Log.e(TAG, this.getClass().getSimpleName() + "   start");
                try {
                    Thread.sleep(2000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                Log.e(TAG, this.getClass().getSimpleName() + "   end");
                lock.unlock();
            } catch (InterruptedException e) {
                e.printStackTrace();
                Log.e(TAG, this.getClass().getSimpleName() + "  Interrupted ");
            }

        }
    }

分析log信息

03-08 15:29:02.243 7069-7097/? E/ReentrantLockFragment: ThreadA   before  
//A线程开始执行
03-08 15:29:02.243 7069-7097/? E/ReentrantLockFragment: ThreadA   start  
//A线程加锁成功,任务开始执行
03-08 15:29:02.245 7069-7098/? E/ReentrantLockFragment: ThreadB   before 
//B线程开始执行,加锁失败,阻塞等待
03-08 15:29:02.245 7069-7098/? E/ReentrantLockFragment: ThreadB  Interrupted 
//500ms之后,A线程调用了threadB.interrupt();,B线程收到终止消息
03-08 15:29:04.745 7069-7097/? E/ReentrantLockFragment: ThreadA   end 
//A线程任务执行完毕,解锁

上面的例子中当B线程被终止时,B线程及时的收到了消息。如果这时候B线程不使用lock.lockInterruptibly();方法,会是什么样子呢?
将线程B的代码改回来

  private class ThreadB extends Thread {
        @Override
        public void run() {
            super.run();

            Log.e(TAG, this.getClass().getSimpleName() + "   before");
            lock.lock();
            Log.e(TAG, this.getClass().getSimpleName() + "   start");
            try {
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            Log.e(TAG, this.getClass().getSimpleName() + "   end");
            lock.unlock();

        }
    }

运行程序,分析log信息

03-08 15:35:27.565 7559-7624/? E/ReentrantLockFragment: ThreadA   before  
//A线程开始执行
03-08 15:35:27.565 7559-7624/? E/ReentrantLockFragment: ThreadA   start 
//A线程加锁成功,开始执行任务
03-08 15:35:27.567 7559-7625/? E/ReentrantLockFragment: ThreadB   before 
//B线程开始执行,加锁失败,阻塞
03-08 15:35:30.067 7559-7624/? E/ReentrantLockFragment: ThreadA   end 
//A线程任务执行完毕,解锁。中间调用 threadB.interrupt();,B线程并没有收到消息
03-08 15:35:30.070 7559-7625/? E/ReentrantLockFragment: ThreadB   start 
//B线程加锁成功,开始执行任务
03-08 15:35:30.071 7559-7625/? W/System.err:     at com.hpplay.threadcommunicationdemo.ReentrantLockFragment$ThreadB.run(ReentrantLockFragment.java:75) 
//由于前面已经调用B线程的interrupt()方法,这里调用Thead.sleep(2000),发生异常,线程没有沉睡2秒,发生异常
03-08 15:35:30.072 7559-7625/? E/ReentrantLockFragment: ThreadB   end 
//B线程任务执行完毕,解锁

3、代码演示:tryLock(),能及时返回加锁状态

修改ThreadA和ThreadB代码

private class ThreadA extends Thread {
        @Override
        public void run() {
            super.run();

            Log.e(TAG, this.getClass().getSimpleName() + "   before");
            if(lock.tryLock()) {
                Log.e(TAG, this.getClass().getSimpleName() + "   start");
                try {
                    Thread.sleep(2000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                Log.e(TAG, this.getClass().getSimpleName() + "   end");
                lock.unlock();
            }

        }
    }

    private class ThreadB extends Thread {
        @Override
        public void run() {
            super.run();

            Log.e(TAG, this.getClass().getSimpleName() + "   before");
            if(lock.tryLock()) {
                Log.e(TAG, this.getClass().getSimpleName() + "   start");
                try {
                    Thread.sleep(2000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                Log.e(TAG, this.getClass().getSimpleName() + "   end");
                lock.unlock();
            }

        }
    }

运行,分析log信息

03-08 16:52:23.865 9169-9826/? E/ReentrantLockFragment: ThreadA   before  
//A线程开始执行
03-08 16:52:23.865 9169-9826/? E/ReentrantLockFragment: ThreadA   start  
//A线程加锁成功,开始执行任务
03-08 16:52:23.871 9169-9827/? E/ReentrantLockFragment: ThreadB   before 
//B线程开始执行,加锁失败,返回false
03-08 16:52:25.866 9169-9826/? E/ReentrantLockFragment: ThreadA   end 
//A线程任务执行完毕,解锁

4、代码演示:tryLock(long time, TimeUnit unit)

主要是跟tryLock()相比,上面可以看到tryLock()失败就直接返回false,tryLock(long time, TimeUnit unit)如果获取锁失败,就会阻塞,并且会在设置的时间去获取锁,成功就返回true。
修改ThreadB代码

 private class ThreadB extends Thread {
        @Override
        public void run() {
            super.run();

            Log.e(TAG, this.getClass().getSimpleName() + "   before");
            boolean isLock = false;
            try {
               isLock = lock.tryLock(5, TimeUnit.SECONDS);//如果获取锁成功就向下执行
                // ,如获取失败线程阻塞5秒,若5秒内仍未获取成功,返回false
            }catch (InterruptedException e){//如果在等待期间被别的线程终止线程,就会进入这里
                e.printStackTrace();
                isLock = false;
            }

            if(isLock) {
                Log.e(TAG, this.getClass().getSimpleName() + "   start");
                try {
                    Thread.sleep(2000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                Log.e(TAG, this.getClass().getSimpleName() + "   end");
                lock.unlock();
            }

        }
    }

运行,分析log信息

03-08 17:03:36.413 10722-10761/? E/ReentrantLockFragment: ThreadA   before 
//A线程启动
03-08 17:03:36.413 10722-10761/? E/ReentrantLockFragment: ThreadA   start 
//A线程加锁成功,并开始执行任务
03-08 17:03:36.413 10722-10762/? E/ReentrantLockFragment: ThreadB   before 
//B线程启动,加锁失败,阻塞
03-08 17:03:38.415 10722-10761/? E/ReentrantLockFragment: ThreadA   end  
//2秒之后A线程执行完毕,解锁
03-08 17:03:38.416 10722-10762/? E/ReentrantLockFragment: ThreadB   start 
//B线程加锁成功,开始执行任务
03-08 17:03:40.417 10722-10762/? E/ReentrantLockFragment: ThreadB   end 
//B线程执行任务完毕,解锁

相关文章

网友评论

      本文标题:Java中线程同步(四):ReentrantLock

      本文链接:https://www.haomeiwen.com/subject/aoxmgttx.html