Android小知识-Java多线程相关(Lock使用)

1,166 阅读3分钟

本篇文章已授权微信公众号 顾林海 独家发布

在JDK1.5中新增ReentrantLock类,效果类似于使用synchronized关键字实现线程间同步互斥,并且在扩展功能上也更加强大,比如具有嗅探锁定、多路分支通知等功能。看下面ReentrantLock的实例代码:

public class Task {

    private Lock mLock=new ReentrantLock();

    public void runTask(){
        mLock.lock();
        for(int i=0;i<3;i++){
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
        mLock.unlock();
    }

}

利用ReentrantLock对象的lock()方法获取锁,通过unlock()方法释放锁。

public class ThreadFirst extends Thread {

    private Task mTask;

    public ThreadFirst(Task task){
        this.mTask=task;
    }

    @Override
    public void run() {
        super.run();
        mTask.runTask();
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        Task task=new Task();
        ThreadFirst threadFirst;
        for (int i = 0; i < 3; i++) {
            threadFirst=new ThreadFirst(task);
            threadFirst.start();
        }
    }
}

打印:

Thread-1-->0
Thread-1-->1
Thread-1-->2
Thread-2-->0
Thread-2-->1
Thread-2-->2
Thread-0-->0
Thread-0-->1
Thread-0-->2

从打印结果看,打印是按分组来打印的,当前线程打印完毕后释放锁,其他线程才可以继续打印,

在前面文章中可以通过synchronized与wait()和notify()/notifyAll()方法相结合实现等待/通知模式,利用ReentrantLock也可以实现同样的效果,但需要借助于Condition对象。Condition可以实现多路通知功能,可以在一个Lock对象里面创建多个Condition实例,线程对象可以注册在指定的Condition中,从而可以有选择性进行线程通知。

代码示例:

public class Task {

    private Lock mLock=new ReentrantLock();
    private Condition mCondition=mLock.newCondition();

    public void runTask(){
        try {
            mLock.lock();
            System.out.println("打印一些信息:A");
            mCondition.await();
            System.out.println("打印剩余信息:B");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            mLock.unlock();
            System.out.println("释放锁");
        }

    }

}

线程类:

public class ThreadFirst extends Thread {

    private Task mTask;

    public ThreadFirst(Task task){
        this.mTask=task;
    }

    @Override
    public void run() {
        super.run();
        mTask.runTask();
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        Task task=new Task();
        ThreadFirst threadFirst=new ThreadFirst(task);
        threadFirst.start();
    }
}

打印:

打印一些信息:A

从打印结果看只打印了A,后面B没打印出来,这是因为调用了Condition对象的await()方法,使当前执行任务的线程进入等待WAITING状态。

补齐上面代码,在Task类中新添加一个notifyTask方法:

    public void notifyTask(){
        try {
            mLock.lock();
            System.out.println("通知task继续执行");
            mCondition.signal();
        }finally {
            mLock.unlock();
        }
    }

Client修改如下:

public class Client {
    public static void main(String[] args) {
        Task task=new Task();
        ThreadFirst threadFirst=new ThreadFirst(task);
        threadFirst.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        task.notifyTask();
    }
}

打印:

打印一些信息:A
通知task继续执行
打印剩余信息:B
释放锁

到这里可以发现Object类的wait()和wait(long)方法相当于Condition类中的await()和await(long)方法。notify()和notifyAll相当于Condition类中的signal()和signalAll()方法。

当然我们也可以创建多个Condition对象来实现通知部分线程,代码如下:

public class Task {

    private Lock mLock = new ReentrantLock();
    private Condition mConditionA = mLock.newCondition();
    private Condition mConditionB = mLock.newCondition();

    public void runTaskA() {
        try {
            mLock.lock();
            System.out.println("打印一些信息:A");
            mConditionA.await();
            System.out.println("打印剩余信息:A");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            mLock.unlock();
            System.out.println("释放锁A");
        }

    }

    public void runTaskB() {
        try {
            mLock.lock();
            System.out.println("打印一些信息:B");
            mConditionB.await();
            System.out.println("打印剩余信息:B");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            mLock.unlock();
            System.out.println("释放锁B");
        }

    }

    public void notifyTaskA() {
        try {
            mLock.lock();
            System.out.println("通知A继续执行");
            mConditionA.signal();
        } finally {
            mLock.unlock();
        }
    }

    public void notifyTaskB() {
        try {
            mLock.lock();
            System.out.println("通知B继续执行");
            mConditionB.signal();
        } finally {
            mLock.unlock();
        }
    }
}

线程类1:

public class ThreadFirst extends Thread {

    private Task mTask;

    public ThreadFirst(Task task){
        this.mTask=task;
    }

    @Override
    public void run() {
        super.run();
        mTask.runTaskA();
    }
}

线程类2:

public class ThreadSecond extends Thread {

    private Task mTask;

    public ThreadSecond(Task task){
        this.mTask=task;
    }

    @Override
    public void run() {
        super.run();
        mTask.runTaskB();
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        Task task=new Task();
        ThreadFirst threadFirst=new ThreadFirst(task);
        ThreadSecond threadSecond=new ThreadSecond(task);
        threadFirst.start();
        threadSecond.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        task.notifyTaskA();
    }
}

打印:

打印一些信息:A
打印一些信息:B
通知A继续执行
打印剩余信息:A
释放锁A

从打印结果可以看出,使用ReentrantLock对象可以唤醒指定种类的线程,非常方便。

锁Lock分为“公平锁”和“非公平锁”,公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先到先得的FIFO先进先出顺序,而非公平锁就是一种获取锁的抢占机制,是随机获得的,和公平锁不一样的就是先来的不一定先得到锁,这样的话可能造成某些线程一直拿不到锁。


838794-506ddad529df4cd4.webp.jpg