多线程的基本概念

什么是进程?

程序(program):就是一个指令的集合
进程(process): 就是正在计算机正在执行的程序。进程是一个静态的概念。
进程是程序一次静态执行的过程,占用特定的地址空间,也就是资源(cpu,内存,磁盘)。
每个进程都是相互独立的,由三个部分组成,cpu,data,code
缺点:进程太多会对内存有所浪费,对cpu造成一定的负担。
优点:安全并且稳定,一个进程的生于死并不会对其他线程造成任何影响,每一个进程都是独立的地址空间。
(查看进程的方法:右键桌面下方的工具栏,打开任务管理器)


什么是线程?

线程(Thread):是进程的一个“单一的连续控值流程” 或者执行路径。
首先线程绝对不是所谓的并行运行,根本上还是串行运行,只是再来回的切换,而每一个线程执行的时间由时间片所决定。

举个例子:迅雷的下载,现在迅雷下载了3个视频,3个视频的是同时任务获取,任务创建,连接,然后再下载的么?不是!我们会发现有的文件下载会很快,有的任务死慢。因为每一个任务都是一个单独的线程去操控,而这三个下载的线程归属于迅雷下载这个进程。进程会占用资源,线程去共享进程中的资源。

线程又叫做轻量级的进程。线程的彼此之间是相互独立的。但是线程如果对同一份文件同一份数据进行操作,那么就不是相互独立的了。要保证数据的一致性,所以线程之间会有通讯。

一个进程中可以拥有多个并行的线程。

一个进程中的线程共享进程的内存单元/地址空间,也就是说线程可以访问相同的变量和相同的对象,而且他们从同一个堆中分配对象进行通讯,数据交换以及同步操作。

由于线程共享同一地址空间,所以不需要额外的通讯机制,这样就使得通讯变得简单并且高效。

所以说,进程是资源分配的最小单位,线程是执行的最小单位

如何创建一个线程?

1.继承Thread类,重写run()方法,new一个该类的对象,调用start启动这个线程。

class Threadtest extends Thread{
    @Override
    public void run() {
       for (int i = 0 ;i < 100000;i++){
       }

    }
}
 public static void main(String[] args) {
        Threadtest td = new Threadtest();
        td.start();
    }

2.实现Runnable接口,重写run()方法,直接new一个Thread类,把Runnable的实例当作参数传入。

class Threadtest2 implements Runnable{

    @Override
    public void run() {
        for (int i = 0 ;i < 100000;i++){
        }
        System.out.println("实现Runnable接口");
    }
}

 public static void main(String[] args) {
        Threadtest2 tdtest = new Threadtest2();
        Thread td = new Thread(tdtest);
        td.start();
    }

3.匿名内部类直接newThread(){ 在这里实现run()方法}并调用start()

public static void main(String[] args) {
 	new Thread(){
            @Override
            public void run() {
                System.out.println("匿名内部类实现 ");
            }
        }.start();
  }

4.实现callable接口(这是一个泛型接口),实现call()方法,call方法有对应的返回值,并且可以抛出异常,实例化后利用FutureTask进行存储,并且调用get()方法可以获得这个线程的返回值

class Threadtest3 implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for(int i =0;i < 10000;i++){
            sum++;
        }
        return sum;
    }
}
  public static void main(String[] args) {
          Threadtest3 callTest = new Threadtest3();
        FutureTask<Integer> futureTask = new FutureTask<>(callTest);
        Thread td = new Thread(futureTask);
        td.start();
        try {

            Integer result = futureTask.get();
            System.out.println(result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
  }

(tips:run()方法和start()方法不一样,run()方法相当于程序串行执行,也就是说run()之后的代码,必须等run()执行结束才可以执行。start()方法是程序启动了一个线程,此时进程中不仅有mian()一个线程,还有另一个线程,这两个线程,交替执行)

线程的常用方法:

.sleep(long x);
使此线程睡眠 x 个毫秒 。还有一个方法更加的方便就是 TimeUnit。
在这里插入图片描述
这里我们可以更方便的让当前线程睡眠多长时间,SECONDS是秒,MILLISECONDS是毫秒,最后我们只需要填写数值即可。TimeUnit的方法一般写在run()方法中,或者call()方法中。
注意此时线程只是睡眠,到了时间就会自己醒来接着运行,所以并不会将锁的资源让出。

.interrupt();
使当前线程中断。此时中断状态设置为true。

.interrupted();
这时一个静态方法,返回当前的中断位,并且将当前中断为重置位false;

.isinterrupted();
返回当前中断位。

.join();
join()方法可以理解为插队,举个例子,此时A线程正在运行,但是此时B非常紧急,B现在就要运行,B运行结束了A再接着运行。
那么此时我们就在线程A中调用线程B.join()。此时B就相当于插了个队,先获取到了资源运行。
(线程A自己调用A.join()方法没有任何用处。)

.yield();
让出一次时间片。(如果学过操作系统的话相信对进程调度:时间片轮转有过一定的了解,时间片就是当前一个时间单位,假如时间片为2微秒,现在有两个进程,这两个进程轮换使用2微秒,谁用完了另一个接着使用即可。线程同样,当前进程的时间片假如为2微秒,线程A使用完B使用,轮换交替。)

但是这个方法的请求,方法不一定会生效。当CPU很忙的情况下,可能CPU并不会处理这个方法。

守护线程

什么是守护线程?

守护线程我们可以理解为我们使用的一种服务,GC垃圾回收机制,就是JVM进程的守护线程。

我们先理解一下什么时候JVM会正常退出,当JVM中没有非守护线程在运行了,此时JVM正常退出,所有线程结束。
守护线程就是从它被调用,一直在提供服务的一种线程,它并不会对JVM的正常退出有影响。

以GC垃圾回收器为例,GC在从我们main()的主线程开始,就在扫描是否有垃圾,有垃圾就回收掉,一直到main()结束,此时JVM结束,所有线程结束。那要是GC不是守护线程时正常线程。首先我们是不知道一个mian()主线程是什么时候结束的,所以GC我就得写个死循环,让它一直扫描是否有垃圾吧。那我们想一下,如果此时main()结束了,GC结束么?不结束啊,因为是死循环啊。所以GC这种从头到尾一直提供服务的线程,我们将它置为守护线程。从调用开始运作,一直到主线程结束,自己自动结束。
怎么将一个线程设置为守护线程呢?

在这个线程启用之前,调用setDaemon(true);
此时这个线程就是守护线程了。

什么时候用守护线程?

守护线程经常用做执行一些后台任务,因此有时也被称之为后台线程,当你希望关闭某些线程的时候,或者退出JVM进程的时候,一些线程能够自动关闭,此时就可以考虑使用守护线程为你完成这样的工作。

线程的生命周期

我们通过Thread源码发现,Java的线程生命周期有6种状态。

现在我们就画一副图来理解一下线程生命周期的6种状态

1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
3. 阻塞(BLOCKED):表示线程阻塞于锁。
4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
6. 终止(TERMINATED):表示该线程已经执行完毕。

在这里插入图片描述

Java线程的6种状态及切换(透彻讲解)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值