3、多线程基础
一、线程与进程
1.1、进程(Process)
是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。 在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。
1.2、线程(thread)
是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
1.3、简单总结
1.4、区别
线程与资源分配无关,它属于某一个进程,并与进程内的其他线程一起共享进程的资源。
每个进程都有自己一套独立的资源(数据),供其内的所有线程共享。
不论是大小,开销线程要更“轻量级”。
一个进程内的线程通信比进程之间的通信更快速,有效。(因为共享变量)。
二、线程的生命周期
线程是一个动态执行的过程,它也有一个从产生到死亡的过程。下图显示了一个线程完整的生命周期:
线程中有一个枚举类型,包括了所有的线程状态:
public enum State {
//NEW状态表示线程刚刚被定义,还未实际获得资源以启动,也就是还未调用start()方法。
NEW,
//RUNNABLE表示线程当前处于运行状态,当然也有可能由于时间片使用完了而等待CPU重新的调度。
RUNNABLE,
//BLOCKED表示线程在竞争某个锁失败时被置于阻塞状态。
BLOCKED,
//WAITING和TIMED_WAITING表示线程在运行中由于缺少某个条件而不得不被置于条件等待队列,等待需要的条件或资源。
WAITING,
TIMED_WAITING,
//TERMINATED表示线程运行结束,当线程的run方法结束之后,该线程就会是TERMINATED状态。
TERMINATED;
}2.1、线程状态之间的切换
public class NewThread implements Runnable {
@Override
//synchronized关键字,其底层是对对象加锁
public synchronized void run() {
while(true) {
try {
//让线程处于等待状态,让当前线程失去对锁的拥有权
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("自定义的线程执行了....");
}
}
public static void main(String[] args) {
NewThread n = new NewThread();
// 创建线程,并指定线程任务,线程进入初始化状态
Thread thread = new Thread(n);
// 启动线程
thread.start();
while(true) {
synchronized (n) {
System.out.println("主线程执行了...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//通知所有线程,释放掉锁,进入就绪状态notify()和notifyAll()的调用最好是在synchronized方法的最后一步
n.notifyAll();
}
}
}
}三,创建线程的多种方式(8种)
3.1、继承Thread类
创建一个新的类,该类继承 Thread 类,然后创建一个该类的实例。在java.lang包中定义, 继承Thread类必须重写run()方法,该方法是新线程的入口点。它也必须调用 start() 方法才能执行。
/**
* 通过继承Thread来实现多线程.
*/
public class Thread1 extends Thread{
@Override
public void run() {
System.err.println("通过继承Thread来实现多线程.");
}
}其本质上是实现了 Runnable 接口的一个实例:
3.2、实现Runnable接口
/**
* 通过实现Runnable来实现多线程.
*/
public class Thread2 implements Runnable{
public void run() {
System.err.println("通过实现Runnable来实现多线程.");
}
}3.3、通过匿名内部类的方式创建线程
匿名内部类创建的线程只会执行一次
public static void main(String[] args) {
//第一种
/*new Thread() {
public void run() {
System.out.println("thread start ..");
};
}.start();*/
//第二种
/*new Thread(new Runnable() {
@Override
public void run() {
System.out.println("thread start ..");
}
}).start();*/
//第三种
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("runnable");
}
})
}.start();
}3.4、通过 Callable 和 Future 创建线程(带返回值的线程)
创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。
创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。
/**
* 通过 Callable 和 Future 创建线程.
*/
public class Thread3 implements Callable<Integer>{
//该方法将作为线程执行体,并且有返回值。
@Override
public Integer call() throws Exception {
int i = 100;
System.err.println("通过 Callable 和 Future 创建线程.i=" + i);
return i;
}
}public class TestMain {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Thread3 thread3 = new Thread3();
FutureTask<Integer> ft = new FutureTask<Integer>(thread3);
new Thread(ft).start();
System.out.println("线程的返回值:" + ft.get());
}
}3.5、通过定时器创建线程
不延迟的每秒钟执行一次指定的线程
public static void main(String[] args) {
Timer timer = new Timer();
//Timer.schedule(TimerTask task,long delay,long period)
//安排指定的任务(task)从指定延迟后的时间(delay)开始进行重复且固定的时间(period)执行.
timer.schedule(new TimerTask() {
@Override
public void run() {
// 实现定时任务
System.out.println("timertask is run");
}
}, 0, 1000);
}3.6、通过线程池创建线程
public static void main(String[] args) {
//创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
ExecutorService threadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
}
//关闭线程池
threadPool.shutdown();
}3.7、Lambda创建线程
public static void main(String[] args) {
List<Integer> values = Arrays.asList(10,20,30,40);
int res = new Test().add(values);
System.out.println(res);
}
public int add (List<Integer> values) {
return values.parallelStream().mapToInt( i -> i++).sum();
}3.8、Spring对多线程的支持
//标记是Spring的配置类
@Configuration
//扫描包
@ComponentScan("com.zender.thread")
// 启用异步任务
@EnableAsync
public class ThreadConfig {
/**
* 返回一个Executor线程池
*/
@Bean
public Executor getExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);
executor.initialize();
return executor;
}
}@Service
public class ThreadService {
/**
* 这里进行标注为异步任务,在执行此方法的时候,会单独开启线程来执行f1方法。
*/
@Async
public void test1() {
System.out.println("test1 : " + Thread.currentThread().getName() + " " + UUID.randomUUID().toString());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 这里进行标注为异步任务,在执行此方法的时候,会单独开启线程来执行f1方法。
*/
@Async
public void test2() {
System.out.println("test2 : " + Thread.currentThread().getName() + " " + UUID.randomUUID().toString());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}public class TestMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ThreadConfig.class);
ThreadService service = context.getBean(ThreadService.class);
for (int i = 0; i < 5; i++) {
// 执行异步任务
service.test1();
service.test2();
}
}
}四,Thread常用方法
下表列出了Thread类的一些重要方法:
五,4种线程池
在Java 5之后,并发编程引入了一堆新的启动、调度和管理线程的API。Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。
Executor框架包括:线程池,Executor,Executors,ExecutorService,CompletionService(异步任务),Future,Callable等。
5.1、Executor接口
Executor接口是Executor框架中最基础的部分,定义了一个用于执行Runnable的execute方法,它没有实现类只有另一个重要的子接口ExecutorService。
public interface Executor {
void execute(Runnable command);
}5.2,ExecutorService接口
ExecutorService接口继承自Executor接口,定义了终止、提交,执行任务、跟踪任务返回结果等方法。
public interface ExecutorService extends Executor {
//关闭线程池,当此方法被调用时,ExecutorService停止接收新的任务并且等待已经提交的任务(包含提交正在执行和提交未执行)执行完成。
//当所有提交任务执行完毕,线程池即被关闭。
void shutdown();
//立即停止,将暂停所有等待处理的任务并返回这些任务的列表
List<Runnable> shutdownNow();
//判断执行器是否已经关闭
boolean isShutdown();
//判断关闭后所有任务是否都已完成
boolean isTerminated();
//接收timeout和TimeUnit(日期工具类)两个参数,用于设定超时时间及单位。
//当等待超过设定时间时,会监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false。
//一般情况下会和shutdown方法组合使用。
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
//提交一个Callable任务
<T> Future<T> submit(Callable<T> task);
//提交一个Runable任务,result是要返回的结果
<T> Future<T> submit(Runnable task, T result);
//提交一个任务
Future<?> submit(Runnable task);
//执行所有给定的任务,当所有任务完成,返回保持任务状态和结果的Future列表
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
//执行给定的任务,当所有任务完成或超时期满时(无论哪个首先发生),返回保持任务状态和结果的 Future 列表。
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
//执行给定的任务,如果某个任务已成功完成(也就是未抛出异常),则返回其结果。
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
//执行给定的任务,如果在给定的超时期满前某个任务已成功完成(也就是未抛出异常),则返回其结果。
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}5.3、Executors类
可以Executors类来创建的线程池的类型,可以创建5种类型的线程池。
newFixedThreadPool类型
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 12; i++) {
int index = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
//关闭线程池
threadPool.shutdown();
}因为线程池大小为3,每个任务输出后间隔2秒,所以每两秒打印3个数字。
newCachedThreadPool类型
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
public static void main(String[] args) {
ExecutorService threadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 4; i++) {
int index = i;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("threadName="+ Thread.currentThread().getName() + " index=" + index);
}
});
}
//关闭线程池
threadPool.shutdown();
}线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
newSingleThreadExecutor类型
创建一个线程池(这个线程池只有一个线程),这个线程池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去。
public class Thread4 extends Thread{
@Override
public void run() {
int temp = 0;
int i = 0;
Random random =new Random();
while(true){
int j =random.nextInt(100);
System.err.println("threadName="+ Thread.currentThread().getName() + " temp="+ temp +" i="+ (i++) + " j=" + j);
try{
if(temp == 0 && j > 60 ) {
temp = 7/0;
}
Thread.sleep(100);
}catch(Exception e){
e.printStackTrace();
temp = 1;
}
}
}
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor();
Thread4 thread4 = new Thread4();
threadPool.execute(thread4);
}
}newScheduledThreadPool类型
创建一个定长线程池,支持定时及周期性任务执行。
延迟3秒执行:
public static void main(String[] args) {
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
threadPool.schedule(new Runnable() {
@Override
public void run() {
System.out.println("delay 3 seconds");
}
}, 3, TimeUnit.SECONDS);
//关闭线程池
threadPool.shutdown();
}延迟1秒后每3秒执行一次:
public static void main(String[] args) {
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
threadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("delay 3 seconds");
}
},1 ,3 , TimeUnit.SECONDS);
}newSingleThreadExecutor类型
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}结果依次输出,相当于顺序执行各个任务。
版权声明
非特殊说明,本文由Zender原创或收集发布,欢迎转载。
ZENDER












发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。