Java 同时运行100000个进程

Java 同时运行100000个进程,java,concurrency,jvm,Java,Concurrency,Jvm,我正在模拟一个银行系统,在这个系统中我有100000笔交易要处理。每种类型的事务都实现runnable,我有各种类型的事务可以发生 事务是一个可运行的数组 理想情况下,以下代码可以解决我的问题: for (Transaction transaction : transactions) { new Thread(transaction).start(); } 但是,很明显,在尝试启动100000个线程时,肯定会出现java.lang.OutOfMemoryError:无法创建新的本机线程

我正在模拟一个银行系统,在这个系统中我有100000笔交易要处理。每种类型的事务都实现runnable,我有各种类型的事务可以发生

事务
是一个可运行的数组

理想情况下,以下代码可以解决我的问题:

for (Transaction transaction : transactions) {
    new Thread(transaction).start();
}
但是,很明显,在尝试启动100000个线程时,肯定会出现
java.lang.OutOfMemoryError:无法创建新的本机线程

所以接下来我尝试实现一个ExecutorService来创建一个线程池来管理我的100000个Runnable

ExecutorService service;
int cpus = Runtime.getRuntime().availableProcessors();
// cpus == 8 in my case
service = Executors.newFixedThreadPool(cpus);

for (Transaction transaction : transactions) {
    service.execute(transaction);
}
在尝试这种方法时,长进程“占用”了JVM。例如,一种类型的事务需要30-60秒才能执行。分析应用程序时,在长事务发生时,不允许运行其他线程

在本例中,线程6不允许任何其他线程运行,直到其处理完成

所以我的问题是:如何在不出现内存问题的情况下尽可能快地运行100000个事务?如果ExecutorService是答案,那么我如何停止占用JVM的很长事务,并允许其他事务并发运行

编辑:


我强制某些类型的事务发生30-60秒,以确保线程程序正常工作。每笔交易锁定在一个账户上,共有10个账户。下面是我的方法,它占用了JVM:(由
run()
调用)

每次运行此事务时,只有一个帐户被锁定,剩下9个其他帐户可供处理为什么JVM不再处理任何线程,而是挂起,直到这个长事务完成?

以下是一个指向该项目缩小版的链接,以演示该问题:

分析应用程序时,在长事务发生时,不允许运行其他线程

很可能,此任务使用的是单线程资源。i、 e ti的编写方式可防止并发使用

如何在不出现内存问题的情况下尽可能快地运行100000个事务

如果事务是CPU绑定的,那么应该有一个与CPU数量相同大小的池

如果事务依赖于数据库,则应考虑对其进行批处理,以便更有效地利用数据库

如果ExecutorService是答案,那么我如何停止占用JVM的很长事务,并允许其他事务并发运行

缩短交易时间。如果您有一个运行时间超过几毫秒的任务,您应该弄清楚为什么需要这么长时间。首先,我将了解network/IO是如何使用和分析任务的。大多数事务(如果您有大量事务)应该在0.01秒左右或远低于理想值


你应该非常小心地考虑如何使用共享资源。如果您的任务过多地使用相同的资源,您可能会发现多线程并没有更快,甚至更慢。

重要的是,要根据您的硬件计算可以并行处理事务的工作线程数。很少有公式可用于调整线程池的大小

适用于CPU限制的应用程序

N*U或(N+1)*U

适用于IO绑定的应用程序

N*U*(1+W/C)

在哪里 N-处理器数量 U-目标CPU利用率 W-等待时间 C-计算时间

例如,如果您的应用程序使用50%的CPU,并且您有8个内核。那么,对于CPU受限的应用程序,要实现高效的多线程,您必须

8*(0.5)=4


如果您有4个线程,那么您的所有内核都将高效地处理。如果你在笔记本电脑甚至16核台式机上进行操作,那么支持在不同线程中执行100000次呼叫的一些公猪很难做到这一点。您将需要一个网格或一组服务器来最佳地执行此操作


但是,您仍然可以通过在
回调中执行任何事务操作来扩展此功能。吞吐量可能会增加。

应用程序的问题是,很快所有线程都会为同一个帐户分配一个事务,然后除了一个线程外,所有线程都必须等待。你可以在下面的截图中看到这一点,我暂停了应用程序。线程池-1-Thread-3当前正在处理id为19的Account对象的事务(该id不是您的帐户id,而是Eclipse分配的唯一对象id),所有其他线程正在等待同一Account对象上的锁。account对象是您的id为9的对象

为什么会发生这种情况?在事务853中,一个线程启动第一个长时间运行的事务(对于帐户9)。其他线程继续处理其他事务。但是,当任何线程到达帐户9的另一个事务时,它将不得不停止并等待。事务857、861和862也用于帐户9,每个事务阻塞一个线程,因此此时所有线程都被阻塞(在我的四核上)

如何解决这个问题?这取决于您的用例

如果在您的实际程序中,只要有另一个事务在为帐户X运行,就可以保证给定帐户X没有传入事务,那么您不需要更改任何内容

如果您的帐户数量与线程数量相比非常大,那么问题就不太可能出现,因此您可能决定接受它

如果您的帐户数量相对较少(比方说可能少于100个左右),您应该(正如Peter所说)为每个帐户设置一个(无休止地运行)线程,每个线程都有自己的事务队列。这可能会更有效,因为线程不需要“争夺”
public void makeTransaction() {
    synchronized(account) {
        long timeStarted = System.nanoTime();
        long timeToEnd = timeStarted + nanos;

        this.view = new BatchView(transactionNumber, account.getId());

        this.displayView();

        while(true) {
            if(System.nanoTime() % 1000000000 == 0) {
                System.out.println("batch | " + account.getId());
            }

            if(System.nanoTime() >= timeToEnd) {
                break;
            }
        }
    }
}
public BlockingQueue<Transaction> queue = ...; // the global queue for all threads

public void run() {
   LinkedList<Transaction> myTransactions = new LinkedList<>();
   while (true) {
     Transaction t = queue.take();
     while (!t.getLock().tryLock()) {
        myTransactions.add(t);
     }
     try {
       // here we hold the lock for t
       t.makeTransaction();
     } finally {
       t.getLock().unlock();
     }

     Iterator<Transaction> iter = myTransactions.iterator();
     while (iter.hasNext()) {
       t = iter.next();
       if (t.getLock().tryLock()) {
         try {
           t.makeTransaction();
         } finally {
           t.getLock().unlock();
         }
         iter.remove();
       }
     }
   }
 }