多线程

1. 多线程基础

多线程状态转换图

普通方法介绍
yeild

yeild,线程让步。是当前线程执行完后所有线程又统一回到同一起跑线。让自己或者其他线程运行,并不是单纯的让给其他线程。

join

等待线程结束;调用线程等待当前线程结束后才能往下执行,阻塞线程之意。join本质是在当前对象实例上调用线程wait()

如下所示:输出完 thread-1后再输出end

public static void main(String[] args) throws InterruptedException{
System.out.println("main start"); Thread t1 = new Thread(new Worker("thread-1"));
t1.start();
t1.join();
System.out.println("main end");
}
sychronized

sychronized确保安全外,还能保证线程之间的可见性和有序性

  1. 指定加锁对象:对给定的对象加锁,进入同步代码前要先获得给定对象的锁
  2. 直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要先获得当前实例的锁。
  3. 直接作用于静态方法:相当于对当前类加锁,进入同步代码前要先获得当前类的锁。

2. JDK并发包

重入锁(ReentrantLock)

决定了线程是否可以访问临界区资源。object.wait()和object.notify()起到线程等待和通知的作用。这些工具能实现多线程相互之间的协作作用

sychronized功能的扩展-重入锁

重入锁:

重入锁使用ReentrantLock来实现

public class ReentrantLocker implements Runnable {

    public static ReentrantLock locker = new ReentrantLock();

    private static int i = 0;

    @Override
public void run() { for (int j = 0; j < 1000; j++) {
locker.lock();
try {
i++;
} catch (Exception e) {
e.printStackTrace();
}finally {
locker.unlock();
}
}
} public static void main(String[] args) throws Exception{
ReentrantLocker locker1 = new ReentrantLocker();
// 对同一个对象加锁。important
Thread t1 = new Thread(locker1);
Thread t2 = new Thread(locker1);
t1.start();
t2.start();
/**调用join方法,阻塞当前线程,待所有线程执行完再往下执行*/
t1.join();
t2.join();
System.out.println(i);
}
}
重入锁对比sychronized有点
  1. 中断响应

    对于sychronized来说,一个线程要访问被同步的资源,要么继续执行,要么继续等待。而使用重入锁,线程可以被中断。程序可以取消对锁的请求,避免死锁的发生。
  2. 锁超时设置
//表示线程对资源锁等待时长。
//如果在规定的时间内没有获取到锁,
//则线程获取锁失败
lock.tryLock(5, TimeUnits.SECONDS)

如果tryLock没加参数,就不需要等待。

3. 公平性

公平锁会按照时间先后顺序,保证先到先得,后到者后得。公平锁的一大特点是:不会产生饥饿现象,只要你排队,最后还是能得到资源的

重入锁条件-Condition
信号量(Semaphore)

信号量是对锁的扩展,无论是内部锁sychronized还是重入锁ReentrantLock,一次都只允许一个线程访问一个资源,而信号量指定多个线程同时访问同一资源

  1. 构造函数
public Semaphore(int permits);
public Semahore(int permits, boolean fair);

构造信号量对象时,permits参数表示的是:同时多少个线程能访问同一资源。

读写锁(ReadWriteLock)

读写分离锁可以有效的较少锁竞争,用锁分离机制来提升系统性能。

读写锁的访问约束情况

--
非阻塞 阻塞
阻塞 阻塞

倒计时器(CountDownLatch)

可以让某一线程等待直到倒计时结束,再开始执行。当调用wait方法的时候,主线程被阻塞,直到countdown减到0的时候,程序再往下执行。

循环栅栏(CyclicBarrier)

作用是阻止线程继续执行,要求线程在栅栏处等待,计数器可以循环使用。假如将计数器设置为10,那么凑齐第一批10个进程数后计数器会归为0,然后接着凑齐下一批10个线程

线程池

声明一个线程池

线程池的作用是复用线程,减少系统开销。声明一个线程池有如下方法

/**
该方法返回一个固定线程数量的线程池。
当有一个新的任务提交,线程池有空闲的线程就提交,没有空闲的话就缓存到待执行列表中,
直到有空闲的线程才执行
*/
public ExecutorService newFixedThreadService(int threadNum);
/**
该方法值返回包含一个线程的线程池,
如果有多个任务提交也是将多余的任务保存到一个任务队列中,待有空闲的线程才执行
**/
public ExecutorService newSingleThreadExecutor();
/**
返回可根据实际情况调整线程数量的线程池,线程池中的线程数量是不一定的,但还是能够复用空闲线程的
*/
public ExecutorService newCachedThreadPool();
线程池的内部实现

通过源代码可以看出newFixedThreadServicenewSingleThreadExecutor,newCachedThreadPool内部都是由ThreadPoolExecutor来处理的

