java多线程

线程与进程

  • 进程
    一个进程就是CPU执行的单个任务的过程,是程序在执行过程当中CPU资源分配的最小单位,并且进程都有自己的地址空间,包含了运行态、就绪态、阻塞态、创建态、终止态五个状态。

  • 线程
    线程是CPU调度的最小单位,它可以和属于同一个进程的其他线程共享这个进程的全部资源

  • 两者之间的联系
    一个进程包含多个线程,一个线程只能在一个进程之中。每一个进程最少包含一个线程。

  • 两者之间的区别
    进程是CPU资源分配的最小单位,线程是CPU调度的最小单位

进程之间的切换开销比较大,但是线程之间的切换开销比较小。

CPU会把资源分配给进程,但是线程几乎不拥有任何的系统资源。因为线程之间是共享同一个进程的,所以线程之间的通信几乎不需要系统的干扰。

核心概念:
  • 线程是独立的执行路径;
  • 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程、gc线程;
  • main()是主线程,系统的入口,用于执行整个程序;
  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关,先后顺序不能人为干预;
  • 对同一份资源操作时会存在资源抢夺问题,需要加入并发控制;
    线程会带来额外开销,如CPU调度时间,并发控制开销;
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。

------------------------------------------------我是分割线,今天是阴天-------------------------------------------------------

线程的创建

  1. 继承Thread类(重点),重写run()方法,调用start()方法启动
  2. 实现Runnable接口(常用),与方法一的区别是不用继承Thread类,而是实现Runnable。启动方式需要将实现Runnable接口的实例化后放入Thread类中。
  3. 实现Callable接口(了解),好处是可以定义返回值和抛出异常

线程池

提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁创建销毁、实现重复利用。
线程池的好处:

  • 提高响应速度(减少了创建新线程的时间)
  • 降低资源消耗(重复利用线程池中线程,不用每次创建)
  • 便于线程管理
    corePoolSize:核心池的大小
    maximumPoolSize:最大线程数
    keepAliveTime:线程没有任务时最多保持多长时间后终止

实现方法:通过接口 ExecutorService 和 工具类 Executors 的 newFixedThreadPool()方法。

方法说明
newFixedThreadPool(int nThreads)指定工作线程数量的线程池
newCachedThreadPool()处理大量短时间工作任务的线程池
1、试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;
2、如果线程闲置的时机超过阈值,则会被终止并移除缓存;
3、系统长时间闲置的时候,不会消耗什么资源
newSingleThreadExecutor()创建唯一的工作者线程来执行任务,如果线程异常结束,会有另一个线程取代它。
newSingleThreadScheduledExecutor() 和 newScheduledThreadPool(int corePoolSize)定时或者周期性的工作调度,两者的区别在于单一工作线程还是多个线程。
newWorkStealingPool()内部会构建 ForkJoinPool,利用 working-stealing 算法,并行地处理任务,不保证处理顺序。JDK1.7 及以上版本 Java 提供了 Fork/Join 框架,Fork/Join 是把大任务分割成若干个小任务并行执行,最终汇总每个小任务结果后得到大任务结果的框架 。

------------------------------------------------我真的是分割线-------------------------------------------------------

线程状态控制、优先级、守护线程

线程状态

创建状态、就绪状态、阻塞状态、运行状态、死亡状态

方法说明
void setPriority(int newPriority)更改线程的优先级
static void sleep(long millis)在指定的毫秒数内让当前线程休眠
void join()等待该线程终止
static void yield()暂停当前正在执行的线程,并执行其他线程
void interrupt()中断线程(不推荐用这个方式中断)
boolean isAlive()测试线程是否处于活动状态
线程休眠sleep()
  • sleep( 时间 ) 指定当前线程阻塞的毫秒数
  • sleep存在异常 InterruptedException
  • sleep时间达到后线程进入就绪状态
  • sleep可以模拟网络延时,倒计时等
  • 每一个对象都有一个锁,sleep不会释放锁
线程礼让 yield()
  • 礼让线程,即让当前运行状态的线程转为就绪状态
  • 让CPU重新调度,但不一定成功。
线程强制执行 join()

通俗点就是让某个线程插队,直到该线程执行完,其它线程才能执行。

线程状态观察

线程的状态包括:

  • NEW :尚未启动的线程
  • RUNNABLE:在Java虚拟机执行的线程
  • BLOCKED:被阻塞的等待监视器锁定的线程
  • WAITING:正在等待另一个线程执行的线程
  • TIMED_WAITING:等待另一给被指定执行等待时间的线程
  • TERMINATED:已终止的线程

可以通过Thread的getState()方法获取到一个Thread.State类型。

线程优先级

Java线程的优先级用数字表示,从1~10,这三个是默认的:

  • Thread.MIN_PRIORITY = 1;
  • Thread.MAX_PRIORITY = 10;
  • Thread.NORM_PRIORITY = 1;

通过 getPriority() 、setPriority(int)来获取和设置优先级。

注意:优先级低只是意味着获得调度的概率低,可能CPU也会先调用优先级低的,这就是性能倒置。但大多数情况还是先调用优先级高的。同时,设置优先级要在 start() 前面

守护线程 daemon
  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机无需确保守护线程执行完毕
  • 守护线程例如:后台记录操作日志、监控内存、垃圾回收等

通过 setDaemon( boolean ) 来给一个线程设置为守护线程。
------------------------------------------------我是分割线啊-------------------------------------------------------

线程安全

同步方法和同步代码块(synchronized)

synchronized 方法控制对"对象"的访问,每个线程中执行到该方法时,会对拥有该方法的对象上锁直到该方法结束,而其他线程必须等待锁解除才能执行该方法。

Lock锁

前面使用 synchronized 的同步方法和同步代码块都是隐式上锁和解锁,JDK 5.0开始,Java 就提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。

ReentrantLock lock = new ReentrantLock();
lock.lock(); //上锁
…要上锁的代码…
lock.unlock(); //解锁

synchronized与Lock对比:

  1. Lock是显式锁(手动打开、关闭),synchronized是隐式锁,出了作用域自动关闭。
  2. Lock只有代码块锁,synchronized有代码块锁和方法锁。
  3. 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好,且有更好的扩展性。
  4. 优先使用:Lock > 同步代码块 > 同步方法
死锁

即多个线程相互持有对方需要的资源,然后形成僵持。

产生死锁的四个必要条件:

  1. 互斥条件:一个资源每次只能被一个进程使用
  2. 请求与保持条件:一个进程因请求资源而被阻塞,对已获得的资源保持不放
  3. 不可剥夺条件:进程已经获得的资源,在未使用完前不能被强行剥夺
  4. 循环等待条件:若干进程之间形成一种首尾相连的循环等待资源关系

解决方法:多数情况下,消除第四个条件成立
------------------------------------------------我是分割线。-------------------------------------------------------

线程之间的通信

Java 提供了几个方法来解决线程之间的通信问题:

方法作用
wait()表示线程一直等待,直到有其他线程通知(与sleep()不同,可以释放锁)
wait(long timeout)指定等待的毫秒数
notify()唤醒一个处于等待状态的线程
notifyAll()唤醒同一个对象上所有调用wait()方法的线程,优先级高的优先调度

注意:这些都是Object类的方法,只能在同步方法或同步代码块中使用,否则会抛出异常。

本文借鉴土豆的热爱

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值