java多线程核心api以及相关概念(一)
这篇博客总结了对线程核心api以及相关概念的学习,黑体字可以理解为重点,其他的都是我对它的理解
个人认为这些是学习java多线程的基础,不理解熟悉这些,后面的也不可能学好滴
目录
1.什么是线程以及优点
二,多线程如何使用
三,线程安全问题,
四,synchronized执行过程叙述
五,几个API:
六,停止线程,暂停线程
七,线程的优先级
八,守护线程
一,首先搞清楚什么是线程以及他的优点,我觉得一句话就就可以说清楚,线程就是一个进程的许多子任务。就比如你打开浏览器之后可能即浏览网页又在下载东西,你虽然你好像是同时做了两件事情,但其实是\cpu不停的在两个任务之间切换,只是切换的速度很快,感觉上是同时运行的,另外,线程是异步滴。
理解这个概念之后,来看线程的优点,都说多线程能够让任务的执行速度变快,我觉得这句话不完全准确,并且也只是其中的一点。
首先多线程提交执行速度是建立在一个基础上的,多个任务的时间执行时间是不等的,什么意思呢?比如你现在有两个任务,任务一用时10秒,任务二用时1秒,如果你顺序执行这两个任务,那么任务一需要10秒,任务二执行了1秒,但是任务二的等待时间还有10秒,所以看作是执行了11秒,这总共就是21秒的执行时间,但是如果你使用多线程呢?可能是这样子的,先执行任务一1秒,然后切换到任务二,执行任务2一秒,之后再切换回任务一,这样子的话,很明显任务二的等待时间缩短,执行时间也就更短了,相应的也就是我们的执行速度变快了。当然啦只是我自己的理解,如果你执行时间相等的话,其实切换来切换去并没有提交效率,但是这时候还是要使用多线程,为撒子?
因为多线程的另一个优点就是能够同时运行多个任务,总不可能让浏览器每次只能打开一个窗口吧,很简单,就这两个优点。
二,多线程如何使用
1.使用多线程,两种办法,很简单,第一继承Thread类,第二种实现Runnable接口,这两个种其实就是java不支持多继承,使用前者方法的话,你就不能继承其他类了,但是实现Runnable的话还是可以的,并且你也可以实现其他的接口,更为灵活一些,本质上是没有区别的,你打开Thread的源码就会发现他也是实现了Runnable接口的,另外要注意实现Runnable接口的话,一般使用时候都是实例化一个该类,用它来初始化一个Thread类,start这个start类。如下:
MyRunnable m = new MyRunnable (); //实现了Runnable接口
Thread m1 = new Thread(m);
m1.setName("我的线程");
m1.start();
2.继承之后,你需要做的就是书写run() 方法以及他的其他可能需要的方法,启动线程时使用Thread.start() ,注意使用了这个方法之后,cpu就会将这个任务加入到执行列当中去,之后根据一定规则分配资源进行调度该任务,调度该任务的意思就是执行该线程当中的 run() 方法。
3.这里一定要理解一个点,相同优先级的线程之间调度顺序是随机的,和代码顺序无关,这个就不用说了,很简单滴,不相信自己写个程序测试下就行。
三,线程安全问题,
我们一直提到线程会不安全什么的,这个不安全是这个意思:首先有一个大前提,无论你有多少个线程,这些线程都是对同一个对象进行操作或者是由同一个线程初始化的,下面这种情况会不安全:
ackage 第一章; import javax.swing.plaf.TableHeaderUI; class MyRunnable implements Runnable{
int count = 10;
public void run(){
count--;
System.out.println(Thread.currentThread().getName() + " " + count);
}
}
public class test1{
public static void main(String[] args){
MyRunnable m = new MyRunnable();
Thread m1 = new Thread(m, "A");
Thread m2 = new Thread(m,"B");
Thread m3 = new Thread(m,"c");
Thread m4 = new Thread(m,"D");
Thread m5 = new Thread(m,"E");
m1.start();
m2.start();
m3.start();
m4.start();
m5.start();
}
}
输出的结果每一次都是不一样的,我们期望的是count从10减到5并输出每一个数值,但是事实上的话,可能B线程已经获取到了count的值,这时候准备执行减一操作并输出,但是程序跳到A线程,执行了count--操作,B可能本来获取的值是10,之后准备count--,然后输出9,但是在这两部操作之间,count被A线程又给减一了,那么B线程获取到count的值时,就获取到了A线程更改之后的值,8,但是他原本获取到的值是9,。也就是说原本输出9,现在输出了8,这很明显是不安全的。。。。这样说不知道清楚不。输出结果如下:
"C:\Program Files\Java\jdk-11.0.1\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.1.3\lib\idea_rt.jar=60024:C:\Program Files\JetBrains\IntelliJ IDEA 2019.1.3\bin" -Dfile.encoding=UTF-8 -classpath C:\learn\java\practice\out\production\threadTest 第一章.test1
A 8
B 8
D 6
c 5
E 7 Process finished with exit code 0
下面这种情况是安全的:
就是将count变量写在run() 函数内部,让他成为局部变量,这样每一个线程虽然是由同一个对象初始化的,但是他们的count是属于各自的,你无法在A线程里更改B线程的count的值,这时候就是安全的,就这样,不难理解,这里就不放代码啦。
这里为了解决我们的不安全问题,引出了synchronized概念,
四,synchronized执行过程叙述
这个关键字其实就是给一个对象的之中的某个方法上锁,每当一个线程访问该对象的该方法时,他会首先拿到这个锁,然后开始执行里面的代码,之后如果有另一个线程再来执行同一个对象的该方法,他会先尝试拿到锁,拿不到的话就会一直等着,直到锁被释放,再拿到锁执行代码,这样子就解决了安全问题,因为相当于每一个线程进入该方法之后,就是它自己的领地了,对count变量的操作都是以它自己为标准的,其他线程不能在中途更改count的值,直到该线程执行完毕。
五,几个API:
1.CurrentThread() :获取当前线程,当前线程指的是当前代码片段是在哪一个线程上被调用的,比如下面的代码,构造函数MyRunnable() 是在main 线程当中被调用的,但是run()函数却是在线程A中调用的, 另外注意和this,getName() 的区别,后者是针对于当前this对象而言的Name
class MyRunnable implements Runnable{
int count = 10;
MyRunnable(){
System.out.println(Thread.currentThread().getName()); //输出 main
}
synchronized public void run(){
count--;
System.out.println(Thread.currentThread().getName() + " " + count); //输出 A
}
}
public class test1{
public static void main(String[] args){
MyRunnable m = new MyRunnable();
Thread m1 = new Thread(m, "A");
m1.start();
}
}
2.isAlive():当前线程是否处于活动状态,简单地说就是是否start()了并且run() 函数还没有运行完 ,用法:Thread.CurrentThread.isAlive()
3.sleep(): 让当前线程休眠一段时间,单位毫秒,注意这里这个当前线程指的是正在运行的线程,就是当前代码片段处于哪一个线程当中,注意放在try语句中,用法:Thread.sleep()
4.getId(): 获取当前线程唯一标识
六,停止线程,暂停线程
概述:停止线程简单来说就是让cpu放弃当前线程的操作,先去执行其他线程,但是如果使用不当,会有数据不同步,独占锁等问题
终止线程的方法:
1.线程run函数调用结束,自动结束
2.调用 interrupt 函数
Thread.interrupt() : 这个函数并不是说立刻停止停止线程,让线程停止操作,而是说在当前位置给线程加一个标识,表示该线程是应该停止的,但是并不会立即停止,那如果想要真正的停止该怎么办呢?使用 isTerrupted()和interrupted(),简单来讲,就是使用interrupt()停止线程添加标识之后,再使用相应的函数判断是否存在这个停止标识,存在的话就不执行某一些代码了,这样来达到停止的效果,但是这两个函数是不同的,看下面
interrupted(): 测试当前线程是否中断,如果中断了,就将中断标识,或者说状态清除掉。简单来讲就是执行完这个函数,线程一定是处于运行状态的
isInterrupted(): 测试线程是否中断,没有清楚中断状态的功能,并且注意判断的并不是当前线程
理解两点:1,当前线程和线程的区别,2.是否会清除中断状态,
看下面例子:
Thread.interrupt() :
class MyRunnable implements Runnable{
int count = 10;
MyRunnable(){
System.out.println(Thread.currentThread().getName());
}
public void run(){
for(int i=0;i<1000;i++)
{
System.out.println(i);
}
}
}
public class test1{
public static void main(String[] args){
MyRunnable m = new MyRunnable();
Thread m1 = new Thread(m, "A");
m1.start();
m1.interrupt(); //注释该行,不注释下一行,输出true,false
//Thread.currentThread().interrupt();
System.out.println("测试是否中断1:"+Thread.interrupted()); //输出false
System.out.println("测试是否中断2:"+Thread.interrupted()); //输出false
}
}
根据运行结果false来讲,我们停止了m1,线程,但是输出为false,因为调用interrupted方法代码处于的线程是main线程,main并没有停止,所以输出false,但是如果将注释去掉,将m1.interrupt()注释掉,会输出true ,false,为什么第二是false?这就是因为该函数具有清除中断状态的作用,第一次执行之后main就不再是中断状态了,所以输出false
isterrupted()方法:不具有清除状态作用,检测的是线程对象,一个具体的对象,不是当前线程,如下
package 第一章; import javax.swing.plaf.TableHeaderUI; class MyRunnable implements Runnable{
int count = 10;
MyRunnable(){
System.out.println(Thread.currentThread().getName());
}
public void run(){
for(int i=0;i<1000;i++)
{
System.out.println(i);
}
}
}
public class test1{
public static void main(String[] args){
MyRunnable m = new MyRunnable();
Thread m1 = new Thread(m, "A");
m1.start();
m1.interrupt();
//Thread.currentThread().interrupt();
System.out.println("测试是否中断1:&&&&&&&&&&&&&&&&&&&&&&&"+m1.isInterrupted()); //输出true
System.out.println("测试是否中断2:&&&&&&&&&&&&&&&&&&&"+m1.isInterrupted()); //输出true
}
}
都输出true,就是因为他们调用的是对象m1的,m1被停止了,所以肯定输出true,不过注意,这个true会输出在任意位置,cpu调用决定的,所以你如果测试的话加点标识找起来方便。
所以,结合来讲的话,停止线程,run方法可以修改为如下:
public void run(){
for(int i=0;i<1000;i++)
{
if(Thread.interrupted()){
System.out.println("停止啦");
break;
}
System.out.println(i);
}
//如果给这里加上语句,不判断是否停止,他还是会运行的
}
执行结果,可以看到输出了一些数字之后就停止了
。。。
205
206
207
208
209
210
211
212
停止啦 Process finished with exit code 0
当然这是一种软性的停止,你需要判断他是否停止了,下面介绍一种硬性的停止,抛出异常,很简单,将run 方法改为如下:
public void run(){
try{
for(int i=0;i<1000;i++)
{
if(Thread.interrupted()){
System.out.println("停止啦");
throw new InterruptedException();
}
System.out.println(i);
}
//for循环外面有语句也不会执行了,捕捉到异常
}catch (InterruptedException e){
e.printStackTrace();
}
}
一种情况,在沉睡中停止线程,
什么意思呢?假设你现在线程中断了,然后你再去调用Thread.sleep()函数让线程休眠,或者线程已经休眠了,你再去调用interrrupt()函数,那就会产生异常,并清除线程的中断状态。这两种情况是对立矛盾的,所以会产生异常,并且因为产生了异常,所以线程一定是被中断的
比如下面的代码,m1.start()之后,m1线程进行了sleep(),之后在main中又中断了m1线程,这明显是不合理的,所以会输出异常之中的内容
class MyRunnable implements Runnable{
int count = 10;
MyRunnable(){
System.out.println(Thread.currentThread().getName());
}
public void run(){
try{
for(int i=0;i<1000;i++)
{
System.out.println(i);
Thread.sleep(1000);
}}catch (InterruptedException e){
System.out.println("先休眠再停止");
e.printStackTrace();
}
}
}
public class test1{
public static void main(String[] args){
MyRunnable m = new MyRunnable();
Thread m1 = new Thread(m, "A");
m1.start();
try { //加这个语句是为了让m1的中断执行在睡眠之后
Thread.sleep(10);
}catch (InterruptedException e){
e.printStackTrace();
}
m1.interrupt();
}
}
输出如下:
main
0
先停止再休眠
java.lang.InterruptedException: sleep interrupted
at java.base/java.lang.Thread.sleep(Native Method)
at 第一章.MyRunnable.run(test1.java:15)
at java.base/java.lang.Thread.run(Thread.java:834) Process finished with exit code 0
3.使用stop
这种方法已经被废除了,会造成很多问题
最简单的一个问题,数据不同步,假设你去银行,取1000,存1000,然后给你操作时候,这两个操作时在一个方法里面的,并且这个方法是上了锁的。在程序执行完了取1000之后,程序去上了个厕所,休眠了1秒,休眠期间,你使用了stop函数将线程强行停止,释放了对象锁,相当于后面存1000这个操作就没有做了,这很明显是不合理的,数据不同步了。
4.暂停线程
暂停线程是说将当前线程的操作暂停,过一会再回来继续执行,注意和中断的区别,它并不会释放对象锁
suspend()暂停函数,
resume()恢复函数
这两个函数也已经被废除了,因为可能造成独占锁和不同步的问题,废除我们也要知道他为撒子废除了,不想看的朋友可以跳过下面
独占锁问题:
不想放太多的代码,放一点,然后用文字说明,更能表达思路
有这样一个对象,比较简洁,不想写没用的代码,意思就是碰到名字为a的线程,就暂停当前线程
class test{
synchronized public void printStr() {
if(Thread.currentThread().getName().equals("a")){
System.out.println("打印。。。。。");
Thread.currentThread().suspend();
}
}
}
class MyRunnable implements Runnable{
int count = 10;
MyRunnable(){
System.out.println(Thread.currentThread().getName());
}
public void run() {
//调用test实例的printStr方法
}
}
现在,你初始化了a,b两个线程,使用了一个MyRunnable实例初始化的,然后你先start了a线程,a线程执行了printStr()方法,获取到了test实例的对象锁,然后被suspend了,但是它并没有释放test实例的对象锁,这时候如果b线程开启,他是无法执行printStr()方法的,因为它他拿不到对象锁,只能等着,这是不合理的。而且这里有一个有意思的问题,如果你这里使用了suspend的话,你如果想要在其他线程里面使用System.out.println() 方法,是不可以的,因为out对象的println()方法已经被锁定了。就是说你无法拿到他的锁,使用不了这个方法了,看下面的例子
class MyThread extends Thread{
private int i=0;
public void run(){
while(true){
System.out.println(i); //占用了println() 方法
i++;
}
}
}
public class test1{
public static void main(String[] args){
MyThread m = new MyThread();
m.start();
try {
Thread.sleep(10);
}catch (InterruptedException e){
e.printStackTrace();
}
m.suspend();
System.out.println("main"); //不会打印
}
}
输出结果就是打印到某一个数字然后停止,永远不会打印 main ,其实你看一下println()方法源码就明白了:里面的代码被锁住了,a线程独占了这个锁
public void println(int x) {
synchronized (this) {
print(x);
newLine();
}
}
不同步问题:
这个我觉得和前面stop的问题是一样的,不说了
七,线程的优先级
概述:前面我们看到的main线程还有自己创建的线程,代码执行都是随机的,55开滴,但是现实中线程肯定是有轻重缓急的,所以就有了线程优先级这一个概念,线程优先级高的执行的概率会大一点,但是也不是一定的,只是相对来讲大一点。
个人感觉这块没撒说的,就几个概念也都很好理解,自己写一些代码看看就行。
1.setPriotity(int ) 设置优先级
2.getPriotity() 获取优先级
3.优先级具有继承性:比如你在A线程里启动了B线程,则B线程的优先级是和A线程一样的,除非你手动进行了设置。很简单吧
4.优先级具有随机性:这个也很理解,优先级高的不一定每一次都比优先级低先执行完,因为cpu在调度的时候肯定是以概率的方式来调度线程的,概率嘛,什么都有可能,只能说样本足够大时,优先级高的一定先执行完。
八,守护线程
java里面有两种线程,用户线程和守护线程,用户线程就是我之前定义的那些,当然也包括main线程,守护线程,举一个最简单的例子,java的垃圾回收机制,它是在什么时候回收的呢?你会跟随着用于线程,有没有delete的空间了,就清除掉它,直到所有线程都结束之后,它才会自动销毁。简单来说,就是一直陪伴着用户线程,帮助用户线程做一些事情,用户线程全部结束了,它也就结束了。
设置方法:thread.setDaemon(true); 默认是false的
个人觉得可能做一些需要不停的做的事情会用到该线程。
java多线程核心api以及相关概念(一)的更多相关文章
- 深入理解 Java 多线程核心知识
多线程相对于其他 Java 知识点来讲,有一定的学习门槛,并且了解起来比较费劲.在平时工作中如若使用不当会出现数据错乱.执行效率低(还不如单线程去运行)或者死锁程序挂掉等等问题,所以掌握了解多线程至关 ...
- 2018跳槽面试必备之深入理解 Java 多线程核心知识
导语:多线程相对于其他 Java 知识点来讲,有一定的学习门槛,并且了解起来比较费劲.在平时工作中如若使用不当会出现数据错乱.执行效率低(还不如单线程去运行)或者死锁程序挂掉等等问题,所以掌握了解多线 ...
- 深入理解 Java 多线程核心知识:跳槽面试必备
多线程相对于其他 Java 知识点来讲,有一定的学习门槛,并且了解起来比较费劲.在平时工作中如若使用不当会出现数据错乱.执行效率低(还不如单线程去运行)或者死锁程序挂掉等等问题,所以掌握了解多线程至关 ...
- Java多线程核心知识(跳槽面试必备)
多线程相对于其他 Java 知识点来讲,有一定的学习门槛,并且了解起来比较费劲.在平时工作中如若使用不当会出现数据错乱.执行效率低(还不如单线程去运行)或者死锁程序挂掉等等问题,所以掌握了解多线程至关 ...
- Java多线程核心知识
多线程相对于其他 Java 知识点来讲,有一定的学习门槛,并且了解起来比较费劲.在平时工作中如若使用不当会出现数据错乱.执行效率低(还不如单线程去运行)或者死锁程序挂掉等等问题,所以掌握了解多线程至关 ...
- java多线程基础API
本次内容主要讲认识Java中的多线程.线程的启动与中止.yield()和join.线程优先级和守护线程. 1.Java程序天生就是多线程的 一个Java程序从main()方法开始执行,然后按照既定的代 ...
- Java核心API需要掌握的程度
分类: java技术2009-08-29 01:03 213人阅读 评论(0) 收藏 举报 javaapiswingxmlio Java的核心API是非常庞大的,这给开发者来说带来了很大的方便,经常人 ...
- Java多线程编程(同步、死锁、生产消费者问题)
Java多线程编程(同步.死锁.生产消费): 关于线程同步以及死锁问题: 线程同步概念:是指若干个线程对象并行进行资源的访问时实现的资源处理保护操作: 线程死锁概念:是指两个线程都在等待对方先完成,造 ...
- Java多线程编程实战指南(核心篇)读书笔记(二)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76651408冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
随机推荐
- 12 DOM操作的相关案例
1.模态框案例 需求: 打开网页时有一个普通的按钮,点击当前按钮显示一个背景图,中心并弹出一个弹出框,点击X的时候会关闭当前的模态框 代码如下: <!DOCTYPE html> <h ...
- 基于Netty的四层和七层代理性能方面的一些压力测试
本文我们主要是想测试和研究几点: 基于Netty写的最简单的转发HTTP请求的程序,四层和七层性能的差异 三种代理线程模型性能的差异,下文会详细解释三种线程模型 池和非池化ByteBuffer性能的差 ...
- 在网页中添加动画,使用WOW.js来实现
[来源] 页面在向下滚动的时候,有些元素会产生细小的动画效果.虽然动画比较小,但却能吸引你的注意 刚知道wow.js这个插件,之前写的类似滚动时页面效果都是自己用jQuery写的,现在有了插件,开发更 ...
- ssm中mapper注入失败的传奇经历
最近因为要测试一个功能,需要用最短的时间来启动服务,开启测试程序,但平常所用的框架中已经集成了各种三方的东西,想着那就再重新搭建一个最简单的ssm框架吧. 搭建可参考:简单ssm最新搭建 搭建过程并不 ...
- 【fullGC】内存监控工具
什么是fullGC: 从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC,对老年代GC称为Major GC,而Full GC是对整个堆来说的,在最近几个版本的J ...
- Nginx查看并发连接数
Nginx查看并发连接 通过界面查看 通过界面查看通过web界面查看时Nginx需要开启status模块,也就是安装Nginx时加上 --with-http_stub_status_module 然后 ...
- 教你用VMware Workstation Pro 12 安装XP系统
转 https://jingyan.baidu.com/article/ff42efa9102da3c19e220219.html
- white box白盒测试
逻辑覆盖法:语句覆盖,判定覆盖,条件覆盖,判定/条件覆盖,组合覆盖,路径覆盖 基本路径测试法:Control Flow Graphs, CFG.带箭头的边 条件覆盖:使每个判定中每个条件的可能值至少满 ...
- POJ 3692:Kindergarten(二分图最大团)
题目链接 题意 已知班级有g个女孩和b个男孩,所有女生之间都相互认识,所有男生之间也相互认识,给出m对关系表示哪个女孩与哪个男孩认识.现在要选择一些学生来组成一个集合,使得里面所有人都认识,求此集合最 ...
- 安装Win10,ERROR_0x8007025D问题解决
Windows10安装的时候,出现ERROR CODE:0x8007025D 大概提示为:windows 无法安装所需的文件.请确保安装所需的所有文件可用,并重新启动安装. 本人在出现这个问题的原因, ...