ThreadPoolExecutor的内部实现

public ThreadPoolExecutor(
// 线程池中线程数量
int corePoolSize,
// 线程池中允许线程最大数量
int maximumPoolSize,
// 线程池中线程数量超出corePoolSize的线程的存活时间
long keepAliveTime,
// 时间单位
TimeUnit unit,
// 任务队列,被提交尚未被执行的任务
BlockingQueue<Runnable> workQueue,
// 拒绝策略,任务太多来不及处理,如何拒绝任务
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}

其中workqueue是指那些尚未被执行的线程队列。它是BlockingQueue的接口对象。在ThreadPoolExecutor中可以使用如下几种 BlockQueue

  • 直接提交的队列
  • 有界的任务队列
  • 无界任务队列
  • 优先任务队列

ThreadLocal

为每个线程运行时存储所需参数。

如下是线程安全的日期格式化方法

public class SafeDataFormat {
static ThreadLocal<SimpleDateFormat> simpleDateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>();
static Date date = new Date();
static String dateStr = "2019-04-27 04:12:41";
static class ParseDate implements Runnable { @Override
public void run() {
if (null == simpleDateFormatThreadLocal.get()) {
simpleDateFormatThreadLocal.set(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"));
}
SimpleDateFormat simpleDateFormat = simpleDateFormatThreadLocal.get();
try {
System.out.println(simpleDateFormat.parse(dateStr)); } catch (Exception e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 2000; i++) {
executorService.execute(new ParseDate());
} executorService.shutdown();
}
}
ThreadLocal实现原理

先看ThreadLocal的get(), set()方法。

set方法
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

从代码中可以看出首先获取当前线程对象,然后通过getMap()拿到当前线程的ThreadLocalMap,并将值更新到这个map中。

get方法
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}

首先get()获取到当前对象的ThreadLocalMap对象,然后将当前线程作为key来获取内部的实际数据。

这些变量是维护在Thread内部的(ThreadLocalMap定义所在类),意味着只要线程不退出,对象的引用就一直都存在着。

ThreadLocalMap的实现使用了弱引用,java虚拟机在进行垃圾回收时,如果发现变量是弱引用,就会立即回收

CAS 比较交换 CompareAndSweep

由于其的非阻塞性,它对死锁天生免疫。

CAS的算法过程

包含三个参数CAS(V, E, N)。V表示要更新的变量,E表示预期值,N表示新值。仅当 V = E时,才会将V设置成N;如果V值跟E值不同,则说明已经被其他线程醉了更新,当前线程什么都不做,最后CAS返回当前V的真实值

CAS操作是抱着乐观的态度进行的,它总认为自己可以完成操作。当多个线程同时CAS同一个值额时候,只有一个线程会胜出并成功更新。失败的线程不会被挂起,仅是被告知操作失败,并允许再次尝试

简单的说,CAS需要额外给出期望值,也就是你认为这个变量最终的样子。如果这个变量最终跟你的预期不同,你就重新读取再次更新就好了。

AtomicInteger

是可变、线程安全的。基于CAS的;就内部实现来说,AtomicIntger保存了核心字段

//代表了当前AtomicInteger的实际值
private volatile int value;
// 保存着value字段在AtomicInteger对象的偏移量。实现AtomicInteger的关键
private static final long valueOffset;

关注一下incrementAndGet()

public final int incrementAndGet() ;
for(; ; )
int v current = get();
int next = cueernt + 1;
if (compareAndSet(current, next))
return next

CAS操作未必是成功的,因此对于不成功的情况,要不断的去重试

Java多线程知识整理的更多相关文章

  1. JAVA多线程知识总结(二)

    本文是承接上一篇文章:JAVA多线程知识总结(一) 四.Java多线程的阻塞状态与线程控制  上文已经提到线程阻塞的集中具体类型.下面主要看引起JAVA线程阻塞的方法 1,join()-----让一个 ...

  2. 2019-9-16 java上课知识整理总结(动手动脑,课后实验)

