一、简介

1、什么是线程?

要解释线程,就必须明白什么是进程。

2、什么是进程呢?

进程是指运行中的应用程序,每个进程都有自己独立的地址空间(内存空间),比如用户点击桌面的IE浏览器,就启动了一个进程,操作系统就会为该进程分配独立的地址空间。

要点:用户每启动一个进程,操作系统就会为该进程分配一个独立的内存空间。

二、线程--概念

在明白进程后,就比较容易理解线程的概念。

1、什么是线程呢?

是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。同一进程中的多个线程之间可以并发执行。

写的太官方了,我的理解就是:

进程就类似一个班级,线程就类似这个班级里面的小学生,多个小学生可以同时做一件事,也可以排队等前面一个小学生做完(synchronized,同步锁操作);

可以让出当前的空间(yield),让另外一个小学生做,如果别人竞争不到或者没有人跟你竞争,那就自己继续干活;

也可以适当的休息一段时间(sleep),让另外一个小学生干活,之后再加入;

如果有两个要好的朋友,一个做完活了,要走了,你想让他等你,你可以用(join)让他等你做完活;

看过一个总结的特别简单的句子(进程就是打开一个APP,线程就是APP里面的各种操作)

2、线程的类型

线程中有两种类型,用户线程和守护线程

用户线程:运行在前台,执行具体的任务,程序的主线程,连接网络的子线程都是用户线程

守护线程:运行在后台,为其他前台线程服务

守护线程的特点:一旦所有的用户线程都结束运行,守护线程随着用户线程的结束而结束

(简单理解:用户线程就好比公司里面的CEO,经理这些,守护线程就好比保安,CEO,经理都下班了,保安也跟着下班呗)

设置线程成为守护线程(Daemon)

Thread thread = new Thread();
thread.setDaemon(true); // 调用对象的setDaemon方法,设置为守护线程,必须在start()前
thread.start();

三、Thread类和Runnable接口

如果想要在main方法中,定义多个线程去执行一段代码,java语言中实现了多线程编程的类和接口,在java.lang包中定义了Thread类和Runnable接口。

Thread类实现了Runnanble接口。Runnable对象为可运行对象,一个线程的执行是执行该对象里面的run方法

Thread的构造方法如下:

public Thread()
public Thread(Runnable target)
public Thread(String name)
public Thread(Runnable target, String name)
public Thread(ThreadGroup group, Runnable target)
public Thread(ThreadGroup group, String name)
public Thread(ThreadGroup group, Runnable target, String name)/**
target:为线程的目标对象,即线程调用start()方法启动后运行那个对象的run()方法
name: 为该线程的名称
group:指定该线程属于哪一个组
*/

Thread的常用方法有:

public static Thread currentThread(); // 返回当前正在执行的线程对象
public void getName();// 返回当前的线程名称
public static void sleep(long millis); // 让当前线程休眠一段时间,让出当前CPU
public static void yield(); // 让出当前CPU,如果存在线程等待并且优先级别高的情况,则该线程获取到CPU执行空间;如果存在同等线程级别的情况,有可能当前让出的线程继续抢到CPU执行空间
public void run(); // 线程的线程体,方法都是写在这里面
public void start(); //JVM调用线程的run方法,启动线程开始
public void setDaemon(boolean on); // 设置线程为守护线程

四、线程的创建

创建线程有两种方法:

1、继承Thread并覆盖run方法

2、实现Runnable接口并重写run方法

继承Thread方法

简单做一个输出

public class ThreadA extends Thread {private String name;public ThreadA(String name){super(name);this.name = name;}@Overridepublic void run(){System.out.println(Thread.currentThread().getName() + " 线程开始执行 ");for (int i = 0; i < 5; i++) {System.out.println("子线程 "+name+"运行:"+i);try{sleep((int)Math.random()*10);} catch (InterruptedException e) {e.printStackTrace();}}System.out.printf(Thread.currentThread().getName()+" 线程运行结束 ");}
}

main方法执行

public static void main(String [] args){System.out.println(Thread.currentThread().getName()+"主线程开始运行");ThreadA threadA = new ThreadA("A");ThreadA threadA1 = new ThreadA("B");threadA.start();threadA1.start();System.out.println(Thread.currentThread().getName()+"主线程结束执行");
}
/**
执行结果:
main主线程开始运行
main主线程结束执行
A 线程开始执行
子线程 A运行:0
B 线程开始执行
子线程 B运行:0
子线程 A运行:1
子线程 A运行:2
子线程 A运行:3
子线程 A运行:4
A 线程运行结束
子线程 B运行:1
子线程 B运行:2
子线程 B运行:3
子线程 B运行:4
B 线程运行结束  */

