多线程

1. 进程和线程的概述

进程:正在运行的程序,是系统进行资源分配和调用的独立单位,每一个进程都有它自己的内存空间和系统资源

多进程的意义:提高CPU使用率

线程:是进程中的单个顺序控制流,是一条执行路径

  • 单线程——一个进程如果只有一条执行路径,则称为单线程程序
  • 多线程——一个进程如果有多条执行路径,则称为多线程程序

多线程的意义:提高应用程序的使用率

Java程序运行原理

  • java 命令会启动 java 虚拟机,相当于启动了一个应用程序,即一个进程
  • 该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法
  • 所以 main方法运行在主线程中,在此之前的所有程序都是单线程的

JVM的启动是多线程的,至少启动了主线程和垃圾回收器两个线程

线程是依赖于进程而存在的

2. 多线程的实现方式1

方式1:继承Thread类

步骤:

  1. 自定义类MyThread继承Thread类
  2. MyThread类里面重写run()
  3. 创建线程对象
  4. 启动线程

并不是类中的所有代码都使用多线程,用run()方法来包含使用线程的那部分代码

run()和start()的区别?

  • run()——仅仅是封装被线程执行的代码,直接调用是普通方法
  • start()——首先启动了线程,然后再由jvm去调用该线程的run()方法

同一个线程不能被多次调用

获取和设置线程名称的方法:

  • public final String getName()——获取名字
  • public final void setName(String name)——设置名字(相当于无参构造+Set方法)
  • 在MyThread类中写无参和带参构造(继承自Thread)——设置名字

针对不是Thread类的子类如何获取线程对象名称?

  • public static Thread currentThread()——返回当前正在执行的线程对象

代码:Thread.currentThread().getName()

线程调度

线程的两种调度模型 

分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片

抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些

Java使用的是抢占式调度模型

获取和设置线程优先级的方法:

  • public final int getPriority()——获取线程优先级
  • public final void setPriority(int newPriority)——更改线程优先级

线程优先级范围1~10,默认5

线程优先级仅仅表示优先几率大,不是一定优先

线程控制

线程休眠

  • public static void sleep(long millis) ——指定毫秒内让当前正在执行的线程暂停执行

线程加入

  • public final void join()——等待该线程终止

线程礼让:让多个线程的执行更和谐,但是不能保证一个线程一次

  • public static void yield()——暂停当前正在执行的线程对象,并执行其他线程

后台线程

  • public final void setDaemon(boolean on)——将该线程标记为守护线程,正在运行的线程是守护线程时,java虚拟机退出该方法必须在线程启动前使用

中断线程

  • public final void stop()——具有不安全性,已过时
  • public void interrupt()——将线程状态终止

3. 多线程的实现方案2

方式2:实现Runnable接口

步骤:

  1. 自定义类MyRunnable实现Runnable接口
  2. 重写run()方法
  3. 创建MyRunnable类的对象
  4. 创建Thread类的对象,并把3步骤的对象作为构造参数传递

获取及设置线程名称

  • Thread.currentThread().getName()——只能间接获取,因为不是继承自Thread
  • Thread对象调用setName(String name)——设置线程名称
  • Thread(Runnable target, String name)——带参构造,造对象的时候直接设置名称

方式2的好处:

  • 可以避免由于Java单继承带来的局限性
  • 适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想

4.线程安全问题

产生的原因:

  1. 是否是多线程环境
  2. 是否有共享数据
  3. 是否有多条语句操作共享数据

解决多线程安全问题

基本思想:让程序没有安全问题的环境

对于问题3的解决实现:把多个语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可

实现方式1:同步代码块

格式

synchronized(对象){需要同步的代码;}

关于对象:

  • 该对象是解决线程安全问题的根本原因,类似于锁的功能
  • 多个线程只能有一把锁,即一个对象,故不能直接在()中造对象,而应该在之前就造好
  • 这个对象可以是任意对象

关于需要同步的代码:

其实就是问题原因3里的代码

同步的前提

  • 多个线程
  • 多个线程使用的是同一个锁对象

同步的好处:同步的出现解决了多线程的安全问题

同步的弊端:当线程很多时,因为每个线程都会去判断同步上的锁,耗费资源,会降低程序的运行效率

实现方式2:同步方法

格式:就是将同步关键字加到方法上

  • 同步代码块的锁对象是任意对象
  • 同步方法的锁对象是this
  • 静态同步方法的锁对象是类的字节码文件对象

实现方式3:Lock实现

为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock

  • void lock() ——获取锁
  • void unlock()——释放锁

实现类:ReentrantLock

同步中的死锁问题

同步弊端

  1. 效率低
  2. 如果出现了同步嵌套,就容易产生死锁问题

死锁问题是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象

线程间通信问题:针对同一个资源的操作有不同种类的线程(生产者和消费者多线程体现)

注意:

  1. 不同种类的线程都要加锁
  2. 不同种类的线程加锁必须是同一把

Java提供的等待唤醒机制

Object类中提供了三个方法,因为这些方法的调用必须通过锁对象调用,而锁对象可以是任意对象,所有必须定义在Object类中

  • wait()——等待并立即释放锁
  • notify()——唤醒单个线程
  • notifyAll()——唤醒所有线程

5. 线程组

线程组:将多个线程组合到一起

可以对一批线程组进行分类管理,Java允许程序直接对线程组进行控制,默认情况下,所有的线程都属于主线程组

Thread类中的方法:

  • public final ThreadGroup getThreadGroup() ——返回该线程所属的线程组

ThreadGroup中的方法:

  • public final String getName()——返回此线程组的名称

给线程设置分组

  • Thread(ThreadGroup group, Runnable target, String name)

6. 线程池

程序启动一个新线程成本比较高,因为它涉及到与操作系统进行交互。而使用线程池可以很好的提高性能。线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用

JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法

  • public static ExecutorService newCachedThreadPool()
  • public static ExecutorService newFixedThreadPool(int nThreads)
  • public static ExecutorService newSingleThreadExecutor()

这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。

它提供了如下方法

  • Future<?> submit(Runnable task)
  • <T> Future<T> submit(Callable<T> task) 

7. 匿名内部类的方式实现多线程

匿名内部类的格式:其本质是该类或接口的子类对象

new 类名或接口名(){
    重写方法;
}

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值