Java多线程笔记[未更新完]
最近课上可摸鱼时间较多,因此并发开坑学习
本篇学习自Java多线程编程实战指南
目前进展:刚开坑,处于理解概念阶段
本篇学习自Java多线程编程实战指南
Q.进程和线程的区别
进程Process是程序运行的实例,在Java的范畴中,运行一个Java程序的实质是启动一个JVM进程,也就是一个运行的Java程序就是一个Java虚拟机进程(Java Web服务器是一个进程同时运行多个Java Web应用)
进程是程序向操作系统申请资源(内存空间、文件句柄等)的基本单位,而线程Thread是进程中可独立运行的最小单位,一个进程可以包含多个线程,同一个进程中的所有线程共享该进程的资源,
Q.extends Thread 和 Runnable的区别
1.面向对象来看,前者是继承实现,后者是组合实现,因此后者有更低的耦合程度
2.从对象共享来看,Runnable意味着多个线程可共享同一个Runnable实例
一种具有迷惑性的例子,在例子中两种非空构造的Thread构造都可以有一致的hashCode的原因是构造声明是Thread(Runnable),而Thread的JDK声明是public class Thread implements Runnable,后两种例子都是体现Runnable的特性而非区分两者
class XjbThreadImpl implements Runnable {
    public void run() {
        System.out.println("Implements: "+this.hashCode());
    }
}
class XjbThreadExt extends Thread {
    public void run() {
        System.out.println("Extends: "+this.hashCode());
    }
}
//
public class Main {
   public static void main(String[] args) {
        for(int i = 0; i < 2; i++) {
            new Thread(new XjbThreadImpl()).start();
            new Thread(new XjbThreadExt()).start();
        }
        // 使用时请分别注释上下两部分
        Runnable tmpImpl = new XjbThreadImpl();
        Thread impl0 = new Thread(tmpImpl);
        Thread impl1 = new Thread(tmpImpl);
        Thread tmpExt = new XjbThreadExt();
        Thread ext0 = new Thread(tmpExt);
        Thread ext1 = new Thread(tmpExt);
        impl0.start();
        impl1.start(); //hashCode一致
        ext0.start();
        ext1.start(); //hashCode一致
   }
}
3.从创建成本来看,创建线程实例比Runnable成本更高,JVM需要为它分配调用栈空间、内核线程等资源
Java的线程分为守护线程(Daemon)和用户线程,前者用于执行重要性不高的任务,后者反之,且只有当所有用户进程结束后JVM才会正常停止(System.exit调用除外)
Thread的方法
.join() 等待相应进程结束/ yield()当前线程主动放弃处理器的占用
Q.线程的生命周期状态
NEW 已创建而为启动的线程,线程只有一次机会处于该状态
RUNNABLE 包括两个自状态READY和RUNNING,前者表示处于改状态的线程可以被Scheduler调度而处于RUNNING。后者表示正在运行(即run正在被处理器执行),可以使用yield()返回到READY。(活跃子状态)
BLOCKED 线程发起阻塞式IO或申请锁(被其他线程占有)时处于该状态,该状态不占用处理器资源。当完成阻塞式IO操作或获得锁时重新回到RUNNALE
WAITING 线程执行特定方法后处于等待其他线程执行另外特定操作的状态。比如Object.wait(),Thread.join(),LockSupport.park(Object)可以获得该状态,返回RUNNABLE的方法有Object.notify()/notifyAll(),LockSupport.unpark(Object)
TIMED_WAITING有时间限制的等待状态,如Thread.sleep(long),Object.wait(long),超时自动返回RUNNABLE
TERMINATED 已经执行结束的进程处于该状态(只有一次),既Thread.run()调用结束、
Q.并发与并行的区别
并行Parallel多个线程在同一时刻处理任务
并发Concurrent单个线程交替的完成不同任务
(书里说的还是不太明确,下次查查别的资料)
竞态指计算的正确性依赖相对时间顺序或者线程的交错,容易产生脏读问题(读取到一个过世的数据、丢失更新)
竞态的模式:read-modify-write(比如a++)和check-then-act(比如对变量的if-else),注意这些都是对于共享变量而言的,局部变量(如形参)和synchronized不会造成竞态
PS.提一下a++的过程,1.read:将变量a的值从内存读到寄存器r中,2.将寄存器r的值+1,3.将寄存器r的内容写入变量a对应的内存空间
如果某线程在执行到第二步的时候变量a的值已经更新了,那它就是脏读(旧数据),接着到第三部覆盖到内存空间的操作就是丢失更新
解决竞态:上锁/局部变量 (Q.有没有更好的方法?
竞态不一定会造成错误结果,拿上面的模式来说,如果是交错执行read(Thread-0)-read(Thread-1)或者check(Thread-0)-check(Thread-1),很显然结果依然正确
Synchronized使修饰的方法在任一时刻只能被一个线程执行
Q.什么是线程安全
如果一个类在单线程环境下额能够正常运行,并且在多线程环境下,使用方不必为其做任何改变的情况下也能运行正常,那就称其线程安全
线程安全表现在原子性、可见性和有序性
原子性:说白了就是“不可分割”,细分为两层含义
1.对于涉及共享变量的操作,在操作以外的线程看来是不可分割的,那就是原子操作(尽管对于正在执行的线程来说是分很多步骤,但在外界看来该操作要么尚未开始,要么已经结束)
2.访问同一组共享变量的非只读原子操作是不能被线程交错执行的
原子性只在多线程以及线程共享的变量以下讨论才有意义
实现原子性的两种Solutions:1.Lock软件实现锁 2.CAS硬件实现锁
Java语言规范中规定6种基础类型的变量和引用变量的写操作都是原子性的
(long/double除外,原因是32位的JVM对64位的写操作可能会分解成低32和高32处理,还是有冲突的可能,好消息是volatile下是确保原子性的)
PS.关于读写的原子性和volatile的关系
1.volatile只保证变量写操作的原子性,不保证两种竞态模式的原子性
2.Java针对任何变量的读操作均符合原子性
Q.你tm还记得哪8种基础类型吗
A.byte boolean short char int long float double
可见性:多线程环境下,一个线程对某个共享变量进行更新后,后续访问该变量的县城可能无法立刻读取该更新的结果(可能永远读取不了),读取不了则是不可见
问题产生的可能:JIT优化对多线程造成不利(书里提了个循环不变式外提的优化,水平太菜不敢评论)、共享变量分配到寄存器(两个线程运行在不同处理器上,一个处理器无法读取另一个处理器的寄存器)等
保证可见性:volatile
volatile作用之一是提示JIT编译器被修饰的变量可能被多个线程共享,阻止不恰当的优化
另一作用是读取volatile关键字修饰的变量会使相应的处理器执行刷新处理器缓存的动作(停留在写缓冲器是不可见的,要求被写入到高速缓存或主内存)
可见性只保证线程能读取到相对新值(更新后其他线程能得到更新后的值),不保证最新值(读取该变量的线程在读取使用时其它线程无法更新,该线程读取到的相对新值为最新值)
PS.单处理器也会存在可见性问题
虽然寄存器都在同一处理器中,但多线程是通过时间片 分配实现的,在上下文切换的过程中,一个线程对寄存器变量的修改会被线程上下文保存,另一线程无法得知该修改
有序性:(玄学
volatile的另一作用是禁止重排序,保证有序性
PS.我用不大准确的词语归纳一下
原子性:线程之间是否能任意穿插
可见性:线程执行中是否时刻得知更新
上下文切换:多个线程共享同一个处理器,不同线程被选中开始或继续执行实现并发,切入切出过程中需要保存和恢复相应进程的进度信息(执行到哪一条指令)称为上下文,一般包括通用寄存器和程序计数器的内容
在Java的角度来看,从RUNNABLE到非RUNNABLE状态的切换过程就是一个上下文切换(暂停or恢复)
自发性上下文切换可从线程的状态转移图(各种方法、锁、IO)得出
非自发性上下文切换由线程调度器引起,比如GC过程对JVM堆进行整理时要求停止所有应用线程
然而,上下文切换是需要开销的,因此并非线程越多越厉害,计算效率可能更加惨淡
一些活性故障的例子:
死锁:不解释
锁死:拿到锁的线程跑路了,资源永远不释放
活锁:线程处于RUNNABLE但执行的任务没有进展(带薪摸鱼)
饥饿:线程无法获得所需资源而无法执行任务
非公平调度优点:吞吐率大、减少上下文切换次数(公平调度会带来更多的暂停和唤醒);缺点:申请资源的时间偏差大、可能导致饥饿,一般首选非公平调度
锁机制的思路:将多个线程对共享数据的并发访问转换为串行访问
JVM对锁的划分有内部锁(Synchronized实现)和显式锁(实现Lock接口的ReentrantLock类实现)
锁的作用是保障线程安全,体现在上面提到的三个方面
1.通过互斥实现原子性,对外在线程来说是不可分割的(因为无法获得锁)
2.锁的获得隐含刷新处理器缓存的动作,使得读线程在临界区前将写线程对共享变量的更新同步到该线程执行处理器的高速缓冲中(可见性+原子性保证了最新值)
3.写线程在临界区执行的操作在读线程所执行的临界区看起来是按源代码顺序执行的(原子性保证读线程无法区分具体顺序,还和可见性保证了最新值,因此感知上是按顺序执行的)
last update:19/03/26
Java多线程笔记[未更新完]的更多相关文章
- 这份java多线程笔记,你真得好好看看,我还没见过总结的这么全面的
		
1.线程,进程和多线程 1.程序:指指令和数据的有序集合,其本身没有任何意义,是一个静态的概念 2.进程:指执行程序的一次执行过程,是一个动态的概念.是系统资源分配的单位(注意:很多多线程是模拟出来的 ...
 - Java多线程_复习(更新中!!)
		
java多线程的常见例子 一.相关知识: Java多线程程序设计到的知识: (一)对同一个数量进行操作 (二)对同一个对象进行操作 (三)回调方法使用 (四)线程同步,死锁问题 (五)线程通信 等等 ...
 - Java并发笔记-未完待续待详解
		
为什么需要并行? – 业务要求 – 性能 并行计算还出于业务模型的需要 – 并不是为了提高系统性能,而是确实在业务上需要多个执行单元. – 比如HTTP服务器,为每一个Socket连接新建一个处理线程 ...
 - Java学习笔记(未完待续)
		
变量的作用域(scope)是指变量可以在程序中引用的范围.在方法中定义的变量称为局部变量(local variable).局部变量的作用域从声明变量的地方开始,直到包含该变量的块结束为止.局部变量都必 ...
 - Java多线程笔记总结
		
1.线程的三种创建方式 对比三种方式: 通过继承Thread类实现 通过实现Runnable接口 实现Callable接口 第1种方式无法继承其他类,第2,3种可以继承其他类: 第2,3种方式多线程可 ...
 - Java 多线程   笔记  转自http://www.cnblogs.com/lwbqqyumidi/p/3804883.html
		
多线程作为Java中很重要的一个知识点, 一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的图: 上图中基本上囊括了Java中多线程各重要知识点.掌握了上图中 ...
 - Java 多线程笔记
		
资料来源于网络,仅供参考学习. 1.A Java program ends when all its threads finish (more specifically, when all its ...
 - java数据库编程(未整理完,待续)
		
java使用数据库可以借助jdbc这个中间媒介.本文将介绍如何使用jdbc连接数据库,数据库的基本操作和jdbc的事物处理. 1 连接数据库 一般java连接数据库,都有几个步骤: 0.导入相应的驱动 ...
 - JAVA复习笔记03(完)
		
31.类中可定义接口 一个定义接口的java文件中最多有1个Public的接口 32.TreeMap 按照键值升序排序 LinkedHashMap 按照插入顺序排序 Map的操作: Map<in ...
 
随机推荐
- JSP中的一个树型结构
			
看方力勋的javaWeb,采用左右值来表示树型结构(就是俺门的多级分类)表结构 页面代码 <%@ page language="java" import="java ...
 - kubernetes 1.6 RBAC访问控制
			
一.简介 之前,Kubernetes中的授权策略主要是ABAC(Attribute-Based Access Control).对于ABAC,Kubernetes在实现上是比较难用的,而且需要Mast ...
 - Zookeeper客户端cli_st为何在crontab中运行不正常?
			
实践中,发现直接在命令行终端运行cli_st时,能够得到预期的结果,但一将它放到crontab中,则只收到: bye 相关的一段clit_st源代码如下: if (FD_ISSET(, &rf ...
 - xib下这种方式创建cell
			
这种方法在iOS5.0之前是不能够创建成功的. MEConvertListTableViewCell *cell = [tableView dequeueReusableCellWithIde ...
 - [翻译] FastReport Class Hierarchy (FastReport 组件类层次结构)
			
"TfrxComponent" is the base class for all FastReport components. Objects of this type have ...
 - zookeeper zoo.cfg配置文件
			
一.zookeeper的配置文件 zoo.cfg 配置文件是我们安装zookeeper的时候复制 重命名出来的文件 命令: cp zoo_smaple.cfg zoo.cfg zkSe ...
 - Sqlserver 密码过期时间查询
			
DECLARE @login nvarchar(30) -- 查询设定密码过期的登陆账号SELECT @login = nameFROM sys.sql_loginsWHERE is_expirati ...
 - jmeter分布式环境
			
搭建jmeter分布式环境 (1)确定分布式结构,即1台机器部署master.几台机器部署slave? (2)将相同版本的jmeter分别拷贝到这几台机器 (3)修改maste ...
 - Visual Studio Code 基本操作 - Windows 版
			
1.Install the .NET SDK 2.Create app: dotnet new console -o myApp cd myApp 3.Run your app:dotnet run
 - sql 存储过程带有模糊查询条件
			
一个简单的存储过程: Create procedure [dbo].[Proc_SeachJob] (@startRecordIndex int, @endRecordIndex int, @seac ...