【Java高并发系列】之入门篇

大家好,我是小菜,一个渴望在互联网行业做到蔡不菜的小菜。可柔可刚,点赞则柔,白嫖则刚!
死鬼~看完记得给我来个三连哦!

本文主要介绍 Java并行程序基础
如有需要,可以参考
如有帮助,不忘 点赞

什么是线程

在了解什么是线程之前,我们先了解一下什么是进程

进程是线程的母亲,进程是系统进行资源分配和调度的基本单位,是操作系统结构的基础。进程是线程的容器,进程中可以容纳多个线程。

简单来讲就是:一间房子相当于一个容器,也就是进程的概念。这间房子住着你和你的父母,三个人就是分别的线程。你上学,妈妈干家务,爸爸上班,做着不同的事情,维持的进程的运行。

线程就是轻量级的进程,是程序执行的最小单位,使用多线程而不是多进程来进行并发程序的设计,是因为线程间的切换和调度的成本远远小于进程线程状态图

线程所有状态的State如下:线程状态图

NEW状态表示刚刚创建的线程,这种线程还没开始执行。等到线程的start()方法调用时,才表示线程刚开始执行。当线程执行时,处于RNNNABLE状态,表示线程所需的一切资源都已经准备好了。如果线程在执行过程中遇到了synchronized同步块,就会进入BLOCKED阻塞状态,这是线程就会暂停执行,直到获得请求的锁。WAITTINGTIMED_WAITING都表示等待状态,它们的区别是WAITING会进入一个无时间限制的等待,TIMED_WAITING会进行一个有时间限制的等待。一般来说,WAITING的线程正是在等待一些特殊的时间。比如,通过wait()方法等待的线程在等待notify()方法,而通过join()方法等待的线程则会等待目标线程的终止。一旦等待了期望的时间,线程就会再次执行,进入RUNNABLE状态。当线程执行完毕后,则进入TERMINATED状态,表示结束。

线程的基本操作

新建线程

使用 new 关键字创建一个线程对象,并调用 start() 方法即可

Thread thread = new Thread();
thread.start();

start()方法执行后,实际上会调用线程中的run()方法,start()方法就会新建一个线程并让这个线程执行run()方法。

Thread thread = new Thread();
thread.run();

如果直接调用线程中的run()方法,那么只是作为一个普通方法的调用。

不要用 run() 方法来开启新线程,它只会在当前线程中 串行 执行 run() 方法中的代码。

按照上面的方法新建线程,实际上是没有执行任何操作的,如果我们要实现自定义的方法,那么就要重写run()方法,把自定义的任务传进去。这个时候就可以使用Thread的另外一个构造方法了。

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("这里定义自定义的任务");
    }
});
thread.start();

Runnable接口是一个单方法接口,它只有一个 run()方法

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
终止线程

一般来说,线程执行完毕就会结束,不需要手动关闭。但是如果一些服务端的后台线程常驻系统后台,它们通常不会正常终结。它们的执行体本身就是一个大大的无穷循环,用于提供某些服务。

我们可以使用 stop()将一个线程立刻终止

通过图片我们可以看到stop()方法已经被注解@Deprecated标记,说明是已经废弃的方法。这是因为stop()方法过于暴力,它会强行把执行到一半的线程终止,可能会引起一些数据不一致的问题。所以除非你很清除自己在做什么,否则不要随便使用stop()方法来停止一个线程。

我们可以定义一个标识,来控制线程何时退出

线程中断

线程中断是一个重要的线程协作机制,线程中断并不会向stop()方法那样使线程马上停止,而是给线程发送一个通知,告知目标线程,你可以退出了。Thread中提供了三个与线程中断有关的方法。

public void interrupt() {}					//中断线程
public boolean isInterrupted() {}			//判断是否被中断
public static boolean interrupted() {}		//判断是否被中断,并清除当前中断状态

这种方法看起来与前面设置 stopSelf()方法相似,但是中断的功能更为强劲,如果循环体中,出现了类似wait()方法或者sleep()方法这样的操作,则只能通过中断来识别了。

等待(wait)和通知(notify)

为了支持多线程之间的协作,JDK提供了两个非常重要的接口线程:等待wait()方法和通知notify()方法。这两个方法并不是 Thread 类中的,而是在 Object 类中。这就意味着任何对象都可以调用这两个方法。

