为什么在静态代码中使用Class.forName()会在使用多线程时终止进程?

huangapple go评论73阅读模式
英文:

Why Class.forName() in static code terminate the process when using mutil-thread?

问题

以下是翻译好的内容:

我目前正在学习JDBC。我尝试在更新产品信息的同时插入日志。

private void testTransaction() {
        try {
            // 获取连接
            Connection connection = ConnectionUtils.getConnection();
            connection.setAutoCommit(false);

            // 执行SQL
            Product product = new Product(1, 4000d);
            productService.updateProduct(connection, product);
            Log log = new Log(true, "None");
            logService.insertLog(connection, log);

            // 提交事务
            connection.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            ConnectionUtils.closeConnection();
        }
    }

在单线程中使用时,一切正常。

    @Test
    public void testMultiThread()  {
        testTransaction();
    }

但是当我使用多线程时,即使只启动一个线程,进程也会自动终止。

    @Test
    public void testMultiThread()  {
        for (int i = 0; i < 1; i++) {
            new Thread(this::testTransaction).start();
        }
    }

经过调试,我发现是ConnectionUtils中的Class.forName()函数导致了这种情况。

public class ConnectionUtils {
    static private String url;
    static private String driver;
    static private String username;
    static private String password;

    private static Connection connection = null;

    private static ThreadLocal<Connection> t = new ThreadLocal<>();

    static {
        try {
            Properties properties = new Properties();
            properties.load(new FileReader("src/main/resources/jdbcConnection.properties"));

            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");

            Class.forName(driver);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

    public static Connection getConnection() {
        try {
            connection = DriverManager.getConnection(url, username, password);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            t.set(connection);
        }
        return connection;
    }
}

进程会在Class.forName()处终止。我通过在语句前后添加两个打印语句来发现这一点。而且只有前者起作用。

System.out.println("Before");
Class.forName(driver);
System.out.println("After");

控制台只打印出Before,并且没有显示任何异常信息。
我想知道为什么Java中的多线程会导致这种情况,以及如何解决这个问题。

英文:

I'm currently learning JDBC. And I try to update the product information and insert a log at the same time.

private void testTransaction() {
        try {
            // Get Connection
            Connection connection = ConnectionUtils.getConnection();
            connection.setAutoCommit(false);

            // Execute SQL
            Product product = new Product(1, 4000d);
            productService.updateProduct(connection, product);
            Log log = new Log(true, &quot;None&quot;);
            logService.insertLog(connection, log);

            // Commit transaction
            connection.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            ConnectionUtils.closeConnection();
        }
    }

When using single thread, it would be fine.

    @Test
    public void testMultiThread()  {
        testTransaction();
    }

But When I using multi-thread, even start one thread, the process would terminate automatically.

    @Test
    public void testMultiThread()  {
        for (int i = 0; i &lt; 1; i++) {
            new Thread(this::testTransaction).start();
        }
    }

After debugging, I found that it was Class.forName() function in ConnectionUtils cause this situation.

public class ConnectionUtils {
    static private String url;
    static private String driver;
    static private String username;
    static private String password;

    private static Connection connection = null;

    private static ThreadLocal&lt;Connection&gt; t = new ThreadLocal&lt;&gt;();

    static {
        try {
            Properties properties = new Properties();
            properties.load(new FileReader(&quot;src/main/resources/jdbcConnection.properties&quot;));

            driver = properties.getProperty(&quot;driver&quot;);
            url = properties.getProperty(&quot;url&quot;);
            username = properties.getProperty(&quot;username&quot;);
            password = properties.getProperty(&quot;password&quot;);

            Class.forName(driver);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

    public static Connection getConnection() {
        try {
            connection = DriverManager.getConnection(url, username, password);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            t.set(connection);
        }
        return connection;
    }
}

The process will terminate at Class.forName(). I found this by adding two print funcion before and after the statement. And only the former works.

System.out.println(&quot;Before&quot;);
Class.forName(driver);
System.out.println(&quot;After&quot;);

The console only print the Before and doesn't show any exception information.
I want to know that why multi-thread in java will cause this situation and how to solve this problem.

答案1

得分: 2

这更有可能是因为您的测试方法在其他线程之前完成,测试框架并没有等待(junit?)。您需要等待这些线程完成。您应该使用 Executors,这更方便。

@Test
public void testMultiThread()  {
    
    Thread[] threads = new Thread[1];
    for (int i = 0; i < threads.length; i++) {
        threads[i] = new Thread(this::testTransaction);
        threads[i].start();
    }

    // 等待线程完成
    for (Thread th : threads) {
        th.join();
    }

}
英文:

This is more likely your test method complete before your other threads and the test framework is not waiting (junit?). You need to wait until the threads have completed. You should use an Executors, this is more convinient.

@Test
public void testMultiThread()  {
    
    Thread[] threads = new Thread[1];
    for (int i = 0; i &lt; threads.length; i++) {
        threads[i] = new Thread(this::testTransaction);
        threads[i].start();
    }

    // wait thread completion
    for (Thread th : threads) {
        th.join();
    }

}

答案2

得分: 1

Junit会在测试方法完成后终止所有线程。

在您的情况下,测试将在循环结束时结束,它不会关心testTransaction是否已经完成。这与class.forName无关,可能只是因为这个方法执行时间较长。

您可以查看这个答案

英文:

Junit will terminate all your thread as long as the test method finish.

In your case, test will finish when the loop ends, it doesn't care whether
testTransaction has finished. It has nothing to do with class.forName , maybe it's just because this method exceute longer.

you can check this answer

huangapple
  • 本文由 发表于 2020年8月30日 10:56:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/63653544.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定