java中线程同步的几种方法

1.同步关键字

   Synchronized

2.并发包中锁

   Lock

3.object对象等待通知

   ObjectMonitor

   wait

   notify


4.锁对应的条件变量

   并发包中锁的条件变量

   condition

   await

   signal


5.并发包中的阻塞队列

   BlockingQueue
6.并发包中的原子操作

   Atomic

7.volatile


8.final

 

9.Thread.join


10.CountDownLatch

    

相当于线程中的方法join方法,不过功能更多。CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。
计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成了任务,然后在CountDownLatch上等待的线程就可以恢复执行任务。

 


11.CyclicBarrier

CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,
所有被屏障拦截的线程才会继续干活。CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。

 


12.Semaphore

控制并发线程数的semaphore, Semaphore翻译成字面意思为 信号量,Semaphore可以控同时访问的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。
如下所示代码中虽然有30个线程在执行,但是只允许10个并发执行,Semaphore(int permits) 接受一个整形数字,表示可用的许可证数量,Semaphore(10)表示允许10个线程获取许可证,也就是并发数为10.

 


13.Phaser

Phaser类的特点是把多个线程协作执行的任务划分成多个阶段(phase),在每个阶段上都可以有任意个参与者参与。线程可以随时注册并参与到某个阶段的执行中来。当一个阶段中所有的线程都成功完成之后,Phaser类的对象会自动进入下一个阶段,如此循环下去,直到Phaser类的对象中不再包含任何参与者,此时它会自动结束。功能强大,可以替代CountDownLatch和CyclicBarrier。

Phaser的构造器可指定初始的参与者的个数。

(1)register
动态添加参与者

(2)arriveAndAwaitAdvance
完成之后等待其他参与者完成,会阻塞直到Phaser类的对象成功进入下一个阶段

(3)arriveAndDeregister
执行完成之后取消自己的注册,不参与下一个阶段的执行

 

示例代码如下:

public class PhaserDemo {
 
    private final Phaser phaser = new Phaser(1);
    private final Pattern imageUrlPattern = Pattern.compile("src=['\"]?(.*?(\\.jpg|\\.gif|\\.png))['\"]?[\\s>]+", Pattern.CASE_INSENSITIVE);
    public void download(URL url, final Path path, Charset charset) throws IOException {
        if (charset == null) {
            charset = StandardCharsets.UTF_8;
        }
        String content = getContent(url, charset);
        List<URL> imageUrls = extractImageUrls(content);
        for (final URL imageUrl : imageUrls) {
            phaser.register();
            new Thread() {
                public void run() {
                    //等待其他线程创建完成
                    phaser.arriveAndAwaitAdvance();
                    //进入图片下载阶段
                    try {
                        InputStream is = imageUrl.openStream();
                        Files.copy(is, getSavedPath(path, imageUrl), StandardCopyOption.REPLACE_EXISTING);
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        phaser.arriveAndDeregister();
                    }
                }
            }.start();
        }
        //等待其他下载线程创建完成
        phaser.arriveAndAwaitAdvance();
        //等待下载阶段的下载线程执行完成
        phaser.arriveAndAwaitAdvance();
        //下载完成之后注销自己
        phaser.arriveAndDeregister();
    }
    private String getContent(URL url, Charset charset) throws IOException {
        InputStream is = url.openStream();
        return IOUtils.toString(new InputStreamReader(is, charset.name()));
    }
    private List<URL> extractImageUrls(String content) {
        List<URL> result = new ArrayList<URL>();
        Matcher matcher = imageUrlPattern.matcher(content);
        while (matcher.find()) {
            try {
                result.add(new URL(matcher.group(1)));
            } catch (MalformedURLException e) {
                //忽略
            }
        }
        return result;
    }
    private Path getSavedPath(Path parent, URL url) {
        String urlString = url.toString();
        int index = urlString.lastIndexOf("/");
        String fileName = urlString.substring(index + 1);
        return parent.resolve(fileName);
    }
 
