在编程中,很大一部分问题都可以用顺序编程来解决。然而,对于某些问题,如果能够并行执行程序中的多个部分,就可以提高我们程序的执行效率,也就提升了程序的性能!Java编程语言支持并发编程(多线程编程)。并发编程使我们可以将程序划分为多个分离的、独立运行的任务。通过使用多线程机制,这些独立任务(也可以称为子任务)中每个都将由执行线程来驱动。一个线程就是在进程中的一个单一的顺序控制流,单个进程可以拥有多个并发执行的任务。
打个比方:你的妈妈需要买生活用品、做饭、洗碗、扫地,本来妈妈亲历亲为需要2个小时完成所有事情,现在有了外卖小哥帮忙买生活用品、妈妈负责做饭、洗碗机器人负责洗碗、扫地机器人负责扫地,将原本2小时的工作量缩短到了半小时。
将原本妈妈这个主线程需要做的事情,拆分给外卖小哥,洗碗机器人,扫地机器人这三个线程。那效率绝对比之前高哇哈哈!
一般有两种方式:
1. 继承Thread类(此种方式不推荐使用,因为Java是单继承的,假如你的类继承了Thread类,将不能再继承其他类,在此也不再介绍如何继承Thread来完成线程的创建)
2. 实现Runnable接口,构造一个Thread对象,将Runnable对象传递给Thread,调用start()方法创建线程
举个栗子:
(1)编写Test1类实现Runnable接口:
package com.ws.concurrency.test1;
public class Test1 implements Runnable {
@Override
public void run() {
System.out.println("Test1 is running..." + Thread.currentThread().getName());
}
}
(2)调用start()创建线程
package com.ws.concurrency.test1;
public class Test1Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(new Test1());
thread.start();
}
System.out.println("main is running..." + Thread.currentThread().getName());
}
}
(3)执行结果(当然每个人的电脑都不同,CPU运行的状态不同会导致结果不同)
main is running...main
Test1 is running...Thread-0
Test1 is running...Thread-1
Test1 is running...Thread-2
Test1 is running...Thread-3
Test1 is running...Thread-6
Test1 is running...Thread-7
Test1 is running...Thread-8
Test1 is running...Thread-9
Test1 is running...Thread-4
Test1 is running...Thread-5
除了这种在main中显示得创建线程外,我们还可以使用Executor来完成对线程的创建,Executor在java.util.concurrent包下,它可以帮助我们管理Thread对象,从而简化并发编程。
(1)上面栗子中的Test1类
package com.ws.concurrency.test1;
public class Test1 implements Runnable {
@Override
public void run() {
// Thread.currentThread().getName()是获取当前执行run()线程的名字,
// 我们不指定线程名字时,系统将自动给线程起名
System.out.println("Test1 is running..." + Thread.currentThread().getName());
}
}
(2)使用Executors调度器工具类创建CachedThreadPool实现线程的创建
package com.ws.concurrency.main;
import com.ws.concurrency.test1.Test1;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CachedThreadPool {
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
cachedThreadPool.execute(new Test1());
}
cachedThreadPool.shutdown(); //防止新任务被提交给这个Executor,当前main方法中的shutdown()被调用之前提交的所有任务将继续运行
System.out.println("CachedThreadPool-main is running..." + Thread.currentThread().getName());
}
}
(3)执行结果
Test1 is running...main
Test1 is running...pool-1-thread-2
Test1 is running...pool-1-thread-1
Test1 is running...pool-1-thread-3
Test1 is running...pool-1-thread-4
Test1 is running...pool-1-thread-5
可以看到CacheThreadPool为我们创建了5个线程。它在程序执行过程中通常会创建与所需数量相同的线程,然后在它回收旧线程时停止创建新的线程。
(1)上面栗子中的Test1类
package com.ws.concurrency.test1;
public class Test1 implements Runnable {
@Override
public void run() {
// Thread.currentThread().getName()是获取当前执行run()线程的名字,
// 我们不指定线程名字时,系统将自动给线程起名
System.out.println("Test1 is running..." + Thread.currentThread().getName());
}
}
(2)使用Executors调度器工具类创建FixedThreadPool实现线程的创建
package com.ws.concurrency.main;
import com.ws.concurrency.test1.Test1;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FixedThreadPool{
public static void main(String[] args) {
ExecutorService fixedThreadPool= Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
cachedThreadPool.execute(new Test1());
}
fixedThreadPool.shutdown(); //防止新任务被提交给这个Executor,当前main方法中的shutdown()被调用之前提交的所有任务将继续运行
System.out.println("FixedThreadPool-main is running..." + Thread.currentThread().getName());
}
}
(3)执行结果
Test1 is running...pool-1-thread-1
Test1 is running...pool-1-thread-2
Test1 is running...pool-1-thread-3
Test1 is running...pool-1-thread-4
Test1 is running...pool-1-thread-5
我们需要5个线程,在创建FixedThreadPool时传递给了它5这个参数,它创建了5个线程。那当我们传递3呢?
package com.ws.concurrency.main;
import com.ws.concurrency.test1.Test1;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FixedThreadPool{
public static void main(String[] args) {
ExecutorService fixedThreadPool= Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
cachedThreadPool.execute(new Test1());
}
fixedThreadPool.shutdown(); //防止新任务被提交给这个Executor,当前main方法中的shutdown()被调用之前提交的所有任务将继续运行
System.out.println("FixedThreadPool-main is running..." + Thread.currentThread().getName());
}
}
结果:
Test1 is running...pool-1-thread-2
Test1 is running...pool-1-thread-1
FixedThreadPool-main is running...main
Test1 is running...pool-1-thread-3
Test1 is running...pool-1-thread-1
Test1 is running...pool-1-thread-2
可以看出,结果是3个线程帮我们完成了5个任务,复用了线程pool-1-thread-2和pool-1-thread-1。因此,使用它我们可以一次性预先执行代价高昂的线程分配,因而也就可以限制线程的数量,不用为每个任务都固定地付出线程创建的开销。
还是在Test1的基础上修改
package com.ws.concurrency.test1;
public class Test1 implements Runnable {
private static int id = 0;
@Override
public void run() {
id ++;
System.out.println("Test1 is running " + id + "..." + Thread.currentThread().getName());
}
}
MySingleThreadExecutor.java
package com.ws.concurrency.main;
import com.ws.concurrency.test1.Test1;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MySingleThreadExecutor {
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
singleThreadExecutor.execute(new Test1());
}
singleThreadExecutor.shutdown(); //防止新任务被提交给这个Executor,当前main方法中的shutdown()被调用之前提交的所有任务将继续运行
System.out.println("MySingleThreadExecutor-main is running..." + Thread.currentThread().getName());
}
}
执行结果
MySingleThreadExecutor-main is running...main
Test1 is running 1...pool-1-thread-1
Test1 is running 2...pool-1-thread-1
Test1 is running 3...pool-1-thread-1
Test1 is running 4...pool-1-thread-1
Test1 is running 5...pool-1-thread-1
它相当于是newFixedThread(1),为我们生成数量为1的线程。而且不论你执行多少次,Test1 is running *** 的顺序始终不会改变,而且始终使用的是pool--thread-1的线程,SingleThreadExecutor执行任务按照它们的提交顺序。