java并行计算--Semaphore信号量的简单使用

多线程不仅适用于IO密集型任务,而且在计算型密集型任务也是同样很有优势,最直观的就是快嘛!先来假设一个任务场景:

假设场景:

有一个计算任务:需要先计算出A,B的结果,通过A,B结果得到C结果,通过A,B,C结果得到结果D,试用多线程完成任务!!

分析

我们知道线程的执行顺序具有不可预测性,那么直接使用4个线程去跑,那么由于执行顺序就会无法预测,结果的正确性就得不到保证,如何控制线程的执行顺序就成为最主要的解决问题。这里我们就需要使用Semaphore这个类库了。我们来看jdk文档的介绍:

一个计数信号量。从概念上讲,信号量维护了一个许可集。
如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。
每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。
但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。
Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目

我们通过这种信号量的阻塞方式来实现线程的执行方式,通过 acquire()阻塞, release() 来释放阻塞。来看一段小程序:

package cacl;

import java.util.concurrent.*;

/**
 * Created by lewis on 2017/3/28.
 *
 *
 */
public class Cacl {

    private static volatile Semaphore a,b,c,d;

    static {
        a = new Semaphore(0);
        b = new Semaphore(0);
        c = new Semaphore(0);
        d = new Semaphore(0);
    }

    public static void main(String []args){
        Thread t1 = new Thread(new A());
        Thread t2 = new Thread(new B());
        Thread t3 = new Thread(new C());
        Thread t4 = new Thread(new D());
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
    public static <T> void print(T s){
        System.out.println(s);
    }

    static class  A implements Runnable{
        @Override
        public void run() {
            print("A");
            a.release();
        }
    }

    static class  B implements Runnable{
        @Override
        public void run() {
           print("B");
           b.release();
        }
    }
    static class  C implements Runnable{
        @Override
        public void run() {
            try {
                a.acquire();
                b.acquire();
                print("C");
                c.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    static class  D implements Runnable{
        @Override
        public void run() {
            try {
                c.acquire();
                print("D");
                d.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

这段程序是输出A,B,C,D字符。程序使用了信号量保证了线程的执行顺序,使得输出A,B是并行,所以输出的顺序是随机的,[A,B],C,D这三个是串行,而且具有顺序的输出。

那么我们就可以用来完成并行计算的任务:

以下程序完成了假设的任务场景。现实生活中的应用嘛~可以刷ACM题的试试,说不定TLE的题就过了,但是据我所知有些OJ(codeforces)会检测多线程的使用,检测到就直接报错了。

package cacl;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * Created by lewis on 2017/3/28.
 *
 *  并行计算
 *
 */
public class Sums {


    /**
     * A:1+2+3+............+10000
     * B:1+sqrt(1)+sqrt(3)+..........sqrt(10000)
     * C:10+.............................+10[SUM(10000*10)] + C + D
     * D:1*1+2*2+..................100*100 + C
     * */
    private static volatile Semaphore a,b,c,d;

    private static volatile int resA,resB,resC,resD;

    static {
        a = new Semaphore(0);
        b = new Semaphore(0);
        c = new Semaphore(0);
        d = new Semaphore(0);
    }

    public static void main(String []args){
        ExecutorService executorService = Executors.newFixedThreadPool(4);
        ArrayList<Callable<Integer>> list = new ArrayList<>();
        list.add(new A());
        list.add(new B());
        list.add(new C());
        list.add(new D());
        List<Future<Integer>> futures = null;
        try {
           futures = executorService.invokeAll(list);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            executorService.shutdown();
        }

        for(Future<Integer>future:futures){
            try {
                System.out.println(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
    public static <T> void print(T s){
        System.out.println(s);
    }

    static class  A implements Callable<Integer>{

        @Override
        public Integer call() throws Exception {

           synchronized (this){
               int res = 0;
               for(int i = 0;i<10000;i++){
                   res += i;
               }
               resA = res;
               a.release();
               return resA;
           }
        }
    }

    static class  B implements  Callable<Integer>{

        @Override
        public Integer call() throws Exception {
            synchronized (this) {
                int res = 0;
                for (int i = 0; i < 10000; i++) {
                    res += Math.sqrt(i);
                }
                resB = res;
                b.release();
                return resB;
            }
        }
    }
    static class  C implements  Callable<Integer>{

        @Override
        public Integer call() throws Exception {
            synchronized (this){
                a.acquire();
                b.acquire();
                int res = 0;
                for(int i = 0;i<100;i++){
                    res += i*i;
                }
                resC = res + resA + resB;
                c.release();
                return resC;
            }
        }
    }
    static class  D implements  Callable<Integer>{

        @Override
        public Integer call() throws Exception {
            synchronized(this){
                c.acquire();
                int res = 0;
                for(int i = 0;i<10000;i++){
                    res += 10;
                }
                resD = res + resC + resA +resB;
                d.release();
                return resD;
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值