Java深入学习12:线程按目标顺序执行以及Lock锁和Condiiton接口

Java深入学习12:线程按目标顺序执行以及Lock锁和Condiiton接口

一、一个多线程问题,有三类线程,分别是A、B、C,如如实现ABCABCABCABCABC,顺次执行。

方案1。代码如下

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

public class ThreadSequenceOutputTest {

    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();

        for(int i=1; i<5; i++){
            //A类线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    threadDemo.outPutA();
                }
            },"A").start();
            //C类线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    threadDemo.outPutB();
                }
            },"B").start();
            //C类线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    threadDemo.outPutC();
                }
            },"C").start();

        }
    }
}

class ThreadDemo{

    private int num = 1;//当前线程标记,123分别对应ABC

    private Lock lock = new ReentrantLock();

    private Condition condition1 = lock.newCondition();//condition1控制A类线程
    private Condition condition2 = lock.newCondition();//condition2控制B类线程
    private Condition condition3 = lock.newCondition();//condition3控制C类线程

    //输出A
    public void outPutA(){
        lock.lock();
        try {
            //如果没有轮到A类线程,则继续等待
            if(num != 1){
                condition1.await();
            }
            //打印线程名称
            System.out.println(Thread.currentThread().getName());
            //num标志切换成B线程
            num = 2;
            //唤醒B类线程
            condition2.signal();
        } catch (InterruptedException e){
           e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    //输出B
    public void outPutB(){
        lock.lock();
        try {
            if(num != 2){
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName());
            num = 3;
            condition3.signal();
        } catch (InterruptedException e){
           e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    //输出C
    public void outPutC(){
        lock.lock();
        try {
            if(num != 3){
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName());
            num = 1;
            condition1.signal();
        } catch (InterruptedException e){
           e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}

二、Condiiton接口

1-Condition

   Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition,阻塞队列实际上是使用了Condition来模拟线程间协作。

  Condition是个接口,基本的方法就是await()和signal()方法;

  Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition()

  调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用

    Conditon中的await()对应Object的wait();

    Condition中的signal()对应Object的notify();

    Condition中的signalAll()对应Object的notifyAll()。

2-在Java Condition接口的注释中有一个案例如下

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

/*这是一个简单的生产者消费者模型,生产者往buffer里put,消费者从buffer里take。
1-同一状态的顺序访问: 有三个状态需要顺序访问:buffer的大小count,生产者用于put的游标putptr,消费者用于take的游标takeptr。
2-基于该状态的条件等待: 当count = 0时,消费者的take需要等待;当count = buffer.size(buffer满了),生产者需要等待。
*/
public class BoundedBuffer {

    public static void main(String[] args) {
        BoundedBuffer boundedBuffer = new BoundedBuffer();
        for(int i=1; i<100; i++){
            //生产者
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        boundedBuffer.put((int)(Math.random()*100));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            //消费者
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        boundedBuffer.take();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            }

    }

    final Lock lock = new ReentrantLock();
    final Condition notFull  = lock.newCondition();
    final Condition notEmpty = lock.newCondition();

    final Object[] items = new Object[100];
    int putptr, takeptr, count;

    //生产
    public void put(Object x) throws InterruptedException {
        System.out.println("count " + count);
        //上锁
        lock.lock();
        try {
            //如果count已满,设置“未满”进程等待
            while (count == items.length){
                notFull.await();
            }
            //赋值
            items[putptr] = x;
            System.out.println("put " + x);
            //判断put的游标到了最后一位,则移到首位
            if (++putptr == items.length){
                putptr = 0;
            }
            //计数count+1
            ++count;
            //唤起“非空”继承
            notEmpty.signal();
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    //消费
    public Object take() throws InterruptedException {
        //上锁
        lock.lock();
        try {
            //当缓存为空是,设置“非空”线程等待
            while (count == 0){
                notEmpty.await();
            }
            //获取当前take游标的value
            Object x = items[takeptr];
            System.out.println("take " + x);
            //判断take的游标如果到了数组末端,则更新为首位
            if (++takeptr == items.length) {
                takeptr = 0;
            }
            //计数-1
            --count;
            //唤醒“未满”线程
            notFull.signal();
            return x;
        } finally {
            //释放锁
            lock.unlock();
        }
    }
}

原文地址:https://www.cnblogs.com/wobuchifanqie/p/12541546.html

时间: 2024-08-07 00:17:11

Java深入学习12:线程按目标顺序执行以及Lock锁和Condiiton接口的相关文章

java SE学习之线程同步(详细介绍)

       java程序中可以允许存在多个线程,但在处理多线程问题时,必须注意这样一个问题:               当两个或多个线程同时访问同一个变量,并且一些线程需要修改这个变量时,那么这个程序是该如何执行?              也就是先访问这个变量还是先修改这个变量.              在学习线程的这段时间里,我也一直被这个问题所困扰!但是今天终于算是搞明白了.              于是将这些好的列子一一列举出来,分享一下. (1)什么是线程同步 ?      

java基础学习总结——线程(二)

永不放弃,一切皆有可能!!! 只为成功找方法,不为失败找借口! java基础学习总结——线程(二) 一.线程的优先级别 线程优先级别的使用范例: 1 package cn.galc.test; 2 3 public class TestThread6 { 4 public static void main(String args[]) { 5 MyThread4 t4 = new MyThread4(); 6 MyThread5 t5 = new MyThread5(); 7 Thread t1

java基础学习总结——线程(一)

永不放弃,一切皆有可能!!! 只为成功找方法,不为失败找借口! java基础学习总结——线程(一) 一.线程的基本概念 线程理解:线程是一个程序里面不同的执行路径 每一个分支都叫做一个线程,main()叫做主分支,也叫主线程. 程只是一个静态的概念,机器上的一个.class文件,机器上的一个.exe文件,这个叫做一个 进程.程序的执行过程都是这样的:首先把程序的代码放到内存的代码区里面,代码放到代码区后并没有马上开始执行,但这时候说明了一个进程准备开始,进程已 经产生了,但还没有开始执行,这就是

Java并发学习之五——线程的睡眠和恢复

本文是学习网络上的文章时的总结,感谢大家无私的分享. 1.Thread类的sleep方法,可以使线程睡眠.此方法接收一个整数作为参数,表示线程暂停运行的毫秒数.在调用sleep方法后,当时间结束时,JVM会安排他们CPU时间,线程会继续按指令执行. 另一种可能是使用一个有TimeUnit列举元素的sleep方法,使用线程类的sleep方法让当前线程睡眠,但是它接收的参数单位后会转成毫秒的. 2.当你调用sleep()方法,Thread离开CPU并在一段时间内停止运行.在这段时间内,他是不消耗CP

Java并发学习之三——线程的中断

本文是学习网络上的文章时的总结,感谢大家无私的分享. 1.一个多个线程在执行的Java程序,只有当其全部的线程执行结束时(更具体的说,是所有非守护线程结束或者某个线程调用System.exit()方法的时候),它才会结束运行.有时,你需要为了终止程序而结束一个线程,或者当程序的用户想要取消某个Thread对象正在做的任务. 2.Java提供中断机制来通知线程表明我们想要结束它.中断机制的特性是线程需要检查是否被中断,而且还可以决定是否相应结束的请求.所以,线程可以忽略中断请求并且继续运行. 3.

java基础学习总结-----线程(一)

转:基础学习总结----线程(一)http://www.cnblogs.com/xdp-gacl/p/3633936.htmljava 一.线程的概念 线程:线程是一个程序里面不同的执行路径 进程:一个静态的概念,机器上的一个.class文件,机器上的一个.exe文件,这个叫做一个进程. 程序的执行过程: 1.首先把程序的代码放到内存的代码区里面,代码放到代码区后并没有马上开始执行,但这时候说明了一个进程准备开始,进程已经产生,但还没有开始执行,这就是进程.其实是2.一进程的执行指的是进程里面主

Java多线程学习之线程的取消与中断机制

任务和线程的启动很容易.在大多数情况下我们都会让他们运行直到结束,或是让他们自行停止.但是,有时我们希望提前结束任务或是线程,可能是因为用户请求取消,或是线程在规定时间内没有结束,或是出现了一些问题迫使线程要提前结束. 强制一个线程或是服务立即停止,可能会造成共享数据状态不一致的问题,比如,两个线程正对一个共享数据进行操作,然后被突然杀死,这样会对数据造成不确定性的影响.Java中没有提供任何机制来安全的终止线程,但它提供了中断,这种协作机制,“提醒”线程可以自己结束自己线程.这种机制提供了更好

Java多线程学习之线程的同步

多线程编程要解决的一个基本问题是:共享资源的竞争.而基本上使用并发模式在解决这个问题都采用序列化访问共享资源的方法.基本原理就是当共享资源被一个任务使用时,在其上加锁,其他任务在资源被解锁之前,无法访问它.在任务对其解锁后,另一个任务就可以锁定并使用它.下面看看Java支持的线程同步机制. 1.synchronized关键字 synchronized关键字即可应用于对象相关的同步,也可用于类层次的同步(static属性): 对象上应用synchronized可以实现对象方法的同步和代码块的同步.

java并发编程的艺术——第五章总结(Lock锁与队列同步器)

Lock锁 锁是用来控制多个线程访问共享资源的方式. 一般来说一个锁可以防止多个线程同时访问共享资源(但有些锁可以允许多个线程访问共享资源,如读写锁). 在Lock接口出现前,java使用synchronized关键字实现锁的功能,但是在javaSE5之后,并发包中提供了Lock接口(以及其实现类)用来实现锁的功能. Lock提供了与synchronized相似的功能,但必须显示的获取锁与释放锁,虽然不及隐式操作方便,但是拥有了锁获取与释放的可操作性.可中断的锁获取与超时获取锁等多重功能. 提供