在java中创建自定义数据结构线程安全?

时间:2017-10-13 11:33:58

标签: java multithreading

在进行多线程处理时,我一直在研究线程安全性。我正在研究使用锁来使自定义数据结构线程安全。

这是使这个自定义直方图线程安全的最合适的实现吗?

我也是新来的。如果我想帮助跟踪代码以找出它的作用,是否有可以使用的标签?

直方图类(不安全)

public class Histogram
{
    protected long[] bins;
    protected int min, max, range;
    protected int numBins;

    public Histogram(int max, int min, int numBins)
    {
        this.max = max;
        this.min = min;
        this.numBins = numBins;
        bins = new long[numBins];
        range = max - min + 1;
    }

    public void add(int num)
    {
        int bin = (int) Math.floor(((num - min) * 1.0 / range) * numBins);
        bins[bin]++;
    }

    public int absDifference(Histogram histogram)
    {
        int sum = 0;
        if (histogram.min == min && histogram.max == max && histogram.numBins == numBins)
            for (int i = 0; i < bins.length; i++)
                sum += (int) Math.abs(bins[i] - histogram.bins[i]);

        return sum;
    }

    @Override
    public String toString()
    {
        String out = String.format("{Min: %d, Max: %d, # Bins: %d, Values: ", min, max, numBins);
        for (int i = 0; i < bins.length; i++)
            out += bins[i] + ", ";

        out = out.substring(0, out.length() - 2);
        out += "}";
        return out;
    }
}

线程安全直方图类

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


public class HistogramSafe extends Histogram
{
    private Lock[] binLocks;

    public HistogramSafe(int max, int min, int numBins)
    {
        super(max, min, numBins);

        binLocks = new ReentrantLock[numBins];
        for (int i = 0; i < numBins; i++)
            binLocks[i] = new ReentrantLock();
    }

    @Override
    public void add(int num)
    {
        int bin = (int) Math.floor(((num - min) * 1.0 / range) * numBins);

        binLocks[bin].lock();
        bins[bin]++;
        binLocks[bin].unlock();
    }
}

4 个答案:

答案 0 :(得分:1)

这取决于。如果您的numBins(也minmax)变量无法更改,即您的数据结构为固定大小,则它应该是线程安全的,允许并行修改不同的bin。

但如果numBins(或minmax)可修改,那么它就不再是线程安全的了。正如您之前访问的numBins那样,它也是一个共享资源,并且不在同一个锁中。

线程可能会进入方法,读取numBins然后休眠(由于线程调度程序)。现在另一个线程来执行完整方法。旧帖子会继续并设置bins[bin]++,但numBins过期值

例如,如果您还提供remove函数,那么这甚至可能导致IndexOutOfBoundException,因为第一个线程可能会读取10的大小,然后其他线程会减少大小为5。当第一个帖子继续时,它可能会尝试写入无效索引

答案 1 :(得分:1)

为了确保方法是线程安全的,synchronized关键字可以提供帮助。此外,任何不可变的数据结构都是主要是线程安全的。

public synchronized void methodName(){}

Zabuza所说,这将阻止所有尝试调用该方法的线程
另一种确保线程安全的方法是制作一个同步块,它将把你想要锁定的对象作为参数

public void methodName(){
  synchronized(object) {
     ...
  }
}

答案 2 :(得分:1)

您应该调查您的bin成员使用AtomicInteger。在您的示例中,线程安全问题是由于整数(读取,添加,写入)的增量。 AtomicInteger操作是线程安全的,速度更快。

同步和锁定更适合保护复杂的数据结构。

答案 3 :(得分:0)

我将从不同的角度来看待这个问题 - 不,我认为它不是线程安全的。为什么?因为你允许继承。当有人扩展这个类时,很多事情都会出错 - 主要是覆盖add方法。