我有一个方法做一些api调用来保存filenet存储库上的图像,然后它在两个数据库表上做一些日志记录,整个方法需要大量的时间来执行,所以我想把登录分成一个单独的线程如下:
public void createNewDocument(){
doSaveImage();
new Thread()
{
@Override
public void run()
{
try{
new LogDAO().addLog(log);
new ReportsDAO().addCreatedDocumentLog(productivityReport);
}catch(Exception e){
e.printStackTrace();
}
}
}.start();
}
它工作正常,对单个用户没有任何问题,所以我的问题是,考虑到一个糟糕的设计,当多个用户同时调用此方法(内存问题?)或安全问题时,它是否会导致问题?
答案 0 :(得分:2)
打开线程,关心一些事情并不是一个坏主意。
数据库连接开启/关闭
交易不会跨越,每个线程都会拥有自己的交易。
如果任何错误/异常发生,则回滚事务 工人线程
你也可以如何管理你的线程,是可运行实例中的线程,还是go> to pool,并在每次实例化等等。在这里讨论threadpoool,>如何管理它。
答案 1 :(得分:1)
拥有多个线程的主要问题是更改线程上下文意味着开销。
交叉点有更多线程会对您的系统造成不利影响将取决于您拥有多少请求,以及每个线程在空闲状态下花费的时间(等待I / O或数据库操作发生) ,例如)。
最安全的方法是创建一个线程池(重用线程,并配置为具有最大并发线程数)并找到更适合您系统的数字。我所知道的大多数服务器都支持开箱即用。
此外,您的设计还有一个缺点,即您正在吞咽操作过程中发生的任何错误。即使不是,您的客户也会认为操作已成功执行。如果你能负担得起,请仔细考虑。
答案 2 :(得分:1)
同时拥有多个(数百个)线程并不是一个好主意。
首先,你的CPU只有4或8个内核,所以它无论如何都不能同时运行多个线程 - 让100个线程同时运行是没有用的,大多数他们将等待,直到CPU核心可以自由运行它们。
制作线程相对昂贵。每个帖子都有一个call stack。此调用堆栈的默认大小取决于平台,但通常范围从几百KB到几MB。这意味着如果你启动100个线程,那么所有这些线程的调用堆栈已经占用了大量内存(最多可达数百MB)。
您应该使用线程池。 Java在java.util.concurrent
包中有一个完整的线程池框架。
示例:
public void createNewDocument(ExecutorService executorService) {
doSaveImage();
executorService.execute(new Runnable() {
@Override
public void run() {
try {
new LogDAO().addLog(log);
new ReportsDAO().addCreatedDocumentLog(productivityReport);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
您使用ExecutorService
中的一种工厂方法创建Executors
,例如:
ExecutorService executorService = Executors.newFixedThreadPool(4);
答案 3 :(得分:1)
正如@ ankur-singhal指出,在线程方面还有其他更重要的因素。
但是,要回答关于线程数的问题:普通PC在正常的一天(所有应用程序和操作系统本身)都有大约1500个活动线程。您可以在“性能”选项卡中打开Windows任务管理器并自行检查。
这表明普通PC可以轻松处理数千个线程。
在服务器上,如果您的应用程序是唯一正在运行的应用程序,则可以有500-1000个线程而不会遇到任何问题。
然而,仅仅因为你并不意味着你应该这样做!如果你每小时有一百万个请求,你可以拥有一个500个线程的线程池。如果每小时有10000个请求,则可以使用25或50的线程池。
在任何情况下,使用ExecutorService
的普通线程都是一个很好的设计实践。
并且,当您的申请退出时,不要忘记致电shutdown
。
答案 4 :(得分:1)
您建议每次调用createNewDocument()
时创建并销毁新线程。创建和销毁线程比唤醒现有的,长期存在的工作线程更加昂贵。
java.util.concurrent.ThreadPoolExecutor
类的实例将为您管理工作线程的池。每次程序想要执行任务时,它都可以向ThreadPoolExecutor提供Runnable
对象或Callable
对象,执行程序将唤醒将执行任务的其中一个池线程。 / p>
如果你不经常调用createNewDocument(),那么你建议的方式没有任何问题,但是如果你养成使用更高级别的习惯,它会让你成为更好的开发者标准库和其他库为您提供的服务(例如,ThreadPoolExecutor)。
答案 5 :(得分:0)
因为,
i)它可能很容易导致死锁状态。
ii)拥有太多线程会对问题的时间复杂度产生影响。
iii)当持有锁的线程的时间片到期时,出现另一个问题。等待锁的所有线程现在必须等待保持线程获得另一个时间片并释放锁。如果锁实现是公平的,则问题更严重,其中锁是以先到先得的顺序获取的。如果等待的线程被挂起,那么在它后面等待的所有线程都被阻止获取锁。 就像让某人在结账行中入睡一样。