Java多线程与线程池
关于并发,不要忘了Java中有个十分强大的AQS框架
1.概念
1.1 并发 & 并行 & 串行
并发:宏观上多个任务同时执行,实际上同一时刻只有一个线程在执行。
并行:微观上多个任务同时执行,即同一时刻多个任务同时执行。
串行:单线程
1.2 并发的作用
并发通常是用于提高运行在单处理器上的程序的性能。乍看不对,多线程涉及轮转使用CPU增加了上下文切换时间,应该更慢。但是我们还需要考虑程序中线程阻塞。单线程程序线程阻塞会导致整个程序阻塞,但是多线程可以应对这种情况。也就是说,如果程序中没有任务会阻塞,多线程将毫无意义。
1.3 并发的多面性
优:
让程序执行更快(总体来看),更充分地利用CPU,提高系统性能。
劣:
带来线程安全问题,增加了系统风险,编程难度也提高了。所以在使用并发时,默认线程不安全,时刻提醒自己。
1.4 线程基本实现机制
底层原理是切分CPU时间,为线程分配CPU时间切片,获得CPU时间切片才可使用CPU。
2.创建线程
2.1 定义线程任务Runnable
实现Runnable
接口是自定义功能简单的线程的常用方式。
public class RunnableThread implements Runnable {
@Override
public void run() {
// ...
}
}
光是定义一个Runnable
接口的实现类是无法实现线程行为的,它就相当于一个普通类,必须要将这个任务显示地附着到线程上。
public class Main {
public static void main(String[] args) {
Thread t = new Thread(new RunnableThread());
t.start();
}
}
2.2 Thread类
如果想用Thread的方法,可以继承Thread创建任务。
public class MyThread extends Thread {
@Override
public void run() {
// ...
}
}
另外还有之中匿名方式,也是lambda。
public class Main {
public static void main(String[] args) {
new Thread(() -> {
System.out.println("是只猪呀");
}).start();
}
}
2.3 Callable
还可以实现Callable
接口,用得比较少,知道有就行。
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
2.4 线程池创建线程
关于线程池,必须要先理清几个接口和类的关系,文章后面会介绍使用。
Executor
接口:执行提交的Runnable
任务的对象ExecutorService
接口:提供了更多管理方法API。ExecutorService extends Executor
Executors
类:定义了ExecutorService
、Executor
、ScheduledExecutorService
、ThreadFactory
和Callable
类的工厂和方法。里面包含了大量的public static ExecutorService
方法,用于创建线程池,而这些方法中部分又使用new ThreadPoolExecutor()
来创建线程池。ThreadPoolExecutor
:阿里Java开发手册推荐使用的创建线程池的工具。
3.线程的生命周期
NEW
:新建RUNNABLE
:运行BLOCKED
:阻塞WAITING
:等待TIMED_WAITING
:计时等待TERMINATED
:已终止
// 新建状态
Thread t = new MyThread();
// 运行状态
t.start();
// 阻塞状态
线程 t 需要 synchronized(obj),但是已经被别的线程持有,一直去尝试给这个对象加锁
// 等待状态
t.wait();
t.join(); // 等待别的线程结束
LockSupport.park();
// 计时等待 给定了时长的
t.sleep(1000);
t.wait(1000);
// 终止
线程任务执行完成
如果线程需要执行一个长时间任务,可能需要能中断线程
// 中断线程
t.interrupt();
// 检查线程是否中断 都调用native方法
t.isInterrupted();
4.守护线程
说到守护线程首先想到JVM的垃圾收集器。守护线程是为用户线程服务的,所有用户线程结束后,整个程序就结束了,而不会关心守护线程状态。守护线程是低优先级线程,任何守护线程都是为所有的用户线程服务,而不是某些用户线程。可以t.setDaemon(true);t.start();
这样设置,要在start()
之前。
5.死锁
死锁表现为:多个线程都持有部分资源,而又在相互尝试获取其它线程持有的资源的状态。
形成锁的四个必要条件:
- 互斥:两个线程执行都需要相同的资源
- 不可剥夺:线程持有的资源不能被其线程剥夺,只能自己释放
- 请求与保持:持有部分资源,同时请求另一部分必要资源
- 循环等待:如两个线程都在等待对方释放资源
死锁的解决办法,破坏四个条件中的任意一个:
- 互斥:是必然的,否则没有加锁的必要。
- 不可剥夺:将资源定义成原子的,一次性获取所有需要的资源
- 请求与保持:申请不到资源可以主动释放已占有的资源
- 循环等待:按顺序申请资源,逆序释放
6.线程池的使用
6.1 Executors.newFixedThreadPool
创建固定大小线程池
// 5个线程的线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交任务
executor.submit(new RunnableTask());
// 关闭线程池
executor.shutdown();
// 立即关闭 不会等待线程执行结束
executor.shutdownNow();
// 指定延时后关闭 需要捕获InterruptedException
executor.awaitTermination(60, TimeUnit.SECONDS);
6.2 Executors.newCachedThreadPool
根据任务数量动态调整线程池的大小
6.3 ScheduledThreadPool
定时任务
ScheduledExecutorService ses = Executors.newScheduledThreadPool(4);
// 10秒后执行任务 且只执行一次
ses.schedule(new RunnableTask(), 10, TimeUnit.SECONDS);
// 延迟10秒后开始执行任务 任务每5秒内执行一次
ses.scheduleAtFixedRate(new RunnableTask(), 10, 5, TimeUnit.SECONDS);
// 延迟10秒后开始执行任务 任务每隔5秒执行一次(这个是不包含执行任务的时间的,如任务执行需要2秒,那就是每7秒执行一次)
ses.scheduleWithFixedDelay(new RunnableTask(), 10, 5, TimeUnit.SECONDS);
6.4 ThreadPoolExecutor
Executors
的方法也使用了ThreadPoolExecutor
来创建线程池。使用ThreadPoolExecutor
能够让我们更方便控制其核心参数。关于ThreadPoolExecutor可以参考另一篇博客。
原文地址:https://www.cnblogs.com/farwalking/p/15242258.html
- MySQL中的Online DDL(第一篇)(r11笔记第3天)
- 转--quick-cocos做客户端,golang做服务端,实现HTTP通信
- Nginx配置SSL证书
- Golang语言RPC Authorization进行简单ip安全验证的方法
- 深入理解Oracle中的DBCA
- Golang语言goto语句
- 转--Golang语言语法汇总
- Oracle,MySQL迁移整合的问题总结(r10笔记第99天)
- MySQL修复表的简单分析(r11笔记第19天)
- Golang语言中的流程控制结构和函数详解
- Golang语言版的ip2long函数实例
- Oracle闪回原理-Logminer解读redo(r11笔记第17天)
- beego如何做到XSRF防护
- Golang语言-- gbk转utf8
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Linux使用sed命令替换字符串教程
- Android实现获取短信验证码并自动填写功能
- Android 定时器实现图片的变换
- Android 软键盘状态并隐藏输入法的实例
- Linux磁盘管理之LVM的使用
- Android编程之菜单Menu的创建方法示例
- Ubuntu下Docker CE的安装
- 基于Android自定义控件实现雷达效果
- Android 中 onSaveInstanceState()使用方法详解
- Linux修改主机名的简单方法
- Android RecycleView使用(CheckBox全选、反选、单选)
- Android自定义滑动解锁控件使用详解
- linux nc命令小结
- Android如何自定义EditText光标与下划线颜色详解
- 基于Centos7 部署Varnish缓存代理服务器