一、线程的基本知识

1.1 线程知识

进程和线程的关系和区别

线程:

线程是进程的基本执行单元,进程想要执行任务,必须要有线程。程序启动默认开启一条线程,这个线程被称为主线程。

进程:

进程是指在系统中正在运行的一个应用程序。每个进程之间是独立的,每个进程均运行在其专用且受保护的内存里。

线程的六个状态:

NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED

Thread流程图:

Thread的方法:

方法 说明
void join() t.join() 当前线程调用其他线程的t.join()方法,当前线程进入等待状态,当前线程不会释放已经持有的锁。线程t执行完毕后,当前线程进入就绪状态。
static native void sleep() 静态方法,线程睡眠,并让出CPU时间片
void wait() 当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒。
native void notify() 唤醒在此对象监视器上等待的单个线程,选择是任意性的。
native void notifyAll() 发送信号通知所有等待线程

1.2 线程安全

并发的相关性质:

  • 原子性:原子操作。对基本数据类型的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行。

  • 可见性:对于可见性,java提供了volatile关键字来保证可见性。

    当一个共享变量被volatile修饰时,他会保证修改的值会立即被更新到主存,当有其他线程需要读取时,他会去主存中读取新值。

    volatile 不保证原子性。

  • 有序性:Java允许编译器和处理器对指令重排序,但是重排序不会影响到单线程的执行,却会影响到多线程并发执行的正确性。

synchronized

使用对象头标记字实现

使用场景:

  • 修饰方法:

    一个对象中的加锁方法只允许一个线程访问。
  • 修饰静态方法:

    由于静态方法是类方法,所以多个线程不同对象访问这个静态方法,也是可以保证同步的。
  • 修饰代码块:

    如:synchronized(obj){...} 这里的obj可以是类中的一个属性,也可以是对象,这时他跟修饰普通方法一样,如果obj是Object.class这样的,那么效果跟修饰静态方法类似。

volatile

  1. 每次读取都强制从主内存刷数据
  2. 适用场景:单个线程写,多个线程读
  3. 原则:能不用就不用,不确定的时候也不用
  4. 语义
  • 可见性
  • 禁止指令重排序(不完全保证有序性)
  • 不能保证原子性。

为什么不保证有序性呢?举个例子说明

上述代码,语句1和2,不会被重排到3的后面,4和5也不会到3的前面。但是1和2的顺序、4和5的顺序无法保证。

final

final定义类型 说明
final class XXX 不允许继承
final 方法 不允许Override
final 局部变量 不允许修改
final 实例属性 构造函数、初始化块后不能变更。只能赋值一次。构造函数结束返回时,final域最新的值保证对其他线程可见。
final static 属性 静态块执行后不允许变更,只能赋值一次

二、线程池

  1. Executor :执行者 -顶层接口
  2. ExecutorService :继承于Executor,线程池的接口API
  3. ThreadFactory:线程工才
  4. Executors:工具类

submit方法和execute方法的区别:

  • submit方法:有返回值,用Future封装,执行的方法异常了可以在主线程里catch到。
  • execute方法:无返回值,方法执行异常是捕捉不到的

如下图:

ExecutorService主要方法:

构造线程池的参数

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

corePoolSize :核心线程数

maximumPoolSize:最大线程数

keepAliveTime:当线程数超过了核心线程数,空闲线程需要等待多久等待不到新任务就终止。

workQueue:任务队列

threadFactory:线程工厂

handler:拒绝策略

提交任务逻辑:

  1. 判断核心线程数
  2. 加入workQueue
  3. 判断最大线程数,没达到就创建
  4. 执行拒绝策略(默认是抛异常)

缓冲队列

  1. ArrayBlockingQueue:规定大小的BlockingQueue,构造时必须指定大小
  2. LinkedBlockingQueue:大小不固定的BlockingQueue,如果构造时指定大小,则有大小限制,不指定大小,则用Integer.MAX_VALUE来决定
  3. PriorityBlockingQueue:类似于LinkedBlockingQueue,不同在它根据对象的自然顺序或者构造函数的Comparator进行排序,不是FIFO
  4. SynchronizedQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成。

拒绝策略:

  1. AbortPolicy:丢弃任务并抛异常
  2. DiscardPolicy:丢弃任务,不抛异常
  3. DiscardOldestPolicy:丢弃队列最前面的任务,重新提交被拒绝的任务
  4. CallerRunsPolicy:由提交任务的线程处理该任务。

线程工厂(ThreadFactory):

自定义示例:

public class CustomThreadFactory implements ThreadFactory {

    private AtomicInteger count=new AtomicInteger();

    @Override
public Thread newThread(Runnable r) {
Thread thread=new Thread(r);
thread.setDaemon(false);
thread.setName("customThread-"+count.getAndIncrement());
return thread;
}
}

线程工具类:

  1. newSingleThreadExecutor

    创建一个单线程的线程池。如果这个线程因为异常结束,那么会有一个新的线程替代它。此线程池保证所有的任务的执行顺序按任务提交顺序执行。

  2. newFixedThreadPool

    创建固定大小的线程池。缺点:队列使用的LinkedBlockingQueue,且没有限制大小。

  3. newCachedThreadPool

    创建一个可缓存的队列,如果线程池大小超过了处理任务需要的线程,那么就会回收部分空闲线程。缺点:此线程池不会对线程池大小做限制。

  4. newScheduledThreadPool

    创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

创建固定线程池的经验:

假设服务器核心数为N

  1. 如果是CPU密集型应用,则线程池大小设置为N或N+1
  2. 如果是IO密集型应用,则线程池大小设置为2N或2N+2