    java上课知识整理总结(动手动脑,课后实验) 一,课堂测试 1,题目:课堂测试:像二柱子那样,花二十分钟写一个能自动生成30道小学四则运算题目的 “软件” 要求:(1)题目避免重复: (2)可定制( ...

  3. JAVA 多线程知识总结(一)

    一,线程的生命周期以及五种基本状态 关于JAVA线程的生命周期,首先看一下下面这张图 上图中基本上囊括了Java中多线程各重要知识点.掌握了上图中的各知识点,Java中的多线程也就基本上掌握了. Ja ...

  4. 阿里 P8 高级架构师吐血总结的 《Java 核心知识整理&面试.pdf》| 免费分享

    最近在网上发现一份非常棒的 PDF 资料,据说是阿里 P8 级高级架构师吐血总结的, 其中内容覆盖很广,包括 Java 核心基础.Java 多线程.高并发.Spring.微服务.Netty 与 RPC ...

  5. Java并发知识整理

    整理了一下前段时间学习Java并发的笔记,大约有40篇. 1. Java并发基础知识 并发基础(一) 线程介绍 并发基础(二) Thread类的API总结 并发基础(三) java线程优先级 并发基础 ...

  6. JAVA hashmap知识整理

    HashMap和Hashtable的比较是Java面试中的常见问题,用来考验程序员是否能够正确使用集合类以及是否可以随机应变使用多种思路解决问题.HashMap的工作原理.ArrayList与Vect ...

  7. 面试求职中需要了解的Java多线程知识

    Java中多线程的实现方式 在java的历史版本中,有两种创建多线程程序的方法 1) 通过创建Thread类的子类来实现(Thread类提供了主线程调用其它线程并行运行的机制) 主要步骤: 自定义类继 ...

  8. java基础知识整理

    java基础入门知识(转载请注明出处.) 1.JVM.JRE和JDK的区别. (1)JVM(Java Virtual Machine):java虚拟机,用于保证java跨平台的特性,java语言是跨平 ...

  9. Java多线程知识总结(一)

    一.创建线程的三种方式: 创建线程的方式有三种,一是创建Thread实例,二是实现Runnable接口,三是实现Callable接口,Runnable接口和Callable接口的区别是一个无返回值,一 ...

随机推荐

  1. SSRS报表服务随笔(rdl报表服务)-创建一个简单的报表

    这段时间一直在敲rdl报表,在国内的不这么留在,在国外的话,还是挺流行的,国内的话,这方面的资料很少很少,也踏过不少坑 先从SSRS了解起,SSRS全称 SQL Server Reporting Se ...

  2. Android开发:文本控件详解——EditText(一)基本属性

    一.简单实例: EditText输入的文字样式部分的属性,基本都是和TextView中的属性一样. 除此之外,EditText还有自己独有的属性. 二.基本属性: hint  输入框显示的提示文本  ...

  3. Python猫荐书系列之五:Python高性能编程

    稍微关心编程语言的使用趋势的人都知道,最近几年,国内最火的两种语言非 Python 与 Go 莫属,于是,隔三差五就会有人问:这两种语言谁更厉害/好找工作/高工资…… 对于编程语言的争论,就是猿界的生 ...

  4. c#在pictureBox控件上绘制多个矩形框及删除绘制的矩形框

    在pictureBox上每次只绘制一个矩形框,绘制下一个矩形框时上次绘制的矩形框取消,代码如链接:https://www.cnblogs.com/luxiao/p/5625196.html 在绘制矩形 ...

  5. NET微信公众号开发环境搭建(一)-了解微信由来

    公众号的应用,开发及调试环境搭建 花生壳要注册 需要二十多块钱 ,还要实名认证,估计要一两天才能审核通过  主要就是在 windows搭建测试环境   1.微信的应用场景   360百科微信简介 ht ...

  6. 关于数据库管理系统DBMS--关系型数据库(MySQL/MariaDB)

    数据库的结构(3种):层次,网状,关系型(用的最多): DBMS的三层模型: 视图层:面向最终用户: 逻辑层:面向程序员或DBA: 物理层:面向系统管理员: 关系型数据库管理系统——RDBMS: 主要 ...

  7. 瑞芯微发布最新旗舰应用处理器-RK3588

    在瑞芯微电子第四届“开发者之春”大会上,瑞芯微推出了新一代8nm旗舰处理器-RK3588 这个芯片将采用8nm 制程工艺.基于A76+A55 内核组合,具备4K UI性能.8K VPU,拥有NPU2. ...

  8. Linux(CentOS 7)安装测试svn服务

    1.yum install subversion,通过yum安装svn服务 2.svnserve --version,查看是否安装成功 3.mkdir -p /home.svn,创建svn仓库目录 4 ...

  9. spring Boot环境下dubbo+zookeeper的一个基础讲解与示例

    一,学习背景 1.   前言 对于我们不管工作还是生活中,需要或者想去学习一些东西的时候,大致都考虑几点: a)      我们为什么需要学习这个东西? b)     这个东西是什么? c)      ...

  10. 搭建环境-Monkeyrunner-自动化测试工具

    这篇博客帮助挺大,我补充部分,帮助同样的小白哈哈,侵删 https://www.cnblogs.com/lynn-li/p/5885001.html 1.前期准备 需要安装:JDK,SDK,pytho ...