操作系统(OS)中并发(同时)执行的多个程序任务
宏观并行,微观串行
在一个时间段内,CPU会将时间段划分为若干个时间片,一个时间片是能被一个程序拥有,且只有拥有时间片的程序才能执行自身内容,所以当时间片的划分足够细小,交替频率足够快,就会形成并行的假象,时间上仍然是串行.
是进程的基本组成部分
是进程中并发执行的多个任务
宏观并行,微观串行
一个时间片只能被一个进程拥有,一个进程一次又只能执行一个线程. 由于进程之间交替执行,所以线程之间必定也是交替执行
只存在多线程,不存在多进程
正在执行中的程序才叫进程,其他的都是等待执行的程序
无论是否拥有时间片,线程任务都叫线程
时间片
CPU调度分配, 线程争抢拥有
数据
堆: 堆共享
栈: 栈独立
代码
书写逻辑
继承Thread, 重写run方法
public class MyThread extends Thread {
? ?@Override
? ?public void run() {
? ? ? ?for (int i = 1; i <=100 ; i++) {
? ? ? ? ? ?System.out.println(i);
? ? ? }
? }
? ?
}
package com.by.test;
?
import com.by.thread.MyThread;
?
public class Test1 {
? ?public static void main(String[] args) {
? ? ? ?Thread t1 = new MyThread();
? ? ? ?Thread t2 = new MyThread();
?
? ? ? ?t1.start();
? ? ? ?t2.start();
? ? ? ?/*t1.run();
? ? ? ?t2.run();*/
?
? ? ? ?System.out.println("main结束");
? }
}
实现Runnable,重写run方法. 在Thread对象的构造中传入任务对象
package com.by.dao.impl;
?
/**
* 线程任务
*/
public class MyRunnable implements Runnable{
? ?@Override
? ?public void run() {
? ? ? ?for (int i=1;i<=100;i++) {
? ? ? ? ? ?System.out.println(i);
? ? ? }
? }
}
package com.by.test;
?
import com.by.dao.impl.MyRunnable;
?
import javax.print.attribute.standard.RequestingUserName;
?
public class Test2 {
? ?public static void main(String[] args) {
? ? ? ?/*//先创建任务对象
? ? ? ?Runnable r = new MyRunnable();
? ? ? ?//将任务对象传入线程对象
? ? ? ?Thread t1 = new Thread(r);*/
//任务只会执行一次时,可以通过匿名内部类或者lambda简化书写
? ? ? Thread t1=new Thread(new Runnable() {
? ? ? ? ? @Override
? ? ? ? ? public void run() {
? ? ? ? ? ? ? for (int i = 1; i <=100 ; i++) {
? ? ? ? ? ? ? ? ? System.out.println("t1:: "+i);
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? });
?
? ? ? Thread t2=new Thread(()->{
? ? ? ? ? ? ? for (int i = 101; i <=200 ; i++) {
? ? ? ? ? ? ? System.out.println("t2> "+i);
? ? ? ? ? }
? ? ? });
?
? ? ? ?t1.start();
? ? ? t2.start();
?
?
? }
}
更推荐使用第二种创建方式: 更符合类的单一职责,将线程对象的创建与线程任务的书写分离,更有利于后期的维护
当开启多个线程之后, 线程之间会争抢时间片,拿到时间片的线程执行自身内容,其他线程无法执行,只能继续尝试争抢时间片,直到线程内容执行结束,才会脱离争夺队列
主函数也成为主线程,其一定是首个拥有时间片的线程
当开启多个线程之后,JVM执行结束的标志将从主函数执行结束转换为所有线程执行结束
开启线程需要调用线程对象.start()
方法
也称为阻塞状态
sleep()
Thread.sleep(毫秒数): 使当前线程释放自身时间片, 进入有限期休眠状态,在休眠时间内,该线程无法争抢时间片,休眠结束之后,才可以进入到就绪状态
1秒=1000毫秒
该方法需要处理非运行时异常, run方法不可上抛异常,所以必须通过try-catch处理解决
Thread t1=new Thread(new Runnable() {
? ? ? ? ? @Override
? ? ? ? ? public void run() {
? ? ? ? ? ? ? //让当前线程休眠3秒钟
? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? Thread.sleep(3000);
? ? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? ? System.out.println("休眠异常");
? ? ? ? ? ? ? }
? ? ? ? ? ? ? for (int i = 1; i <=100 ; i++) {
? ? ? ? ? ? ? ? ? System.out.println("t1:: "+i);
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? });
join()
线程对象.join(): 使调用者线程在当前线程之前执行, 当前线程只有等调用者线程执行结束进入死亡状态之后才有可能回到就绪状态.
该方法需要处理非运行时异常,run方法无法上抛,必须通过try-catch处理
package com.by.test;
?
public class Test3 {
? ?public static void main(String[] args) {
? ? ? ?//以下代码执行顺序:t1->t2->t3
? ? ? ?Thread t1=new Thread(()->{
? ? ? ? ? ?for (int i = 0; i < 30; i++) {
? ? ? ? ? ? ? ?System.out.println("t1: "+i);
? ? ? ? ? }
? ? ? });
?
? ? ? ?Thread t2=new Thread(()->{
? ? ? ? ? ?//使t1线程在t2线程之前执行
? ? ? ? ? ?try {
? ? ? ? ? ? ? ?t1.join();
? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ?System.out.println("join失败");
? ? ? ? ? }
? ? ? ? ? ?for (int i = 101; i < 130; i++) {
? ? ? ? ? ? ? ?System.out.println("t2>"+i);
? ? ? ? ? }
? ? ? });
?
? ? ? ?Thread t3=new Thread(()->{
? ? ? ? ? ?try {
? ? ? ? ? ? ? ?t2.join();
? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ?System.out.println("join失败");
? ? ? ? ? }
? ? ? ? ? ?for (char i = 65; i <=90; i++) {
? ? ? ? ? ? ? ?System.out.println("t3::"+i);
? ? ? ? ? }
? ? ? });
?
?
? ? ? ?t1.start();
? ? ? ?t2.start();
? ? ? ?t3.start();
? }
}
sleep和join的区别?
sleep方法进入的是有限期等待状态,join方法进入的是无限期等待状态
sleep是静态方法,可以直接通过类名调用,join是非静态方法,必须通过线程对象调用
前言: 当一个任务需要多次执行时,如果将任务放置于线程对象Thread中,会浪费内存空间导致不合理的并发,线程池可以解决该问题
管理盛放线程任务, 将需要执行的任务提交执行,任务结束之后池与任务并不会立即销毁,任务对象会回到池中等待下次执行,直到线程池关闭,内部任务才会失效
ExecutorService: 线程池接口
submit(线程任务对象): 提交线程任务使其执行
shutdown(): 关闭线程池
Executors: 线程池工具类,用来获取线程池对象
newCachedThreadPool(): 获取一个不固定并发数量的线程池对象
newFixedThreadPool(int ):获取一个固定并发数量的线程池对象
不固定并发数量的线程池: 所有提交到池中的任务都会同时并发
固定并发数量的线程池: 对应并发数量的任务先并发执行,超出的任务需要等待执行,等池中执行的任务结束让位之后,超出部分的任务才会进入池中执行
Runnable: run()
无返回值,不能上抛异常
package com.by.test;
?
import com.by.dao.impl.MyRunnable;
?
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
?
public class Test4 {
? ?public static void main(String[] args) {
? ? ? ?//获取一个不固定并发数量的线程池
? ? ? // ExecutorService es1 = Executors.newCachedThreadPool();
? ? ? ?ExecutorService es1 = Executors.newFixedThreadPool(2);
?
? ? ? ?Runnable r1=new Runnable() {
? ? ? ? ? ?@Override
? ? ? ? ? ?public void run() {
? ? ? ? ? ? ? ?for (int i = 0; i < 50; i++) {
? ? ? ? ? ? ? ? ? ?System.out.println("r1>>"+i);
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? };
? ? ? ?Runnable r2=new Runnable() {
? ? ? ? ? ?@Override
? ? ? ? ? ?public void run() {
? ? ? ? ? ? ? ?for (int i = 50; i < 100; i++) {
? ? ? ? ? ? ? ? ? ?System.out.println("r2:::"+i);
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? };
? ? ? ?//提交任务执行
? ? ? ?es1.submit(r1);
? ? ? ?es1.submit(r2);
? ? ? ?es1.submit(r2);
?
? ? ? ?//关闭线程池
? ? ? ?es1.shutdown();
? }
}
Callable: call()
有返回值,可以上抛异常,默认上抛Exception
返回值会存放于一个Future对象中,可以通过Future对象.get()
获取内部的返回值
Callable<返回值类型> c1=new Callable<返回值类型>() {
? ? ? ? ? ?@Override
? ? ? ? ? ?public 返回值类型 call() throws Exception {
? ? ? ? ? ? ? //...
? ? ? ? ? ? ? ?return 值;
? ? ? ? ? }
? ? ? };
package com.by.test;
?
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
?
public class Test5 {
? ?public static void main(String[] args) throws Exception {
? ? ? ?//创建线程池
? ? ? ?ExecutorService es = Executors.newCachedThreadPool();
? ? ? ?//线程任务1: 计算1-100的和并返回
? ? ? ?Callable<Integer> c1=new Callable<Integer>() {
? ? ? ? ? ?@Override
? ? ? ? ? ?public Integer call() throws Exception {
? ? ? ? ? ? ? ?int sum=0;
? ? ? ? ? ? ? ?for (int i = 0; i < 101; i++) {
? ? ? ? ? ? ? ? ? ?sum += i;
? ? ? ? ? ? ? }
? ? ? ? ? ? ? ?return sum;
?
? ? ? ? ? }
? ? ? };
? ? ? ?//提交任务执行并接收
? ? ? ?Future<Integer> f =es.submit(c1);
? ? ? ?System.out.println(f.get());
?
? ? ? ?System.out.println(es.submit(c1).get());
?
? ? ? ?//关闭线程池
? ? ? ?es.shutdown();
? }
}