多线程常用类

1. Callable

使用方法:

public class CallableDemo {
    public static void main(String[] args) {
        CallableTest callableTest = new CallableTest();
        FutureTask<Integer> futureTask  = new FutureTask(callableTest);
        new Thread(futureTask,"t1").start();
        try {
            System.out.println(futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}


class CallableTest implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        Integer num = 0;
        for (int i = 0; i < 10; i++) {
            num++;
        }
        return num;
    }
}

优点:

  • 可以获取通过get方法获取线程的返回值,注意get方法阻塞线程。
  • 支持异常捕获
  • 支持泛型

2. CountDownLatch

 

CountDownLatch(倒计时计算器)使用说明

方法说明

public void countDown()

  递减锁存器的计数,如果计数到达零,则释放所有等待的线程。如果当前计数大于零,则将计数减少.

public boolean await(long timeout,TimeUnit unit) throws InterruptedException

  使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。如果当前计数为零,则此方法立刻返回true值。

  如果当前计数大于零,则出于线程调度目的,将禁用当前线程,且在发生以下三种情况之一前,该线程将一直出于休眠状态:

  由于调用countDown()方法,计数到达零;或者其他某个线程中断当前线程;或者已超出指定的等待时间。

  • 如果计数到达零,则该方法返回true值。
  • 如果当前线程,在进入此方法时已经设置了该线程的中断状态;或者在等待时被中断,则抛出InterruptedException,并且清除当前线程的已中断状态。
  • 如果超出了指定的等待时间,则返回值为false。如果该时间小于等于零,则该方法根本不会等待。

参数:

  timeout-要等待的最长时间

  unit-timeout 参数的时间单位

返回:

  如果计数到达零,则返回true;如果在计数到达零之前超过了等待时间,则返回false

抛出:

  InterruptedException-如果当前线程在等待时被中断

例子:线程a需要等待线程b和c完成在执行

public class CountDownLatchDemo {
    public static void main(String[] args) {
        CDLDPrint cdldPrint = new CDLDPrint();
       final CountDownLatch latch = new CountDownLatch(2);  //需要等待b,c线程完成,所以为2
        new Thread(()->{
            try {
                latch.await();    //等待bc线程执行完成
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            cdldPrint.printA();
        },"线程A").start();


        new Thread(()->{
            cdldPrint.printB();
            latch.countDown();  //计数器减一
        },"线程B").start();
        new Thread(()->{
            cdldPrint.printC();
            latch.countDown();   //计数器减一
        },"线程C").start();
    }
}

class CDLDPrint{

    public void printA(){
        System.out.println(Thread.currentThread().getName()+"aaaaaaaaaaaaaaaaa");
    }
    public void printB(){
        System.out.println(Thread.currentThread().getName()+"bbbbbbbbbbbbbbbb");
    }
    public void printC(){
        System.out.println(Thread.currentThread().getName()+"ccccccccccccccccc");
    }

}

3. CyclicBarrier

就像生活中我们会约朋友们到某个餐厅一起吃饭,有些朋友可能会早到,有些朋友可能会晚到,但是这个餐厅规定必须等到所有人到齐之后才会让我们进去。这里的朋友们就是各个线程,餐厅就是 CyclicBarrier。类似于累加器,线程运行达到多少时,开始执行。

public int await()
          throws InterruptedException,
                 BrokenBarrierException

等待所有parties已经在这个障碍上调用了await

如果当前线程不是最后一个线程,那么它被禁用以进行线程调度,并且处于休眠状态,直到发生下列事情之一:

  • 最后一个线程到达; 要么
  • 一些其他线程当前线程为interrupts ; 要么
  • 一些其他线程interrupts其他等待线程之一; 要么
  • 一些其他线程在等待屏障时超时; 要么
  • 其他一些线程在这个屏障上调用reset()

如果当前线程:

