多线程知识篇

简介: 多线程知识篇

多线程


线程

  • 进程有多个子任务,每一个子任务就是一个线程

进程

  • 简单理解,正在运行的程序或软件

线程与进程之间的关系

  • 线程依赖于进程而存在
  • 一个进程里面至少有1个线程
  • 用迅雷下载多个电影的例子
  • 线程共享进程资源

串行

  • 一个任务接一个任务按顺序执行

并行

  • 多个任务,在同一时刻(时间点)同时执行

并发

  • 多个任务,在同一时间段内同时执行

同步与异步

指的是被调用者

A调用B

同步: A的本次调用可以得到结果 (你走我不走)

异步: A的本次调用不会得到结果,等有了结果之后再通知A (你走你的我走我的)

Jvm是多线程的,至少有两个线程,main方法和GC回收垃圾

java采用的是抢占式的线程调度方式,优先级是1-10


多线程的实现方式一:继承Thread类


步骤

  1. 定义一个类继承Thread类
  2. 重写run方法
  3. 创建子类对象
  4. 通过start方法启动

注意事项

对象才代表一个线程,和类无关

一个线程不能多次启动

如果调用run方法就相当于普通的方法调用,并不是真正的启动线程

获取和设置线程名称

获取线程名称

String getName() 返回该线程的名称

获取主线程名称

static Thread currentThread() 返回对当前正在执行的线程对象的引用

设置线程名称

void setName(String name) 改变线程名称,使之与参数 name 相同。


线程控制API


线程休眠sleep

static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。

线程加入(合并) join

void join() 等待该线程终止。

哪个线程调用join,其他线程就必须等待该线程执行完才能执行

守护线程daemon

线程分类

  • 用户线程
  • 守护线程(为用户线程服务的) GC垃圾回收线程就是一个守护线程
void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。
当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。

当所有的线程中只剩下守护线程的时候,程序终止


多线程的实现方式二:实现Runnable接口


步骤

  1. 实现Runnable接口
  2. 重写run方法
  3. 创建子类对象
  4. 创建Thread对象,并且把实现了Runnable接口的子类对象作为参数传递
  5. start方法启动

注意

为什么Runnable中的run方法会运行在子线程当中

因为在Thread类中有一个target来判断我们传过来的Runnable接口的子类有没有重写run方法,如果有的话就执行

建议使用Runnable来实现多线程


解决多线程数据安全问题


产生的原因

  • 多线程的运行环境(需求不能改)
  • 多线程共享数据(需求不能改)
  • 存在非原子操作
  • 什么是原子操作? 一个操作要么1下完成,要么不完成

只能通过原子操作来解决多线程的安全问题

synchronized

同步代码块

锁对象 可以是任意的java对象(但是一定要保证是同一个)

同步方法

锁对象是this

静态方法

锁对象是类的字节码文件对象

细节

  • 同步代码块中的锁对象可以是任意java对象,任意java对象都可以充当锁对象的这个角色,仅限于同步代码块当中
  • 任意java对象内部,都存在这一个标志位,标志位用来表示加锁和释放锁

我们的代码是运行在某条执行路径下(某个线程),当某个线程要执行同步代码块

  • 访问之前会尝试对锁对象加锁,如果没有加锁,可以访问执行
  • 如果别的线程想要访问这个代码块,不能执行, 会处于阻塞状态
  • 当这个线程访问完了同步代码块,退出之前,会释放锁,修改标志位

lock

不建议使用

使用方法:

在要上锁的代码前使用:lock.lock()

执行完毕后使用解锁:lock.unlock()

但一般要配合finally使用,因为不能自动关闭,所以通常是使用synchronized来实现同步


死锁


2个或以上线程争抢资源而造成的互相等待的现象就被称之为死锁

死锁产生的场景

一般出现在嵌套同步代码块中,嵌套的顺序不一致就会存在死锁

// 同步代码块嵌套
synchronized(objA){
  synchronized(objB){
        // 代码
    }
}

死锁的解决方法

