个人博客网:https://wushaopei.github.io/    (你想要这里多有)

1、程序、进程、线程的理解

1.1 概念

  • 程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
  • 进程(process)是程序的一次执行过程,或是正在运行的一个程序。动态过程:有它自身的产生、存在和消亡的过程。

如:运行中的QQ,运行中的MP3播放器
           程序是静态的,进程是动态的

  • 线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。

若一个程序可同一时间执行多个线程,就是支持多线程的

1.2 进程与多线程

每个Java程序都有一个隐含的主线程: main 方法

  • 程序需要同时执行两个或多个任务。
  • 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
  • 需要一些后台运行的程序时。

2、多线程的创建和启动

2.1 Thread 线程概念:

2.2 源码流程解析:

(1)Thread 类 与 Runnable接口

并且在Thread中有一个构造器的参数是Runnable的实现子类,所以当一个自定义类实现了Runnable接口后,可以通过newThread()作为构造参数传入来实现Thread多线程的创建。

如图:

(2)Thread 与 Runnable 、Callable 及Future Task 的关系

首先分析:

Future Task的底层实现了RunnableFuture 接口:

RunnableFuture 的底层实现了两个接口: Runnable , Future

而根据FutureTask 的构造方法和Callable 的类型可知:

由以上可以得知,通过自定义实现了Callable 接口的实例对象做为FutureTask 的构造器形参,然后因为实现了Runnable 接口的缘故,该对象可以做参数传入Thread对象的构造器中,用来创建新的多线程 thread ,并使用 start ()启动线程。

2.3 案例:mt子线程的创建和启动过程

3、线程中常用的API

interrupt() : 中断线程(也会将当前线程中的任务执行完毕会进行线程的一个标记,标记该线程是要被停止。
isInterrupted() :测试此线程是否已被中断。(如果线程的标记被标识成中断则返回true)

3、创建多线程的四中种方式

3.1 继承Thread

  1. 自定义一个类并继承Thread
  2. 重写run方法
  3. run方法中可以写入需要在分线程中执行的代码
  4. 创建Thread子类的对象
  5. 通过对象调用start方法

3.2 实现Runnable接口

  1. 自定义一个类并实现Runnable接口
  2. 重写run方法
  3. 在run方法中写入需要在分线程中执行的代码
  4. 创建Runnable实现类的对象
  5. 创建Thread对象并将Runnable实现类的对象作为实参传入到Thread的构造器中
  6. 通过Thread对象调用start方法

3.3 实现Callable接口

  1. 自定义一个类,并实现Callable接口
  2. 重写call方法,并返回结果
  3. 在call方法中实现需要在分线程中执行的代码
  4. 创建Callable接口的实现类的对象
  5. 创建FutureTask对象,并将Callable接口的实现类的对象作为实参传到FutureTask的构造器中
  6. 创建Thread对象,并将FutureTask对象作为实参传入到Thread构造器中
  7. 调用start方法

代码:

public class ThreadTest {

	public static void main(String[] args) throws Exception {
//4.创建Callable接口的实现类的对象
MyCallble myCallble = new MyCallble();
//5.创建FutureTask对象,并将Callable接口的实现类的对象作为实参传到FutureTask的构造器中
FutureTask<Integer> futureTask = new FutureTask<>(myCallble);
//6.创建Thread对象,并将FutureTask对象作为实参传入到Thread构造器中
Thread thread = new Thread(futureTask);
//7.调用start方法
thread.start(); //阻塞主线程,等待分线程返回数据结果,然后再向下执行。
Integer intNumber = futureTask.get(); System.out.println("===" + intNumber);
System.out.println("aaaaaaaaaaaaaaaaaaa");
}
}

//1.自定义一个类,并实现Callable接口
class MyCallble implements Callable<Integer>{ //2.重写call方法,并返回结果
@Override
public Integer call() throws Exception {
/*
* 3.需要在分线程中执行的代码
*/
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " == " + i);
}
return 100;
} }

3.4 线程池

                //创建一个线程池,根据需要创建新的线程,但在可用时将重用先前构建的线程。
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
//创建一个线程池,使用固定数量的线程操作了共享无界队列。
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3);
//创建一个执行器,使用一个单一的工作线程操作关闭一个无限的队列。
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
newCachedThreadPool.execute(new Runnable() { @Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "====" + i);
}
}
});
//关闭线程池
newCachedThreadPool.shutdown();

4、Thread的生命周期

