java并发编程(1):Java多线程-基本线程类-基础知识复习笔记
复习资料:《同步与异步:并发/并行/进程/线程/多cpu/多核/超线程/管程 》
基本线程类
基本线程类
基本线程类指的是Thread类,Runnable接口,Callable接口
继承Thread创建线程
继承java.lang.Thread类创建线程是最简单的一种方法,也最直接。
public class MyThread1 extends Thread {}
种创建方式,把线程执行的逻辑代码直接写在了Thread的子类中,这样根据线程的概念模型,虚拟CPU和代码混合在一起了。并且java是单继承机制,线程体继承Thread类后,就不能继承其他类了,线程的扩展受影响。
实现Runnable接口创建线程
为了构建结构清晰线程程序,可以把代码独立出来形成线程目标对象,然后传给Thread对象。通常,实现Runnable接口的类创建的对象,称作线程的目标对象。
public class MyThreadTarget implements Runnable{ }
出线程目标对象和Thread分开了,并传递给了Thread。如果有比较复杂的数据要处理,可以在线程目标对象中引入数据。使用这种方式获得线程的名字就稍微复杂一些,需要使用到Thread中的静态方法,获得当前线程对象,然后再调用getName()方法。这种方式在较复杂的程序中用得比较普遍。
Callable
future模式:并发模式的一种,可以有两种形式,即无阻塞和阻塞,分别是isDone和get。其中Future对象用来存放该线程的返回值以及状态
ExecutorService e = Executors.newFixedThreadPool(3);
//submit方法有多重参数版本,及支持callable也能够支持runnable接口类型.
Future future = e.submit(new myCallable());
future.isDone() //return true,false 无阻塞
future.get() // return 返回值,阻塞直到该线程运行结束
以上是基本方法,总结一下
多线程的基本操作
创建线程的方式
将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法
声明实现 Runnable 接口的类。该类然后实现 run 方法
推荐方式二,因为接口方式比继承方式更灵活,也减少程序间的耦合。
线程API一览
start() 使线程处于就绪状态,Java虚拟机会调用该线程的run方法;
stop() 停止线程,已过时,存在不安全性:
一是可能请理性的工作得不得完成;
二是可能对锁定的对象进行“解锁”,导致数据不同步不一致的情况。
推荐 使用 interrupt() +抛异常 中断线程。
suspend() 暂停线程,已过时。
resume() 恢复线程,已过时。
suspend 与resume 不建议使用,存在缺陷:
一是可能独占同步对象;
二是导致数据不一致。
yield() 放弃当前线程的CPU资源。放弃时间不确认,也有可能刚刚放弃又获得CPU资源。
t.join() 等待该线程t 销毁终止。
获取当前线程信息
Thread.currentThread()
线程的分类
线程分为守护线程、用户线程。线程初始化默认为用户线程。
setDaemon(true) 将该线程标记为守护线程或用户线程。
特性:设置守护线程,会作为进程的守护者,如果进程内没有其他非守护线程,那么守护线程也会被销毁,即使可能线程内没有运行结束。
某线程a 中启动另外一个线程t,那么我们称线程t是线程a的一个子线程,而 线程a是线程t的父线程。
最典型的就是我们在main方法中 启动一个线程去执行。其中main方法隐含的main线程为父线程。
线程安全
线程安全就是每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的。
线程安全就是说多线程访问同一代码,不会产生不确定的结果。
反过来,线程不安全就意味着线程的调度顺序会影响最终结果,如不加事务的转账代码:
线程安全问题多是由全局变量和静态变量引起的
当多个线程对共享数据只执行读操作,不执行写操作时,一般是线程安全的;
当多个线程都执行写操作时,需要考虑线程同步来解决线程安全问题。
线程同步
同步是保证多个线程之间共享同一个全局变量数据安全问题,保证数据原子性。
多个线程操作一个资源的情况下,导致资源数据前后不一致。这样就需要协调线程的调度,即线程同步。 解决多个线程使用共通资源的方法是:线程操作资源时独占资源,其他线程不能访问资源。使用锁可以保证在某一代码段上只有一条线程访问共用资源。
有两种方式实现线程同步:
synchronized,使用同步代码块解决线程安全问题
同步锁(Lock)
Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。如上面的代码简单加入@synchronized关键字。在保证结果准确的同时,提高性能,才是优秀的程序。线程安全的优先级高于性能。
线程状态
synchronized关键字用法
当它用来修饰一个方法或者一个代码块的时候(同步代码块:将可能会发生线程安全问题的代码给包裹起来),能够保证在同一时刻最多只有一个线程执行该段代码。
原子性(互斥性):实现多线程的同步机制,使得锁内代码的运行必需先获得对应的锁,运行完后自动释放对应的锁。
内存可见性:在同一锁情况下,synchronized锁内代码保证变量的可见性。
可重入性:当一个线程获取一个对象的锁,再次请求该对象的锁时是可以再次获取该对象的锁的。
如果在synchronized锁内发生异常,锁会被释放。
synchronized方法 与 synchronized(this) 代码块 锁定的都是当前对象,不同的只是同步代码的范围
synchronized (非this对象x) 将对象x本身作为“对象监视器”:
多个线程同时执行 synchronized(x) 代码块,呈现同步效果。
当其他线程同时执行对象x里面的 synchronized方法时,呈现同步效果。
当其他线程同时执行对象x里面的 synchronized(this)方法时,呈现同步效果。
静态synchronized方法 与 synchronized(calss)代码块 锁定的都是Class锁。Class 锁与 对象锁 不是同一个锁,两者同时使用情况可能呈异步效果。
尽量不使用 synchronized(string),是因为string的实际锁为string的常量池对象,多个值相同的string对象可能持有同一个锁。
synchronized原理:
首先有一个线程已经拿到了锁,其他线程已经有cup执行权,一直排队,等待释放锁。
锁是在什么时候释放?代码执行完毕或者程序抛出异常都会被释放掉。
锁已经被释放掉的话,其他线程开始进行抢锁(资源竞争),谁抢到谁进入同步中去,其他线程继续等待。
效率非常低,多个线程需要判断锁,比较消耗资源,抢锁的资源。——synchronized 只能有一个线程进行执行(就像厕所只有一个坑,好多人等着上厕所,谁进入厕所谁上锁,其他人在外等待),这个线程不释放锁的话,其他线程就一直等,就会产生死锁问题。
volatile关键字用法
多线程的内存模型:main memory(主存)、working memory(线程栈),在处理数据时,线程会把值从主存load到本地栈,完成操作后再save回去(volatile关键词的作用:每次针对该变量的操作都激发一次load and save)。
Volatile 关键字的作用是变量在多个线程之间可见,但不保证原子性。
内存可见性:保证变量的可见性,线程在每次使用变量的时候,都会读取变量修改后的最的值。
不保证原子性。
针对多线程使用的变量如果不是volatile或者final修饰的,很有可能产生不可预知的结果(另一个线程修改了这个值,但是之后在某线程看到的是修改之前的值)。其实道理上讲同一实例的同一属性本身只有一个副本。但是多线程是会缓存值的,本质上,volatile就是不去缓存,直接取值。在线程安全的情况下加volatile会牺牲性能。
线程间的通信方式
线程间通信的方式主要为共享内存、线程同步。
线程同步除了synchronized互斥同步外,也可以使用wait/notify实现等待、通知的机制。
wait/notify属于Object类的方法,但wait和notify方法调用,必须获取对象的对象级别锁,即synchronized同步方法或同步块中使用。
wait()方法:在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,或者其他某个线程中断当前线程,导致当前线程一直阻塞等待。等同wait方法。
wait(long timeout) 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。 单位为毫秒。
void wait(long timeout, int nanos) 与 wait(long timeout) 不同的是增加了额外的纳秒级别,更精细的等待时间控制。
notfiy方法:唤醒在此对象监视器上等待的单个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。
notifyAll方法:唤醒在此对象监视器上等待的所有线程。
需要:wait被执行后,会自动释放锁,而notify被执行后,锁没有立刻释放,由synchronized同步块结束时释放。
应用场景:简单的生产、消费问题。
synchronized (lock) {//获取到对象锁lock
try {
lock.wait();//等待通信信号, 释放对象锁lock
} catch (InterruptedException e) {
e.printStackTrace();
}
}//接到通信信号
synchronized (lock) {
//获取到对象锁lock
lock.notify();//通知并唤醒某个正等待的线程
//其他操作
}//释放对象锁lock
synchronized, wait, notify 是任何对象都具有的同步工具。
他们是应用于同步问题的人工线程调度工具。讲其本质,首先就要明确monitor的概念,Java中的每个对象都有一个监视器,来监测并发代码的重入。在非多线程编码时该监视器不发挥作用,反之如果在synchronized 范围内,监视器发挥作用。
wait/notify必须存在于synchronized块中。并且,这三个关键字针对的是同一个监视器(某对象的监视器)。这意味着wait之后,其他线程可以进入同步块执行。
当某代码并不持有监视器的使用权时(如上图的状态,即脱离同步块)去wait或notify,会抛出java.lang.IllegalMonitorStateException。也包括在synchronized块中去调用另一个对象的wait/notify,因为不同对象的监视器不同,同样会抛出此异常。
参考文章:
Java多线程并发编程一览笔录 https://www.cnblogs.com/yw0219/p/10597041.html
Java 中的多线程你只要看这一篇就够了 https://juejin.im/entry/57339fe82e958a0066bf284f
阿里大牛详细讲解java多线程并发,PDT文档实战篇https://zhuanlan.zhihu.com/p/92958977
JAVA多线程之间实现同步+多线程并发同步解决方案 https://blog.csdn.net/yz2015/article/details/79436123
转载本站文章《java并发编程(1):Java多线程-基本线程类-基础知识复习笔记》,
请注明出处:https://www.zhoulujun.cn/html/java/KeyConcepts/8471.html
java并发编程(1):Java多线程-基本线程类-基础知识复习笔记的更多相关文章
- 《Java并发编程实战》第八章 线程池的使用 读书笔记
一.在任务与运行策略之间的隐性解耦 有些类型的任务须要明白地指定运行策略,包含: . 依赖性任务.依赖关系对运行策略造成约束.须要注意活跃性问题. 要求线程池足够大,确保任务都能放入. . 使用线程封 ...
- java核心-多线程(4)-线程类基础知识
1.并发 <1>使用并发的一个重要原因是提高执行效率.由于I/O等情况阻塞,单个任务并不能充分利用CPU时间.所以在单处理器的机器上也应该使用并发. <2>为了实现并发,操作系 ...
- Java并发编程(您不知道的线程池操作)
Java并发编程(您不知道的线程池操作) 这几篇博客,一直在谈线程,设想一下这个场景,如果并发的线程很多,然而每个线程如果执行的时间很多的话,这样的话,就会大量的降低系统的效率.这时候就可以采用线程池 ...
- Java并发编程:Java的四种线程池的使用,以及自定义线程工厂
目录 引言 四种线程池 newCachedThreadPool:可缓存的线程池 newFixedThreadPool:定长线程池 newSingleThreadExecutor:单线程线程池 newS ...
- 【Java并发编程】之二:线程中断
[Java并发编程]之二:线程中断 使用interrupt()中断线程 当一个线程运行时,另一个线程可以调用对应的Thread对象的interrupt()方法来中断它,该方法只是在目标线程中设置一 ...
- Java并发编程(您不知道的线程池操作), 最受欢迎的 8 位 Java 大师,Java并发包中的同步队列SynchronousQueue实现原理
Java_并发编程培训 java并发程序设计教程 JUC Exchanger 一.概述 Exchanger 可以在对中对元素进行配对和交换的线程的同步点.每个线程将条目上的某个方法呈现给 exchan ...
- 《Java并发编程实战》第三章 对象的共享 读书笔记
一.可见性 什么是可见性? Java线程安全须要防止某个线程正在使用对象状态而还有一个线程在同一时候改动该状态,并且须要确保当一个线程改动了对象的状态后,其它线程能够看到发生的状态变化. 后者就是可见 ...
- Java并发编程(1)-Java内存模型
本文主要是学习Java内存模型的笔记以及加上自己的一些案例分享,如有错误之处请指出. 一 Java内存模型的基础 1.并发编程模型的两个问题 在并发编程中,需要了解并会处理这两个关键问题: 1.1.线 ...
- Java并发编程:Java线程池核心ThreadPoolExecutor的使用和原理分析
目录 引出线程池 Executor框架 ThreadPoolExecutor详解 构造函数 重要的变量 线程池执行流程 任务队列workQueue 任务拒绝策略 线程池的关闭 ThreadPoolEx ...
- Java并发编程(四):线程安全性
一.定义 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的. 二.线程安 ...
随机推荐
- 博弈论(Nim游戏 , 有向图游戏)
博弈论专题 Nim游戏 内容: 有 n 堆石子,每堆石子的石子数给出,甲乙两人回合制取石子,每次可以取任意一堆石子的任意多个(可以直接取完,但不能不取),每个人都按照最优策略来取(抽象),问先手必胜或 ...
- AsyncOperation更好的实现大场景载入
说明:为了实现场景A->大场景B,可以让场景A->等待场景C->大场景B 知识点:AsyncOperation;AsyncOperation.allowSceneActivation ...
- 【日常收支账本】【Day04】优化编辑动账记录的操作——QTableWidget单元格设置QComboBox控件
一.项目地址 https://github.com/LinFeng-BingYi/DailyAccountBook 二.新增 1. 在表格中设置选项列表,让用户更快地编辑动账记录 1.1 功能详述 为 ...
- Java——设计模式
一.概述 设计模式是历代程序员总结出的经验 二.分类 创建型模式:简单工厂模式 工厂方法模式 单例模式:饿汉式(开发) 懒汉式(面试) 行为型模式 结构型模式 三.简单工厂模式 一个工厂中可以创建很多 ...
- 文心一言 VS 讯飞星火 VS chatgpt (143)-- 算法导论12.1 3题
三.用go语言,设计一个执行中序遍历的非递归算法.(提示:一种容易的方法是使用栈作为辅助数据结构;另一种较复杂但比较简洁的做法是不使用栈,但要假设能测试两个指针是否相等.) 文心一言,代码正常运行: ...
- Android学习day02【页面布局的练习】
在网上找了一些图片,只用最简单的颜色进行区分,目的是熟悉线性布局和相对布局 下面是我找到的简单的Android页面,你也可以尝试以下' 下面是我的实现代码 第一个
- 一文秒懂|Linux字符设备驱动
1.前言 众所周知,Linux内核主要包括三种驱动模型,字符设备驱动,块设备驱动以及网络设备驱动. 其中,Linux字符设备驱动,可以说是Linux驱动开发中最常见的一种驱动模型. 我们该系列文章,主 ...
- .NET周刊【11月第4期 2023-11-26】
国内文章 万字长文:从 C# 入门学会 RabbitMQ 消息队列编程 https://www.cnblogs.com/whuanle/p/17837034.html 如题,详细的介绍RabbitMQ ...
- 利用Jdk动态代理模拟MyBatis的Mapper功能
本文将先介绍jdk动态代理的基本用法,并对其原理和注意事项予以说明.之后将以两个最常见的应用场景为例,进行代码实操.这两个应用场景分别是拦截器和声明性接口,它们在许多开发框架中广泛使用.比如在spri ...
- PolarCTF-2023冬季个人挑战赛 WP
Crypto 数星星 题目 小明暗恋小红很久了,终于在一个月黑风高的夜晚,决定约她出去数星星.小明数着数着,数出了一串数字,3,6,10,12,15,他觉得这是爱情的关键,思考了整整一晚上,小红很生气 ...