When learning about multithreading, we all know that multiple threads can execute concurrently if they are allocated to multiple cores of the CPU.But is that true?
First let's look at the computer configuration:
Test computer is single CPU, 4 cores.It makes sense to create four threads that can be allocated to four cores for simultaneous execution.Next execute the test code to see the results!
public class ThreadTest { private static final int num = 1000 * 1000; public static void main(String[] args) throws InterruptedException { new Thread(()->{ for (int i = 0; i < num; i++) { System.out.println(i); } },"Thread 1").start(); new Thread(()->{ for (int i = 0; i < num; i++) { System.out.println(i); } },"Thread 2").start(); new Thread(()->{ for (int i = 0; i < num; i++) { System.out.println(i); } },"Thread 3").start(); new Thread(()->{ for (int i = 0; i < num; i++) { System.out.println(i); } },"Thread 4").start(); } }
The test code creates four threads, each traversing one million times.By using the JDK native monitoring tool: Visual VM to view the execution of threads, is it really like I imagine, concurrent execution threads?
Focusing on the contents of the red box, it is amazing to see that multiple threads are not executing concurrently at all, but are constantly switching contexts between threads!That is, all four threads are executed in a single kernel, and the other cores are not working!
This is a bit of a disruption of my knowledge, and then I kept google, consulting the data, I found that this has something to do with the algorithm of the operating system CPU![Reference article: https://www.zhihu.com/question/64072646]
Threads are dispatched according to the algorithm of the cpu. If the thread is not computationally heavy, the CPU algorithm dispatch threads may not be evenly allocated to each kernel.That means that if the computation is heavy, other cores will be used?
Continue to improve the test code:
public class ThreadTest{ // Data volume private static final int num = 2000 * 1000; // The fence is set to prevent the main thread from executing output before the child thread has finished private static final CountDownLatch countDownLatch = new CountDownLatch(4); private static ExecutorService service = Executors.newFixedThreadPool(4); private static final String filePath1 = "/Users/hao/IdeaProjects/Sample/src/test1.txt"; private static final String filePath2 = "/Users/hao/IdeaProjects/Sample/src/test2.txt"; private static final String filePath3 = "/Users/hao/IdeaProjects/Sample/src/test3.txt"; private static final String filePath4 = "/Users/hao/IdeaProjects/Sample/src/test4.txt"; private static File file1 = new File(filePath1); private static File file2 = new File(filePath2); private static File file3 = new File(filePath3); private static File file4 = new File(filePath4); public static void main(String[] args) throws InterruptedException, IOException { // start time long startTime = System.currentTimeMillis(); new Thread(new WriteFileThread(file1),"Thread 1").start(); new Thread(new WriteFileThread(file2),"Thread 2").start(); new Thread(new WriteFileThread(file3),"Thread 3").start(); new Thread(new WriteFileThread(file4),"Thread 4").start(); try { countDownLatch.await(); } finally { service.shutdown(); } // End time long endTime = System.currentTimeMillis(); System.out.println(); System.out.println("The total time consumed is:" + (endTime - startTime) / 1000.0 + "s"); } static class WriteFileThread implements Runnable { private File file; public WriteFileThread(File file) { this.file = file; } @Override public void run() { writeFile(file); } } static void writeFile(File file){ // Determine if there is this file if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } if (!file.exists()) { try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } long startTime = System.currentTimeMillis(); //Create output buffer stream object BufferedWriter bufferedWriter = null; try { bufferedWriter = new BufferedWriter(new FileWriter(file)); } catch (IOException e) { e.printStackTrace(); } for (int i = 0; i < num; i++) { try { bufferedWriter.write(i); bufferedWriter.newLine(); bufferedWriter.flush(); } catch (IOException e) { e.printStackTrace(); } } long endTime = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName() + "Execution complete, time consuming : " + (endTime - startTime) / 1000 + "s"); countDownLatch.countDown(); try { bufferedWriter.close(); } catch (IOException e) { e.printStackTrace(); } } }
Output results:
Thread 4 execution completed, time-consuming: 22s Thread 3 execution completed, time-consuming: 22s Thread 1 execution completed, time-consuming: 22s Thread 2 execution completed, time-consuming: 24s Total time consumed is: 24.709s
Looking at the Visual VM monitoring tool again, you can see that all four threads executed concurrently!