要想实现多线程,必须在主线程中创建新的线程对象。Java语言使用Thread类及其子类的对象来表示线程,在它的一个完整的生命周期中通常要经历如下的五种状态:

  • 新建: 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
  • 就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件
  • 运行:当就绪的线程被调度并获得处理器资源时,便进入运行状态, run()方法定义了线程的操作和功能
  • 阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时中止自己的执行,进入阻塞状态
  • 死亡:线程完成了它的全部工作或线程被提前强制性地中止

5、线程的同步机制

5.1 为什么要使用线程的同步机制?

多个线程在同时操作共享数据的时候可能会发生线程安全问题。

比如卖票:会发生重票,0票,负票的问题

5.2 解决线程安全问题的方式?
解决方法一 :同步代码块 :
格式 :

         synchronized (同步监视器) {
//操作共 享数据的代码
}

1.同步监视器 : 可以是任何对象
    注意 : 多个线程必须使用的是同一把锁(必须保证多个线程使用到的同步监视器是同一个对象)。
2.同步代码块内 : 操作共享数据的代码
(实现Runnable使用同步代码块案例 : 详见RunnableTest.java)
        
解决方法二:  同步方法
          格式 :

        public synchronized void say(){}  -- 修饰普通方法

默认的同步监视器是:this

           public static synchronized void say(){} -- 修饰静态方法

默认的同步监视是运行时类的对象。 例:Person.class

6、线程的单例模式

class Bank {
private Bank() {
}
private static Bank bank = null;
public static Bank getInstance() { if (bank == null) {
synchronized (Bank.class) {
if (bank == null) {
bank = new Bank();
}
}
} return bank;
}
}

7、死锁的问题

死锁的原因:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
代码:

                //创建两把锁
StringBuffer s1 = new StringBuffer();
StringBuffer s2 = new StringBuffer(); new Thread() {
public void run() {
synchronized (s1) {
s2.append("A");
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (s2) {
s2.append("B");
System.out.print(s1);
System.out.print(s2);
}
}
}
}.start(); new Thread() {
public void run() {
synchronized (s2) {
s2.append("C");
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (s1) {
s1.append("D");
System.out.print(s2);
System.out.print(s1);
}
}
}
}.start();

8、线程通信

8.1 线程通信涉及到三个方法:

注意 :

  1. 上面个方法只能用在同步代码块和同步方法中
  2. 调用个方法实际上是调用监视对象中的方法

8.2 [面试题] wait和sleep的区别?

  1. sleep睡觉的时候会抱着锁。wait睡觉的时候会释放锁。
  2. sleep时间一到就自动唤醒,wait需要被其它线程调用notify/notifyAll才能唤醒。
  3. sleep是Thread中的方法, wait是Object中的方法。

8.3 [面试题] 继承Thread和实现Runnable的区别?

继承Thread :

          同步监视器 : 不可以使用this
          共享资源 : 需要使用static关键字修饰。
          单继承
          public static synchronized void say() - 默认锁是运行时类的对象。 比如 : Person.class

实现Runnable

        同步监视器 : 可以使用this
        共享资源 : 不需要使用static关键字修饰。
        多实现
        public synchronized void say(){} - 默认锁是this

9、同步的优点和缺点

  • 同步的优点 : 解决了线程安全问题
  • 同步的缺点 : 在同步代码块和同步方法的方法体执行时,只能有一个线程在执行。效率低。

10、ReentrantLock

代码:

class MyRunnable2 implements Runnable {
// 同一个类的多个对象共同拥有一份类变量
private int ticket = 100;
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() { while (true) {
lock.lock(); //锁住当前的线程
try {
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " ==== " + ticket);
ticket--; } else {
return;
}
} finally {
//最后一定要手动解锁
lock.unlock();
}
}
}
}

注意 :ReentrantLock使用到的线程通信不是使用的wait()和notify()

