java 多线程详细笔记(原理理解到全部使用)
鸽了好久以后终于又更新了,看同学去实习都是先学源码然后修改之类,才发觉只是知道语法怎么用还远远不够,必须要深入理解以后不管是学习还是工作,才能举一反三,快速掌握。
基础知识
既然说到了多线程,那自然就要了解一下线程到底是什么,计算机又是怎么实现对应功能的呢。
进程与线程
进程: 是代码在数据集合的一次运行活动,是系统进行资源分配和调度的基本单位。我们常用的程序就是一个进程,像qq音乐、word之类,进程就是一个程序的动态执行过程。
线程: 线程是进程的一个组成部分,一个进程可以有多个线程,而一个线程只能属于一个进程。线程可以拥有自己的堆栈、自己的程序计数器还有自己的局部变量,但不能拥有系统资源。它与父进程的其他线程共享该进程的所有资源。就好比在使用word的时候,可以一遍打字,word自动统计字数、提示语法错误等,这些功能都属于一个线程。
但其实我们电脑中存在的线程数,要远远大于电脑cpu的个数,如果是单cpu的电脑的话,每一时刻就只有一个线程可以执行,但我们仍然可以实现很多程序的同时使用,其实我们所看到的多线程的运行,只是电脑在极短时间里交替执行多个线程所表现出来的效果,比如我在一边使用word,还一边听歌,那电脑可以每隔1ms播放一下音乐,再接受一下输入,再检测一下字数等,这样以极快的速度处理每个线程的部分代码,从而使得多个任务看起来可以"同时"进行。
多线程的确可以为我们处理任务提供很大的便利,提高效率,但是因为多线程的机制使得很多在单线程中正常执行的程序出现问题,其中包括:1、原子性问题 2、线程共享内存不可见的问题。
线程原子性问题
此问题经常出现在多个线程对同一组共享变量的访问中,假设有三个线程,每个线程都会使共享变量x减少1并打印,那么应该有如下示意图:
但因为执行的操作 x -= 1 实际上并不是一个操作,执行过程是获得现有x,创建一个新的变量x',然后另x'的数值变为x - 1,之后再将公共变量中x的指针指向新的变量x',从而实现对应操作。这样的非原子性的操作就会出现这样的情况:当A线程在获得了x,并且执行x-1以后,还没有将公共变量指针指向新的变量x'的时候,线程B进入并且也获得了x,此时线程B获得的x就还是没有更新的x,从而造成了数据读写不一致的问题。
线程共享内存不可见问题
出现不可见性问题的原因与线程访问共享数据方式有关,每一个线程都有单独的堆栈、程序计数器等信息,当线程要读取公共变量的时候,也是先读取到自己的栈中,然后进行操作,最后写回到进程的公共变量里去,但若在线程A将变量加入自己的栈后,线程B将修改后的结果写回公共变量,但此时线程A不知道公共变量已经修改,从而造成不一致问题。
基于这些问题,我们可以使用synchronized 以及volatile 关键字来进行避免,这些将在下一篇单独讲解。
线程状态介绍
线程共有五种状态,分别是新建状态(NEW),可运行状态(RUNNABLE), 运行状态(RUNNING),阻塞状态(BLOCKED),死亡状态(DEAD).现对其一一介绍:
新建状态
新建状态(NEW):指新创建了一个线程对象。
可运行状态
可运行状态(RUNNABLE):指有资格可以运行,但是没有获得cpu,所以要等待直到获得cpu后变成运行状态(RUNNING)。
有很多方法可以使线程变成可运行状态,譬如:
1、对新建立的线程执行start()方法。
2、对线程调用sleep()方法使之变为阻塞状态,当sleep时间过后,会恢复成可运行状态。
3、在本线程使用join()方法,当指定线程执行完毕后,本线程恢复为可运行状态。
4、使用yield()方法,进入可运行状态。
运行状态
运行状态就是线程获得了cpu,并执行对应代码。
阻塞状态
阻塞状态是程序让出cpu控制权,并且暂时停止运行。直到指定条件达成以后,有资格运行,变成可运行状态。
使程序进入阻塞状态的方法有:
1、等待阻塞:使用wait()方法,直到被notify()方法唤醒
2、同步阻塞:申请的资源上锁并被其他资源占用,此时阻塞直到对象锁被释放
3、其他阻塞:比如使用join()方法,则当前线程会阻塞直到指定线程完成。使用sleep()等待进入阻塞状态,直到等待时间后恢复。等待io处理等。
死亡状态
一般是因异常退出了run(),或者程序执行完成
多线程使用方法
如果只是单纯地使用多线程而不是深究其同步问题的话,java提供的Thread类是已经足够使用的,而对于同步问题的解决,将会在下一篇中进行详细分析与介绍。
线程的创建
可以使用两种方法来创建线程,分别是继承Thread并重写run()方法,或者实现Runable接口的run()方法,因为java不支持多继承,因此推荐使用实现接口的方式.
可以采用接口实现:
//使用接口实现
public class MyThread implements Runnable {
@Override
public void run() {
System.out.println("run!");
}
}
也可以使用继承的方式实现:
public class MyThread02 extends Thread {
@Override
public void run() {
System.out.println("i am running!");
}
}
线程的运行
调用start()方法即可运行。虽然我们写的是run()方法,但不要直接调用run()方法,那样就起不到多线程的作用了,应该调用start()方法,然后会将该线程变为可运行状态,根据启动条件和cpu情况,适时地执行程序。
线程的礼让
使用yield()方法,使线程让出cpu,重新回到可运行状态。这时候会继续争夺cpu,也就是有可能线程A使用yield()方法让出cpu以后,又重新获得cpu的使用资格。也就是如果有A、B两个线程,并不一定A线程礼让的时候,B线程一定先于A线程执行。
线程的睡眠
使用sleep()方法,使线程阻塞一定时间。
线程的join
使用threadx.join()方法,使得当前线程堵塞,直到threadx线程执行完成。
线程的等待与唤醒
一般都与锁一起使用。
java 多线程详细笔记(原理理解到全部使用)的更多相关文章
- java多线程学习笔记——详细
一.线程类 1.新建状态(New):新创建了一个线程对象. 2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中, ...
- Java多线程学习笔记--生产消费者模式
实际开发中,我们经常会接触到生产消费者模型,如:Android的Looper相应handler处理UI操作,Socket通信的响应过程.数据缓冲区在文件读写应用等.强大的模型框架,鉴于本人水平有限目前 ...
- JAVA多线程学习笔记(1)
JAVA多线程学习笔记(1) 由于笔者使用markdown格式书写,后续copy到blog可能存在格式不美观的问题,本文的.mk文件已经上传到个人的github,会进行同步更新.github传送门 一 ...
- Java多线程学习笔记(一)——多线程实现和安全问题
1. 线程.进程.多线程: 进程是正在执行的程序,线程是进程中的代码执行,多线程就是在一个进程中有多个线程同时执行不同的任务,就像QQ,既可以开视频,又可以同时打字聊天. 2.线程的特点: 1.运行任 ...
- Java多线程学习笔记——从Java JVM对多线程数据同步的一些理解
我们知道在多线程编程中,我们很大的一部分内容是为了解决线程间的资源同步问题和线程间共同协作解决问题.线程间的同步,通俗我们理解为僧多粥少,在粥有限情况下,我们怎么去防止大家有秩序的喝到粥,不至于 ...
- Java 多线程学习笔记:生产者消费者问题
前言:最近在学习Java多线程,看到ImportNew网上有网友翻译的一篇文章<阻塞队列实现生产者消费者模式>.在文中,使用的是Java的concurrent包中的阻塞队列来实现.在看完后 ...
- java多线程-读写锁原理
Java5 在 java.util.concurrent 包中已经包含了读写锁.尽管如此,我们还是应该了解其实现背后的原理. 读/写锁的 Java 实现(Read / Write Lock Java ...
- java多线程断点下载原理(代码实例演示)
原文:http://www.open-open.com/lib/view/open1423214229232.html 其实多线程断点下载原理,很简单的,那么我们就来先了解下,如何实现多线程的断点下载 ...
- java多线程学习笔记(三)
java多线程下的对象及变量的并发访问 上一节讲到,并发访问的时候,因为是多线程,变量如果不加锁的话,会出现“脏读”的现象,这个时候需要“临界区”的出现去解决多线程的安全的并发访问.(这个“脏读”的现 ...
随机推荐
- vue显示富文本
来源:https://segmentfault.com/q/1010000013952512 用 v-html 属性解决
- VideoView--简单获取进度条的方法
使用MediaController类就可以简单的把视频中的进度条加进去 实例: 现在布局哪里放一个VideoView,然后: videoView = (VideoView) findViewById( ...
- thinkphp5 -- _initialize()初始化控制器
public function _initialize() { parent::_initialize(); } public function __construct() { $; parent:: ...
- [Inno Setup] 对比字符串
[Code] var MD5Comp: string; procedure ExitProcess(uExitCode:UINT); external 'ExitProcess@kernel32.dl ...
- hdu3033 I love sneakers! 分组背包变形(详解)
这个题很怪,一开始没仔细读题,写了个简单的分组背包交上去,果不其然WA. 题目分析: 分组背包问题是这样描述的:有K组物品,每组 i 个,费用分别为Ci ,价值为Vi,每组物品是互斥的,只能取一个或者 ...
- vuex-persist数据持久化存储插件
Vuex 解决了多视图之间的数据共享问题.但是运用过程中又带来了一个新的问题是,Vuex 的状态存储并不能持久化.也就是说当你存储在 Vuex 中的 store 里的数据,只要一刷新页面,数据就丢失了 ...
- CSS开发技巧(三):图片点击缩放
前言 利用CSS实现图片的点击缩放是一个很值得研究的效果.在某些业务需求场景中,我们可能并没有足够的空间展示过大的图片,这就需要限制图片容器的宽度和高度.然而图片限制了宽度,一些图片的细节便又无法看 ...
- 安装opencv3.3.0方法
#系统环境:CentOS6.5 x64 #首先安装jdk7u80 mkdir /java tar -zxvf jdk-7u80-linux-x64.gz -C /java/ vim /etc ...
- 华为设备RIP实施和理论详解
1.路由协议基础 共同的目的:更新.维护和控制3层的路由 工作机制: RIP,封装在UDP这个协议上,端口号520(优先级100) OSPF,封装在IP层,协议号89(优先级,内部10,外部是150- ...
- STL部分学习总结
一.map/multimap map/multimap映射容器的元素数据是由一个Key和一个Value成的,key与映照value之间具有一一映照的关系. map/multimap容器的数据结构也采用 ...