当 A 线程调用了 wait()方法后,A 线程就会停止执行转为等待状态。一直等到其他线程调用了 notify()方法为止。

如果一个线程调用了wait()方法后,就会进入 object 对象的等待队列。这个等待队列可能会有多个线程。因为系统运行了多个线程同时等待某一个对象。当有其他线程调用notify()方法后,会随机的从这个等待队列中选择一个线程进行唤醒,这个选择是不公平的,并不是先等待的线程就会优先选择。

注意:wait() 方法并不能随便调用,它必须包含在对应的synchronized语句中,因为无论是 wait() 方法还是 notify() 方法都需要获取目标对象的一个监视器,在 wait() 方法执行后,会释放这个监视器。

*wait()方法和sleep()的区别就是:*wait() 方法会释放目标对象的锁,而 sleep() 则不会

挂起(suspend)和继续执行(resume)线程

挂起(suspend) 和 继续执行(resume)这两个操作是一对相反的操作,被挂起的线程,必须要等到resume()方法操作后才能继续执行。当时这两个也是被标注为废弃的方法。这是因为suspend()方法在导致线程暂停的同时,并不会释放任何资源,此时,其他任何线程想要访问被它占用的锁时,都会被前两,导致无法正常继续执行。直到对应的线程上进行了resume()方法操作,被挂起的线程才能继续执行,从而其他所有阻塞在相关锁上的线程也可以继续执行。

重要的是这两个方法不能保证有序性。也就是说resume()方法可能会发生在suspend()方法的前面,那么结果就是,挂起线程的锁永远不会被释放,进入死锁状态,将会导致整个系统工作不正常!

等待线程结束(join) 和 谦让(yeild)

有些时候,一个线程的输入可能需要依赖其他线程的输出,这个时候,这个线程就需要等待其他线程执行完成才能继续执行。那么就需要通过join()方法来协助。JDK中有两个join()方法:

public final void join() throws InterruptedException 
public final synchronized void join(long millis) throws InterruptedException

第一个join()方法表示无限等待,它会一直阻塞线程,直到目标线程执行完毕。第二个方法给出了一个最大等待时间,如果超过给定时间目标线程还在执行,当前线程就会停止等待,继续往下执行。

在主函数中,如果不使用join()方法等待 AThread 线程执行结束那么打印 i 的结果是0,使用了 join()方法后,打印 i 的结果是10000。

yield()方法则是一个静态方法,它会使当前线程让出CPU。但是注意的是,让出CPU并不表示当前线程不执行了,当前线程让出CPU后,还会进行CPU资源的争夺,但是是否能够再次分配到就不一定了。

如果你觉得一个线程不那么重要,或者优先级非常低,而且又害怕它会占用太多的CPU资源,那么可以在适当的时候调用yield()方法,给予其他重要线程更多工作机会。

线程组的使用

守护线程(Daemon)

守护线程是一种特殊的线程,它是系统的守护者,在后台默默地完成一些系统性的服务,如垃圾回收线程JIT线程就可以理解为守护线程。

与之相对应的就是用户线程,用户线程可以认为是系统的工作线程,它会完成这个程序应该要完成的业务操作。如果用户线程全部完成,那么这个程序实际上就无事可做了,守护线程要守护的对象已经不存在了,整个应用程序就应该结束。因此,当一个 Java 应用内只有守护线程时,Java 虚拟机就会自然退出。

守护线程的使用:

线程优先级

Java 中线程可以有自己的优先级,优先级高的线程在竞争资源时会有更多的优势,更可能抢占资源,当然这也是一个概率问题,有些时候设置高优先级的线程并不管用。

在 Java 中使用 1-10 来表示线程优先级,一般可以使用内置的三个静态标量表示:

public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;

上图中,将 A 线程的优先级设置为 10,B 线程的优先级设置为1,可以看到最后先打印的是 “A 线程执行完毕”,但是因为这种方法有概率性的,因为并不会总是打印 “A 线程执行完毕”。

看完不赞,都是坏蛋

今天的你多努力一点,明天的你就能少说一句求人的话!

我是小菜,一个和你一起学习的男人。 💋

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值