这篇博客总结了对线程核心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以及相关概念(一)的更多相关文章

  1. 深入理解 Java 多线程核心知识

    多线程相对于其他 Java 知识点来讲,有一定的学习门槛,并且了解起来比较费劲.在平时工作中如若使用不当会出现数据错乱.执行效率低(还不如单线程去运行)或者死锁程序挂掉等等问题,所以掌握了解多线程至关 ...

  2. 2018跳槽面试必备之深入理解 Java 多线程核心知识

    导语:多线程相对于其他 Java 知识点来讲,有一定的学习门槛,并且了解起来比较费劲.在平时工作中如若使用不当会出现数据错乱.执行效率低(还不如单线程去运行)或者死锁程序挂掉等等问题,所以掌握了解多线 ...

  3. 深入理解 Java 多线程核心知识:跳槽面试必备

    多线程相对于其他 Java 知识点来讲,有一定的学习门槛,并且了解起来比较费劲.在平时工作中如若使用不当会出现数据错乱.执行效率低(还不如单线程去运行)或者死锁程序挂掉等等问题,所以掌握了解多线程至关 ...

  4. Java多线程核心知识(跳槽面试必备)

    多线程相对于其他 Java 知识点来讲,有一定的学习门槛,并且了解起来比较费劲.在平时工作中如若使用不当会出现数据错乱.执行效率低(还不如单线程去运行)或者死锁程序挂掉等等问题,所以掌握了解多线程至关 ...

  5. Java多线程核心知识

    多线程相对于其他 Java 知识点来讲,有一定的学习门槛,并且了解起来比较费劲.在平时工作中如若使用不当会出现数据错乱.执行效率低(还不如单线程去运行)或者死锁程序挂掉等等问题,所以掌握了解多线程至关 ...

  6. java多线程基础API

    本次内容主要讲认识Java中的多线程.线程的启动与中止.yield()和join.线程优先级和守护线程. 1.Java程序天生就是多线程的 一个Java程序从main()方法开始执行,然后按照既定的代 ...

  7. Java核心API需要掌握的程度

    分类: java技术2009-08-29 01:03 213人阅读 评论(0) 收藏 举报 javaapiswingxmlio Java的核心API是非常庞大的,这给开发者来说带来了很大的方便,经常人 ...

  8. Java多线程编程(同步、死锁、生产消费者问题)

    Java多线程编程(同步.死锁.生产消费): 关于线程同步以及死锁问题: 线程同步概念:是指若干个线程对象并行进行资源的访问时实现的资源处理保护操作: 线程死锁概念:是指两个线程都在等待对方先完成,造 ...

  9. Java多线程编程实战指南(核心篇)读书笔记(二)

    (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76651408冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...

随机推荐

  1. javascript中&&运算符和||运算符的使用

    前言 我们在前端开发中,&&运算符和||运算符是使用率和频繁度比较高的,&&运算符和||运算符的功能特别强大,想成为一名优秀的前端工程师,&&运算符和| ...

  2. spring boot 2.x 系列 —— actuator 服务监控与管理

    文章目录 一.概念综述 1.1 端点 1.2 启用端点 1.3 暴露端点 1.4 健康检查信息 二.项目说明 1.1 项目结构说明 1.2 主要依赖 1.3 项目配置 1.4 查看监控状态 三.自定义 ...

  3. 【设计模式】【最后一个】结构型07适配器模式(Adapter Pattern)

    适配器模式(Adapter Pattern) 推荐一个很全面的博客:https://blog.csdn.net/zxt0601/article/details/52848004 定义:将一个类的接口转 ...

  4. centos crontab用法详解 定时任务的设置

    crontab 是用来让使用者在固定时间或固定间隔执行程序之用,类似于windows的计划任务 安装: yum -y install vixie-cron yum -y install crontab ...

  5. 戴尔R720安装ESXI系统

    1.U盘安装系统,使用UltraISO制作启动盘 参考地址:https://jingyan.baidu.com/article/5225f26b0bb45fe6fa0908bc.html 2.插上U盘 ...

  6. kali Linux渗透测试技术详解

    kali Linux渗透测试技术详解 下载:https://pan.baidu.com/s/1g7dTFfzFRtPDmMiEsrZDkQ 提取码:p23d <Kali Linux渗透测试技术详 ...

  7. [apue] 使用 poll 检测管道断开

    一般使用 poll 检测 socket 或标准输入时,只要指定 POLLIN 标志位,就可以检测是否有数据到达,或者连接断开: ]; fds[].fd = STDIN_FILENO; fds[].ev ...

  8. 新手如何学python?

    在学习Python前,你应该规划好自己的学习方向,毕竟Python的方向比较多,比如: web开发 爬虫 自动化开发(运维,测试).脚本开发 大数据(数据挖掘,数据分析) 人工智能.机器学习 选择好自 ...

  9. 按行读取String类型

    BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(content.getByt ...

  10. (转)代码结构中Dao,Service,Controller,Util,Model是什么意思?

    作者:技能树IT修真院链接:https://www.zhihu.com/question/58410621/answer/623496434来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商 ...