1.更改加锁顺序

2.在外面加一把大锁,变成原子操作


线程间通信


wait

  1. 阻塞功能:
    当在某线程中,对象上.wait(), 在哪个线程中调用wait(), 导致哪个线程处于阻塞状态
  2. 唤醒条件
    在其他线程中,在同一个对象(即对象A)上调用其notify()或notifyAll()
  3. 运行条件
    当前线程必须拥有此对象监视器。
    监视器:指synchronized代码块中的锁对象
    即我们只能在当前线程所持有的synchronized代码块中的锁对象上调用wait方法,才能正常执行
    如果我不在同步代码块中调用就会有这样一个异常
    IllegalMonitorStateException
  4. 执行特征
    执行wait的时候会释放监视器,即释放锁
    注意:Thread的sleep方法,执行的时候:
    该线程不丢失任何监视器的所属权

notify

  • 唤醒在此对象监视器上等待的单个线程。
  • 如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。
  • 选择是任意性的

notifyAll

唤醒所有等待的线程

完整的线程的状态转换



多线程工具


线程池

使用线程池的原因:run方法执行完了之后线程就死了,需要重新创建,因此需要引入线程池

//JDK5提供了一Executors来产生线程池,有如下方法:
ExecutorService newCachedThreadPool()
// 特点:
// 1.会根据需要创建新线程,也可以自动删除,60s处于空闲状态的线程
// 2.线程数量可变,立马执行提交的异步任务(异步任务:在子线程中执行的任务)
// 适用场景: 执行很多短期异步的小程序或负载较轻的服务器
ExecutorService newFixedThreadPool(int nThreads)
// 特点:
// 1.线程数量固定
// 2.维护一个无界队列(暂存已提交的来不及执行的任务)
// 3.按照任务的提交顺序,将任务执行完毕  
// 适用场景: 执行长期的任务
ExecutorService newSingleThreadExecutor()
// 特点:
// 1.单个线程
// 2.维护了一个无界队列(暂存已提交的来不及执行的任务)
// 3.按照任务的提交顺序,将任务执行完毕
// 适用场景: 一个任务接一个任务执行

线程池的使用:ExecutorService(接口)

Future submit(Callable task)

Future<?> submit(Runnable task)

停止线程池:

shutdown() 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。

shutdownNow() 停止目前活动的所有任务

线程池的使用步骤

1.创建线程池

2.submit任务

多线程的实现方式三:实现Callable接口

会得到一个返回值,基本上用于在需要返回值的场景

Future 表示异步计算的结果

不使用线程池,可以用Future来执行Callable

使用future.get()可以获得异步计算的结果

Timer

schedule(TimerTask task, Date time)
schedule(TimerTask task, long delay, long period)//常用
schedule(TimerTask task, Date firstTime, long period)
scheduleAtFixedRate(TimerTask task, long delay, long period)
2跟4的区别 : 追赶特性

TimerTask是一个抽象类,需要子类继承并重写方法才能使用

使用future.get()可以获得异步计算的结果

Timer

schedule(TimerTask task, Date time)
schedule(TimerTask task, long delay, long period)//常用
schedule(TimerTask task, Date firstTime, long period)
scheduleAtFixedRate(TimerTask task, long delay, long period)
2跟4的区别 : 追赶特性

TimerTask是一个抽象类,需要子类继承并重写方法才能使用

相关文章
|
7月前
|
并行计算 调度 UED
为什么要用多线程?
为什么要用多线程?
77 0
|
安全 Java
多线程02
多线程02
|
2月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
58 1
|
6月前
多线程知识
多线程知识
27 1
|
7月前
|
设计模式 安全 Java
多线程问题(三)
多线程问题(三)
39 0
|
7月前
|
Unix Linux 编译器
c++多线程
c++多线程
49 0
|
7月前
|
前端开发 Java
|
7月前
|
C#
[C#] 多线程的使用
[C#] 多线程的使用
48 0
|
监控 Java API
多线程专题
多线程专题