这里可以看出,两个线程互相抢占资源,输出的信息,都是乱的;再看main主线程,一开始就结束了运行,可以看出我们的子线程是执行成功的;

如果我们想要主线程等待子线程执行成功之后,再做下一步操作呢?可以用Join() 方法

public static void main(String[] args){System.out.println(Thread.currentThread().getName()+"主线程开始运行");ThreadA threadA = new ThreadA("A");ThreadA threadA1 = new ThreadA("B");threadA.start();threadA1.start();// join的意思:等待指定线程终止,这里可以理解为: main主线程等待子线程的终止threadA.join();threadA1.join();System.out.println(Thread.currentThread().getName()+"主线程结束执行");
}
/**
main主线程开始运行
A 线程开始执行
子线程 A运行:0
B 线程开始执行
子线程 B运行:0
子线程 A运行:1
子线程 A运行:2
子线程 A运行:3
子线程 A运行:4
A 线程运行结束
子线程 B运行:1
子线程 B运行:2
子线程 B运行:3
子线程 B运行:4
B 线程运行结束
main主线程结束执行
*/

仔细看输出日志,main主线程结束执行排在最后面;

看的这个排序是在不爽,我们想要让A输出完,B再输出的话,怎么处理呢?

用synchronized 同步锁,他的意思是:被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;

  @Overridepublic synchronized void run(){System.out.println(Thread.currentThread().getName() + " 线程开始执行 ");for (int i = 0; i < 5; i++) {System.out.println("子线程 "+name+"运行:"+i);try{sleep((int)Math.random()*10);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName()+" 线程运行结束 ");}/**
执行结果:
main主线程开始运行
B 线程开始执行
子线程 B运行:0
子线程 B运行:1
子线程 B运行:2
子线程 B运行:3
子线程 B运行:4
B 线程运行结束
A 线程开始执行
子线程 A运行:0
子线程 A运行:1
子线程 A运行:2
子线程 A运行:3
子线程 A运行:4
A 线程运行结束
main主线程结束执行*/

嗯,很奈斯

使用Synchronized 表示多个线程同时调用一个方法的时候,没有调用wait()或者sleep()方法的话,只能先进入的一个线程执行完毕之后,后一个线程才能进入。

实现Runnable接口

实现两个线程执行同一个方法,打印输出10、9、8...

// 多个线程,简单输出10、9、8...
public class RunnableA implements Runnable {private int count = 10;@Overridepublic void run() {while (count > 0){System.out.println("当前线程名称是 >>> "+Thread.currentThread().getName()+" ,count为 >>> "+count);count--;}}
}

调用方法

public static void main(String[] args){RunnableA ra = new RunnableA();Thread tA = new Thread(ra,"A");Thread tB = new Thread(ra,"B");tA.start();tB.start();
}
/**
执行结果:
当前线程名称是 >>> A ,count为 >>> 10
当前线程名称是 >>> B ,count为 >>> 10
当前线程名称是 >>> B ,count为 >>> 8
当前线程名称是 >>> A ,count为 >>> 9
当前线程名称是 >>> B ,count为 >>> 7
当前线程名称是 >>> A ,count为 >>> 6
当前线程名称是 >>> B ,count为 >>> 5
当前线程名称是 >>> A ,count为 >>> 4
当前线程名称是 >>> A ,count为 >>> 2
当前线程名称是 >>> A ,count为 >>> 1
当前线程名称是 >>> B ,count为 >>> 3
*/

可以看出,两个线程同时执行一个方法,得出的结果存在两个10的情况,我们可以加synchornized 同步锁去处理,但是这样,出现的情况就是要么 tA全执行完,要么tB全执行完,两个线程变成了单个线程去使用。

如果我们想要 tA线程输出双数,tB线程输出单数的情况,应该怎么处理呢?

嘿,上代码

// 多个线程,简单输出10、9、8...
public class RunnableA implements Runnable {private int count = 10;private int i = 0;@Overridepublic void run() {// synchronized (this) 表示的是该代码块同步,this表示的是当前进来的线程。synchronized (this) {while (count > 0) {notify();   // 唤醒当前进来的线程,如果没有在等待状态,也可以唤醒System.out.println("当前线程名称是 >>> " + Thread.currentThread().getName() + " ,count为 >>> " + count);count--;try {//  wait:使当前线程进入等待状态,并且释放当前对象所持有的锁// (简单理解就是 synchronized 锁住的对象,有方法,有变量,简单理解为当前线程释放了对资源的控制)wait();// System.out.println(Thread.currentThread().getName() + ">>>" + Thread.currentThread().isAlive()+" >>> "+(i++));} catch (InterruptedException e) {e.printStackTrace();}}}}
}

调用方法

public static void main(String[] args){RunnableA ra = new RunnableA();Thread tA = new Thread(ra,"A");Thread tB = new Thread(ra,"B");tA.start();tB.start();
}/**
执行结果:
当前线程名称是 >>> A ,count为 >>> 10
当前线程名称是 >>> B ,count为 >>> 9
当前线程名称是 >>> A ,count为 >>> 8
当前线程名称是 >>> B ,count为 >>> 7
当前线程名称是 >>> A ,count为 >>> 6
当前线程名称是 >>> B ,count为 >>> 5
当前线程名称是 >>> A ,count为 >>> 4
当前线程名称是 >>> B ,count为 >>> 3
当前线程名称是 >>> A ,count为 >>> 2
当前线程名称是 >>> B ,count为 >>> 1
*/

【注:博主一直看不懂 wait()  后面说的,并且释放该线程所持有的锁,后面捋了一遍,我的简单粗暴的理解就是:好比有一个屋子,里面很多吃的,一群饿鬼在门外面抢着要进去吃东西,你抢先进去了,然后马上把门关上了,等你吃的差不多了,就要把门打开了。不能这么自私吧。栗子不是很恰当,反正意思就是说,一个线程使用wait()进入等待状态的时候,同时也会释放掉对当前锁的控制,让其他线程可以抢占到锁

总结:

实现Runnable接口比继承Thread具有的优势:

1、避免java中单继承的限制

2、适合多个相同程序的代码去实现同一个资源

3、增强代码的健壮性,代码可以被多个线程共享,代码和数据独立

五、synchronized线程同步

synchronized是java中的关键字,是一种同步锁,修饰的对象有几种

1、修饰一个代码块,被修饰的代码块称为同步语句块,作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码的对象

2、修饰一个类,其作用的范围是synchronized的括号{} 括起来的代码,作用的对象是这个类的所有对象

3、修饰一个方法,被修饰的方法称为同步方法,作用的范围是整个方法,作用的对象是调用这个方法的对象

4、修饰一个静态的方法,作用的范围是整个静态方法,作用的对象是这个类的所有对象

上栗子

synchronized修饰代码块

public void run(){synchronized(this){System.out.println("修饰一个代码块,作用的对象是调用这个代码块的对象");}
}

当两个并发线程同时访问这个代码块的时候,同一时刻只能有一个线程得到执行,另外一个阻塞。必须等到当前线程执行完毕之后才能执行该代码块。

我们也可以通过定义特殊变量来充当锁

private byte[] lock = new byte[0];  // 特殊的instance变量
public void method()
{synchronized(lock) {// todo 同步代码块}
}

说明:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。

synchronized修饰方法

public synchronized void run(){System.out.println("修饰方法范围是整个函数");
}

这样的写法相当于

public synchronized void methodName(){}
public void run(){synchronized(this){}
}

synchronized关键字不能继承。 
虽然可以使用synchronized来定义方法,但synchronized并不属于方法定义的一部分,因此,synchronized关键字不能被继承。如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上synchronized关键字才可以。当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了。

synchronized修饰静态方法

public synchronized static void methodName(){System.out.println("锁定的是这个类的所有对象");
}

synchronized修饰一个类

public static void method() {synchronized(ClassName.class) {System.out.println("所有对象用的是同一把锁");}
}

总结:

只是单纯做一个笔记总结,期间拷贝一些博主的代码,方便自己日后使用记录。

2019第二天的一个感悟就是:不能活在舒适区啊,尤其是我们程序猿,要时刻有危机感。谨记自己!!!

参考网址链接以下

https://blog.csdn.net/luoweifu/article/details/46595285 (编程思想之多线程与多进程)

https://blog.csdn.net/luoweifu/article/details/46613015#t3  (Java中Synchronized的用法)

https://www.cnblogs.com/GarfieldEr007/p/5746362.html (Java多线程学习(吐血超详细总结))

Java基础----多线程详解相关推荐

  1. java基础(十三)-----详解内部类——Java高级开发必须懂的

    java基础(十三)-----详解内部类--Java高级开发必须懂的 目录 为什么要使用内部类 内部类基础 静态内部类 成员内部类 成员内部类的对象创建 继承成员内部类 局部内部类 推荐博客 匿名内部 ...

  2. Java基础——匿名内部类详解及举例

    Java基础--匿名内部类详解及举例 说明:定义在外部类的局部位置,比如方法中,并且没有类名 匿名内部类的使用 匿名内部类的基本语法 new 类或接口(参数列表){类体 }; 用途:有些时候为了简化开 ...

  3. 【狂神说Java】多线程详解

    [狂神说Java]多线程详解 1.任务 生活中的例子.边吃饭.边玩手机 开车.打电话.挂点滴 上厕所.玩手机 现实中太多这样同时做多件事情的例子了,看起来是多个任务都在做,其实本质上我们的大脑在同一时 ...

  4. 厚积薄发打卡Day25 :狂神说Java之多线程详解<全网最全(代码+笔记)>

    概述 视频来源:[狂神说Java]多线程详解 强烈推荐,

  5. Java基础:详解static关键字与类加载顺序

    1. 前言 前文中说到了static关键字,在Java中这是一个很重要的关键字,它有很多的用法,并且在某些特定的情况下使用可以优化程序的性能.本文学习static关键字的应用场景.在这之前了解变量的类 ...

  6. JAVA基础——异常详解

    阅读目录 一.异常简介 二.try-catch-finally语句 三.throw和throws关键字 四.java中的异常链 五.结束语 JAVA异常与异常处理详解 回到顶部 一.异常简介 什么是异 ...

  7. Java基础:详解HashMap在多线程下不安全

    今天想知道HashMap为什么在多线程下不安全,找了许多资料,终于理解了. 首先先了解一下HashMap: HashMap实现的原理是:数组+链表 HashMap的size大于等于(容量*加载因子)的 ...

  8. Java基础知识详解

    一.基础 1.Java都有哪些数据类型?基本数据类型有哪些?分别占多少字节?多少位?引用数据类型又有哪些? Java的数据类型:基本数据类型和引用数据类型 基本数据类型:byte(1),short(2 ...

  9. Java之多线程详解

    Process与Thread(进程和线程) 程序是指令和数据的集合,是一个静态的概念. 进程则是执行程序的一次过程,是动态的概念,是系统资源分配的单位. 通常一个进程中可以包含若干个线程,一个进程中至 ...

最新文章

  1. PHP多台服务实现单点登录,如何在多台服务器上共享Session(PHP/JSP/ASP.NET)以及单点登录(SSO)...
  2. LeetCode移掉k位数字(贪心算法)python
  3. c# 获取方法所在的命名空间 类名 方法名
  4. black.lst 丢失或被破坏,怎么解决
  5. matlab的边缘检测方法,常用图像边缘检测方法及Matlab研究
  6. java 审计 漏洞函数_Java Web代码审计流程与漏洞函数
  7. 【PMP学习笔记】:三、项目经理角色
  8. QT [005] 数据库设计 - 一个被忽略的数据库show类 - ConnectionWidget Class
  9. 【免费毕设】PHP课程网站络管理系统(源代码+论文)
  10. 安卓为什么卡及其解决方案
  11. 对JS中this的理解
  12. python语言月份缩写_Python替换月份为英文缩写的实现方法
  13. ExtJs4 笔记(2) ExtJs对js基本语法扩展支持
  14. 5G笔记| 概述:5G三大应用场景、5G关键技术概览
  15. 50道C/C++编程练习题 复习必备(1-10)
  16. 外接硬盘在电脑中不显示的解决方案
  17. 聚类分析在用户行为中的实例_聚类分析的简单案例
  18. Nagios汉化页面
  19. 电子科技大学计算机学刘峰林,康昭 - 电子科技大学 - 计算机科学与工程学院
  20. ffmpeg每隔1秒抽取视频的1帧画面

热门文章

  1. 监听mysql表内容变化 使用canal,canal 监听同步指定数据库,所有表
  2. 潜意识吸引!如何成功钓到心仪妹子
  3. pdf如何转换成word文档教程
  4. 支持向量机算法之鸢尾花特征分类【机器学习】
  5. 如何快速将F1~F12热键恢复成功能键?(直接避免叩击F9熄屏)
  6. Tacacs-协议交互报文抓包示例
  7. 目标检测模型YOLO-V1损失函数详解
  8. linux下路径怎么写,linux相对路径怎么写 linux的相对路径怎么表示?
  9. 基于java的疫情数据查看系统与实现
  10. 构建OPC UA 可执行模型(1)