Java必会之多线程的更多相关文章

  1. java必背面试题

    JAVA必背面试题和项目面试通关要点 一 数据库 1.常问数据库查询.修改(SQL查询包含筛选查询.聚合查询和链接查询和优化问题,手写SQL语句,例如四个球队比赛,用SQL显示所有比赛组合:举例2:选 ...

  2. 学习Java必看书籍和步骤(转载)

    原地址:http://blog.csdn.net/yongjian1092/article/details/7372678 Java语言基础 谈到Java语言基础学习的书籍,大家肯定会推荐Bruce ...

  3. 学习Java必看书籍和步骤

    Java语言基础  谈到Java语言基础学习的书籍,大家肯定会推荐Bruce Eckel的<ThinkinginJava>.它是一本写的相当深刻的技术书籍,Java语言基础部分基本没有其它 ...

  4. Java学习手记2——多线程

    一.线程的概念 CPU执行程序,就好比一个人在干事情一样,同一个时间你只能做一件事情,但是这样的效率实在是太低了,在你用电脑的时候,听歌就不能浏览网页,看电影就不能下载视频,你想想是不是很蛋疼. 所以 ...

  5. java 并发性和多线程 -- 读感 (一 线程的基本概念部分)

    1.目录略览      线程的基本概念:介绍线程的优点,代价,并发编程的模型.如何创建运行java 线程.      线程间通讯的机制:竞态条件与临界区,线程安全和共享资源与不可变性.java内存模型 ...

  6. Java 并发性和多线程

    一.介绍 在过去单 CPU 时代,单任务在一个时间点只能执行单一程序.之后发展到多任务阶段,计算机能在同一时间点并行执行多任务或多进程.虽然并不是真正意义上的“同一时间点”,而是多个任务或进程共享一个 ...

  7. Java 并发和多线程(一) Java并发性和多线程介绍[转]

    作者:Jakob Jenkov 译者:Simon-SZ  校对:方腾飞 http://tutorials.jenkov.com/java-concurrency/index.html 在过去单CPU时 ...

  8. Java核心知识点学习----多线程中的阻塞队列,ArrayBlockingQueue介绍

    1.什么是阻塞队列? 所谓队列,遵循的是先进先出原则(FIFO),阻塞队列,即是数据共享时,A在写数据时,B想读同一数据,那么就将发生阻塞了. 看一下线程的四种状态,首先是新创建一个线程,然后,通过s ...

  9. Java并发性和多线程

    Java并发性和多线程介绍   java并发性和多线程介绍: 单个程序内运行多个线程,多任务并发运行 多线程优点: 高效运行,多组件并行.读->操作->写: 程序设计的简单性,遇到多问题, ...

随机推荐

  1. 这个世界上只有一个你之Java设计模式:单例模式

    目录 单例模式的要点 单例模式的特点 饿汉式单例类 懒汉式单例类 一:懒汉式,线程不安全 二:懒汉式,线程安全 三:懒汉式,线程不安全 四:懒汉式,双检锁/双重校验锁 五:懒汉式,静态内部类 六:懒汉 ...

  2. 页面元素定位 - XPath

    1. XPath 简介 2. 选取节点 2.1 选取节点表达式 2.2 XPath 运算符 2.3 XPath 常用函数 2.4 亲属关系匹配 2.5 *综合示例 1. XPath 简介 什么是 XP ...

  3. Typora 主题推荐

    Typora 修改代码块高亮样式可以参考:点击这里 下面推荐按几款比较好看的主题样式 1.cobalt主题 2.Drake主题 3.fluent主题 4.gitbook主题 5.techo主题 6.U ...

  4. 通读《构建之法》与CI/CD工具尝试

    项目 内容 这个作业属于哪个课程 2021春季软件工程(罗杰 任健) 这个作业的要求在哪里 作业要求 我在这个课程的目标是 积累软件开发经验,提高工程能力 这个作业在哪个具体方面帮助我实现目标 通读课 ...

  5. hdu4604 不错的子序列问题

    题意:       给你一个栈,里面有n个数,和一个双头队列(空的),每次从栈里拿出一个数据,有三种选择,可以选择丢弃这个数字,也可以放到队头或者队尾,最后问你这个队列你面的最长连续非下降序列的长度. ...

  6. LA4636积木艺术

    题意:       有一些1*1*1的单位正方体积木,现在要摆积木,每一块积木有两种方法,要么放在地面上,要么放在别的积木的正上方,现在给你摆好积木的正面图和侧面图,问你最少用了多少块积木. 思路: ...

  7. 堆栈上的舞蹈之释放重引用(UAF) 漏洞原理实验分析

    0x01 前言 释放重引用的英文名名称是 Use After Free,也就是著名的 UAF 漏洞的全称.从字面意思可以看出 After Free 就是释放后的内存空间,Use 就是使用的意思,使用释 ...

  8. Spring Boot & Cloud 轻量替代框架 Solon 1.3.37 发布

    Solon 是一个微型的Java开发框架.强调,克制 + 简洁 + 开放的原则:力求,更小.更快.更自由的体验.支持:RPC.REST API.MVC.Micro service.WebSocket. ...

  9. Mybatis学习之自定义持久层框架(二) 自定义持久层框架设计思路

    前言 上一篇文章讲到了JDBC的基本用法及其问题所在,并提出了使用Mybatis的好处,那么今天这篇文章就来说一下该如何设计一个类似Mybatis这样的持久层框架(暂时只讲思路,具体的代码编写工作从下 ...

  10. Truncate用法详解

    前言: 当我们想要清空某张表时,往往会使用truncate语句.大多时候我们只关心能否满足需求,而不去想这类语句的使用场景及注意事项.本篇文章主要介绍truncate语句的使用方法及注意事项. 1.t ...