    public static void main(String[] args) throws IOException {
        URL url = new URL("http://www.baidu.com");
        PhaserDemo downloader = new PhaserDemo();
        downloader.download(url, Paths.get("imgs"), Charset.forName("GB2312"));
    }
 
}
View Code

 


14.fork-join

fork/join是java7更新的一个新的轻量级任务执行框架,其主要目的是要更好滴利用底层平台上的多核CPU和多处理器来进行并行处理,解决问题时通常采用分治(divide and conquer)算法或map/reduce算法来进行。

fork操作是把一个大问题划分为若干较小的问题,一般是递归进行。

join操作是把部分解收集并组织起来,得到最终的完整解,也可能是递归进行的。

如果某个子问题由于等待另外一个子问题的完成而无法继续运行,那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行,减少了线程等待时间,提高了性能。

ForkJoinTask的子类:RecursiveTask(可返回结果)与RecursiveAction。

ForkJoinPool实现了ExecutorService接口。

示例代码如下:

public class ForkJoinDemo {
 
    private static final int RANGE_LENGTH = 2000;
    private final ForkJoinPool forkJoinPool = new ForkJoinPool();
 
    private static class MaxValueTask extends RecursiveTask<Long> {
        private final long[] array;
        private final int start;
        private final int end;
 
        MaxValueTask(long[] array, int start, int end) {
            this.array = array;
            this.start = start;
            this.end = end;
        }
 
        protected Long compute() {
            long max = Long.MIN_VALUE;
            if (end - start <= RANGE_LENGTH) {
                for (int i = start; i < end; i++) {
                    if (array[i] > max) {
                        max = array[i];
                    }
                }
            } else {
                int mid = (start + end) / 2;
                MaxValueTask lowTask = new MaxValueTask(array, start, mid);
                MaxValueTask highTask = new MaxValueTask(array, mid, end);
                lowTask.fork();
                highTask.fork();
                max = Math.max(max, lowTask.join());
                max = Math.max(max, highTask.join());
            }
            return max;
        }
    }
 
    public void calculate(long[] array) {
        MaxValueTask task = new MaxValueTask(array, 0, array.length);
        Long result = forkJoinPool.invoke(task);
        System.out.println(result);
    }
 
    public void calculateNormal(long[] array) {
        long max = Long.MIN_VALUE;
        for (int i = 0, n = array.length; i < n; i++) {
            if (array[i] > max) {
                max = array[i];
            }
        }
        System.out.println(max);
    }
 
    public static void main(String[] args) {
        Random random = new Random();
        int size = Integer.MAX_VALUE / 256;
        long[] array = new long[size];
        for (int i = 0; i < size; i++) {
            array[i] = random.nextLong();
        }
        ForkJoinDemo mv = new ForkJoinDemo();
        long startTime = System.currentTimeMillis();
        mv.calculate(array);
        long midTime = System.currentTimeMillis();
        System.out.println(midTime - startTime);
        mv.calculateNormal(array);
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - midTime);
    }
 
}
View Code

 


15.parrelStream

 

Java8并行流ParallelStream和Stream的区别就是支持并行执行,提高程序运行效率。但是如果使用不当可能会发生线程安全的问题。Demo如下:
public static void concurrentFun() {
        List<Integer> listOfIntegers =
                new ArrayList<>();
        for (int i = 0; i <100; i++) {
            listOfIntegers.add(i);
        }
        List<Integer> parallelStorage = new ArrayList<>() ;
        listOfIntegers
                .parallelStream()
                .filter(i->i%2==0)
                .forEach(i->parallelStorage.add(i));
        System.out.println();

        parallelStorage
                .stream()
                .forEachOrdered(e -> System.out.print(e + " "));

        System.out.println();
        System.out.println("Sleep 5 sec");
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        parallelStorage
                .stream()
                .forEachOrdered(e -> System.out.print(e + " "));
    }
View Code

 

posted @ 2022-03-09 11:30  高压锅里的大萝卜  阅读(819)  评论(0编辑  收藏  举报