首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Java中使用线程的ArrayList<object>操作

Java中使用线程的ArrayList<object>操作
EN

Stack Overflow用户
提问于 2015-08-04 17:03:32
回答 2查看 863关注 0票数 0

我试图在java中使用线程时结合使用list.addlist.remove来解决一个问题。假设我们和Stack一起玩

这是我的Stack定义类。

代码语言:javascript
运行
复制
import java.util.ArrayList;

public class Stack {

    private int size;
    private int maxSize;

    private final ArrayList<Object> list;

    public Stack(int size) {
        this.size = 0;
        this.maxSize = size;
        this.list = new ArrayList<Object>(size);
    }

    public boolean push(Object o) {
        if (size >= maxSize) {
            return false;
        }

        this.list.add(0, o);
        this.size++;
        return true;
    }

    public Object pop() {
        Object o;
        if (this.size == 0) {
            return null;
        }

        o = this.list.remove(0);
        this.size--;
        return o;
    }

    public int size() {
        return this.size;
    }
}

下面是我们如何在Java线程中使用堆栈

代码语言:javascript
运行
复制
final Stack stack = new Stack(4);
for(int i = 0; i < 10000; i++) {
    final String data = "hello " + i;
    final int x = i;
    new Thread(new Runnable() {
        public void run() {
            if(x % 2 == 0) {
                System.out.println(stack.push(data));
            } else {
                System.out.println(stack.pop());
            }
        }
    }).start();
}

基本上,我们只需创建10000个线程来操作Stack对象。stack.push导致True (如果堆栈尚未满)或false (如果堆栈已经满),如果堆栈为空,则stack.pop结果为null

问题是:上面的Stack实现有什么问题,以及如何修复它?

到目前为止,我的分析是线程如何在java中运行。线程以并行方式运行,而不是按顺序运行。我试着执行这个程序,有时会出现异常IndexOutOfBounds。如果我的分析是正确的(或接近),有什么办法可以避免例外吗?也许在Stack类中包括一些检查方法?

如果我的分析是错误的,那么上面的实现有什么问题呢?以及如何修复它?

EN

回答 2

Stack Overflow用户

发布于 2015-08-04 17:07:16

上面的堆栈实现有什么问题?

当只有一个线程可以访问堆栈对象时,您的实现是好的。但是,如果至少有2个线程可以在同一个堆栈上执行pop和push操作-- https://stackoverflow.com/questions/34510/what-is-a-race-condition就会发生。JSR-133描述了java多线程的主要描述。

想象一下使用pop方法编写的代码的情况:

代码语言:javascript
运行
复制
if (this.size == 0) {
    return null;
}
o = this.list.remove(0);
  1. 如果条件为1,则执行第一个线程。
  2. 第二个线程执行相同的if条件-大小仍然是1。
  3. 首先,线程试图从列表中删除索引为0的元素-成功,大小变为0.
  4. 第二,当大小为0时,线程尝试从列表中删除索引为0的元素--异常抛出。

你需要确保有些事情发生在其他事件之前。其中一种方法是同步poppush方法。这可以通过在方法返回类型之前添加synchronized关键字来实现。

代码语言:javascript
运行
复制
public synchronized boolean push(Object o) {...}

public synchronized Object pop() { ...}

这里,这两种方法都是同一个对象的synchronized -- this。因此,当一个线程通过执行thispush获得pop锁时,没有其他线程能够进入由同一个this对象锁定(同步)的代码块或方法。使用这些方法是完全线程安全的。

如果您使用一些同步集合而不是常规的ArrayList,您仍然会遇到麻烦,因为您的逻辑依赖于size变量,而前面的错误场景仍然有效。如果需要并发Stack实现,可以使用java.util.concurrent包中的LinkedBlockingDeque类。它还会更高效,因为向ArrayList的开头添加一个元素的成本非常高。但是,如果您想单独启用它,只需向poppush方法添加同步修饰符,并将列表更改为LinkedList

票数 4
EN

Stack Overflow用户

发布于 2015-08-04 19:15:37

@ka4eli告诉您为什么Stack类不是线程安全的,但这也是错误的:

代码语言:javascript
运行
复制
if(x % 2 == 0) {
    System.out.println(stack.push(data));
} else {
    System.out.println(stack.pop());
}

即使您的堆栈是完全线程安全的,在其他情况下也一定会得到NullPointerExceptions。

不同步线程可以按任何顺序运行。仅仅因为您的程序在启动线程1之前启动线程0,并不意味着线程1在线程0(或任何其他线程)之前不会尝试从堆栈中弹出字符串。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31815217

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档