JUC系列(一)-线程

106 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第19天,点击查看活动详情

前言

项目中我们经常会用到使用到多线程,多线程也是比较难的难点,关于并发编程的相关知识如下:

图片.png

本文将详细讲解JUC的基础之线程。

进程与线程之间关系

一个操作系统中可以有多个进程,一个进程中可以包含一个线程(单线程程序),也可以包含多个线程(多线程程序)。

图片.png

进程

进程是一个在内存中运行的应程序,每个进程都有自己独立的一块内存空间,一个进程可以有多个线程。比如window的一个运行的程序就是一个进程。

图片.png

线程

  • 线程是轻量级的进程,是程序执行的最小单元,使用多线程而不是多进程去进行并发程序的设计,是因为线程间的切换和调度的成本远远小于进程。
  • 在同⼀个进程内可以执⾏多个任务,⽽这每⼀个任务我们就可以看做是⼀个线程 ⼀个进程会有1个或多个线程的。多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈。

进程与线程区别

  • 资源开销:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。

  • 包含关系:如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。

  • 内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的

  • 影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。

  • 执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行

创建线程的几种方式

创建线程有三种方式,分别是Thread、Runnable、Callable.

thread创建线程

  Thread thread = new Thread(()->{
      System.out.println("this is thread");   
    });
    thread.start();

通过Thread创建线程,start()方法启动线程。

实现Runnable

 MyRunnable runnable =new MyRunnable();
 Thread runableThread =new Thread(runnable);
 runableThread.start();

通过实现Runnbale接口来创建线程.

实现Callable

public class CallableTest<String> implements Callable<String>
{
    private String str;
    
    CallableTest(String s){
       this.str = s;   
    }
    
    @Override
    public String call() throws Exception
    {
        return this.str;
    }

}

//
Callable<String> callable =
             new CallableTest<String>("this my callable");
     
 FutureTask<String> task = new FutureTask<String>(callable);
 Thread futureThread =new Thread(task);
 futureThread.start();
  System.out.println(task.get());

说明:因为Thread只能接收Runnable接口,而Callable并为实现Runnable接口,需要通过FutureTask类作为简单适配类。

图片.png

线程状态

java线程的状态分为:New(新建)、RUNNABLE(可运行)、RUNNING(运行中)、BLOCKED(阻塞)、Dead(死亡)

线程状态的转换如下图:

图片.png

线程常用方法

线程常用方法包含了join、yield、sleep等方法。

Join

线程A在运行时,线程Bjoin(),之后,线程A会等待直到线程B运行完之后,才开始执行。

相关示例:

public class JoinAThread extends Thread
{
    @Override
    public void run() {
        try {
            JoinBThread b = new JoinBThread();
            b.start();
            b.join();//B线程join之后,A线程需要等待B运行完之后才执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程A执行任务。。。。。。。");
    }
}

public class JoinAThread extends Thread
{
    @Override
    public void run() {
        try {
            JoinBThread b = new JoinBThread();
            b.start();
            b.join();//B线程join之后,A线程需要等待B运行完之后才执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程A执行任务。。。。。。。");
    }
}

public class ThreadJoinTest
{
  public static void main(String[] args)
{
    JoinAThread a =new JoinAThread();
    a.start();
}
}

执行结果:

图片.png

Yield

Yield的作用是让正在执行的线程让出CPU的执行权限,使得CPU可以去执行其他任务。

相关示例:

public class ThreadA implements Runnable
{
    @Override
    public void run()
    {
        Thread.yield();
        System.out.println("this thread name:"+Thread.currentThread().getName());
    }
}

public class ThreadB implements Runnable
{
    @Override
    public void run()
    {
        System.out.println("this thread name:"+Thread.currentThread().getName());
    }
}

public class ThreadYield
{
  public static void main(String[] args)
    {
        ThreadA a =new ThreadA();
        Thread threadA =new Thread(a);
        threadA.setName("tA");
        
        ThreadB b =new ThreadB();
        Thread threadB =new Thread(b);
        threadB.setName("tB");
        
        threadA.start();
        threadB.start();
    }
}

执行结果:

图片.png

Sleep

主要是让当前线程“睡眠”指定时间的。

总结

本文对于JUC的线程进行详细的讲解,如有疑问请随时反馈。