  • 在进入该方法时设置了中断状态; 要么
  • 是等待interrupted

然后InterruptedException被关上,当前线程的中断状态被清除。

如果屏蔽是reset() ,而任何线程正在等待,或者当await被调用时屏障is broken ,或者任何线程等待,则抛出BrokenBarrierException

如果任何线程在等待的时候是interrupted ,那么所有其他等待的线程将会丢失BrokenBarrierException ,并且屏障被置于断开的状态。

如果当前线程是要到达的最后一个线程,并且在构造函数中提供非空障碍操作,则当前线程在允许其他线程继续之前运行该动作。 如果在屏障动作期间发生异常,则该异常将在当前线程中传播,并且屏障置于断开状态。

结果

当前线程的到达索引,其中索引 getParties() - 1表示第一个到达,零表示最后到达

异常

InterruptedException - 如果当前线程在等待时中断

BrokenBarrierException - 如果当前线程正在等待,或者屏蔽被重置,或者当 await时屏障被破坏,或者屏蔽动作(如果存在)由于异常而失败,则 另一个线程被中断或超时

例子:实现abc三个线程同时执行。

public class CyclicBarrierDemo {
    public static void main(String[] args) {

        CBDPrint cbdPrint = new CBDPrint();
        final CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
        new Thread(()->{
            try {
                cyclicBarrier.await();
                cbdPrint.printA();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        },"线程A").start();
        new Thread(()->{
            try {
                cyclicBarrier.await();
                cbdPrint.printB();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        },"线程B").start();
        new Thread(()->{
            try {
                cyclicBarrier.await();
                cbdPrint.printB();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        },"线程C").start();
    }
}

class CBDPrint{
    public void printA(){
        System.out.println(Thread.currentThread().getName()+"aaaaaaaaaaaaaaaaa");
    }
    public void printB(){
        System.out.println(Thread.currentThread().getName()+"bbbbbbbbbbbbbbbb");
    }
    public void printC(){
        System.out.println(Thread.currentThread().getName()+"ccccccccccccccccc");
    }
}

 4. Semaphore

信号量,类似于操作系统的信号量。

  • public Semaphore(int permits)

创建一个 Semaphore与给定数量的许可证和非公平公平设置。

参数

permits - permits的初始许可证。 该值可能为负数,在这种情况下,必须在任何获取被授予之前发布释放。

  • public void acquire()
                 throws InterruptedException

从此信号量获取许可证,阻止直到可用,否则线程为interrupted

获得许可证,如果有可用并立即返回,则将可用许可证数量减少一个。

如果没有可用的许可证,那么当前线程将被禁用以进行线程调度,并且处于休眠状态,直至发生两件事情之一:

如果当前线程:

  • 在进入该方法时设置了中断状态; 要么
  • interrupted等候许可证,

然后InterruptedException被关上,当前线程的中断状态被清除。

异常

InterruptedException - 当前线程是否中断

 

  • public void release(int permits)

释放给定数量的许可证,将其返回到信号量。

释放给定数量的许可证,增加可用许可证的数量。 如果任何线程尝试获取许可证,则选择一个线程并给出刚刚发布的许可证。 如果可用许可证的数量满足该线程的请求,则该线程(线程)被重新启用以进行线程调度; 否则线程将等待,直到有足够的许可证可用。 如果在该线程的请求已经满足之后还有可用的许可证,那么这些许可证依次被分配给其他尝试获取许可证的线程。

没有要求释放许可证的线程必须通过调用acquire获取该许可证。 信号量的正确使用通过应用程序中的编程惯例来确定。

参数

permits - 发放许可证的数量

异常

IllegalArgumentException - 如果 permits是否定的

 例子:利用信号量实现限流,接受5个线程,最大处理线程3个线程

 public static void main(String[] args) {
        final Semaphore semaphore = new Semaphore(3); //假设产品为2个
        //创建三个生产者
         //int temp = 0;
        for (int i = 1; i <= 5; i++) {
          //  temp = i;
            new Thread(()->{
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"开始执行了");
                    semaphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            },"生产者"+String.valueOf(i)).start();
        }
    }

5. ReentrantReadWriteLock

         ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
        reentrantReadWriteLock.readLock().lock();  //读锁(共享锁)
        reentrantReadWriteLock.readLock().unlock();
        
        reentrantReadWriteLock.writeLock().lock();//写锁(独占锁)
        reentrantReadWriteLock.writeLock().lock();
  •  读写锁对于读-读操作是共享的,对与读-写和写-写操作是互斥的。
  • 读锁不支持条件变量,写锁支持条件变量
  • 读锁内不能在获取写锁,但是写锁内可以获取读锁
  •  

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值