JAVASE(十七) 多线程:程序、进程、线程与线程的生命周期、死锁、单例、同步锁的更多相关文章

  1. Java多线程——线程的优先级和生命周期

    Java多线程——线程的优先级和生命周期 摘要:本文主要介绍了线程的优先级以及线程有哪些生命周期. 部分内容来自以下博客: https://www.cnblogs.com/sunddenly/p/41 ...

  2. ☕【Java技术指南】「难点-核心-遗漏」Java线程状态流转及生命周期的技术指南(知识点串烧)!

    前提介绍 本章主要介绍相关线程声明周期的转换机制以及声明周期的流转关系以及相关AQS的实现和相关的基本原理,配合这相关官方文档的中英文互译的介绍. 线程状态流转及生命周期 当线程被创建并启动以后,它既 ...

  3. [转]Java 对象锁-synchronized()与线程的状态与生命周期

      线程的状态与生命周期 Java 对象锁-synchronized() ? 1 2 3 4 synchronized(someObject){   //对象锁 } 对象锁的使用说明: 1.对象锁的返 ...

  4. 【微信小程序开发•系列文章六】生命周期和路由

    这篇文章理论的知识比较多一些,都是个人观点,描述有失妥当的地方希望读者指出. [微信小程序开发•系列文章一]入门 [微信小程序开发•系列文章二]视图层 [微信小程序开发•系列文章三]数据层 [微信小程 ...

  5. 微信小程序把玩(五)页面生命周期

    原文:微信小程序把玩(五)页面生命周期 这里只要熟悉页面的基本生命周期即可,业务在指定生命周期函数内书写. 以下是官网给出的生命周期函数方法和状态图 上面的生周期函数图对于做Android 或者IOS ...

  6. 微信小程序把玩(四)应用生命周期

    原文:微信小程序把玩(四)应用生命周期 App() 函数用来注册一个小程序,注意必须在 app.js 中注册,且不能注册多个. 使用方式也跟Android中的Application中初始化一些全局信息 ...

  7. 多线程程序 怎样查看每个线程的cpu占用

    可以用下面的命令将 cpu 占用率高的线程找出来: ps H -eo user,pid,ppid,tid,time,%cpu,cmd --sort=%cpu 这个命令首先指定参数'H',显示线程相关的 ...

  8. System、应用程序进程的Binder线程池和Handler消息循环

    首先看一张Android系统启动流程图:

  9. Java多线程2:线程的使用及其生命周期

    一.线程的使用方式 1.继承Thread类,重写父类的run()方法 优点:实现简单,只需实例化继承类的实例,即可使用线程 缺点:扩展性不足,Java是单继承的语言,如果一个类已经继承了其他类,就无法 ...

随机推荐

  1. 【Scala】用实例弄清楚scala几种函数的定义和特点

    文章目录 作为参数的函数 匿名函数 柯里化函数(currying) 闭包函数 作为参数的函数 scala> val a1 = Array(1,2,3,4) //这是一个数组 a1: Array[ ...

  2. STM32 标准库3.5修改默认外部8M晶振为16M晶振

    ST官方标准库V3.5默认的外部晶振频率为8M,实际使用中外部晶振需要修改为16M: 经过实验,修改有效,具体的patch如下: 修改 HSE_VALUE 值 diff --git "a/L ...

  3. [hdu5379 Mahjong tree]dfs计数

    题意:给n个节点的树编号1-n,一个节点唯一对应一种编号,要求编完号的树满足如下性质:所有节点的儿子的编号是连续的,对一棵子树,它包含的所有节点的编号也是连续的.连续的意思是把所有数排序后是一段连续的 ...

  4. 【csu oj 1542】线段树

    题目大意:给定一个合法的括号序列(只包含'(',')'),有q次操作,对每次操作改变一个位置的括号,求最左端的位置,使得改变这个位置上的括号以后,新序列合法(完全配对). 思路:对于合法的括号序列,如 ...

  5. Git、Github习笔记01——Git本地仓库

    作者:Eventi 出处:http://www.cnblogs.com/Eventi 欢迎转载,也请保留这段声明.谢谢! git简介 版本控制软件,由Linus(linux开发者)开发,最初用来对li ...

  6. c#得出两个列表的交集

    c#提供了Intersect来得到两个列表的交集,它是通过使用默认的相等比较器对值进行比较生成两个序列的交集,定义为: public static IEnumerable<TSource> ...

  7. Linux常用命令详解—基于CentOS7

    ## Linux 目录- /:根目录,一般只存放目录,不存放文件- /bin -> /usr/bin:可执行二进制文件的目录,也是常用命令目录,如常用的命令 ls.cat.mv 等- /boot ...

  8. Vi 和 Vim 的使用

    Vi (Visual Interface)是 Linux下基于Shell 的文本编辑器,Vim (Visual Interface iMproved)是 Vi的增强版本,扩展了很多功能,比如对程序源文 ...

  9. Tensorflow从0到1(一)之如何安装Tensorflow(Windows和Linux两种版本)

    现在越来越多的人工智能和机器学习以及深度学习,强化学习出现了,然后自己也对这个产生了点兴趣,特别的进行了一点点学习,就通过这篇文章来简单介绍一下,关于如何搭建Tensorflow以及如何进行使用.建议 ...

  10. Django之ORM多表增删改操作

    关系表的操作语句: 以上一节中创建的书籍.出版社.作者.作者信息表为例进行: 增: # 一对一 # (1)类属性外键关联,使用外键约束属性直接进行对象关联插入 author_detail_obj=mo ...