Java多线程解释

本文详细介绍了进程和线程的概念,进程是应用程序的实例,可包含多个线程。线程是进程中的执行单元,共享堆内存和方法区,但拥有独立的栈内存。CPU核心数影响线程并发,例如I7-11代处理器支持超线程技术,一个核心可以支持两个逻辑线程。Java线程有六种状态,包括NEW、RUNNABLE、BLOCKED、WAITING、TIME_WAITING和TERMINATED。多线程实现方式包括继承Thread类、实现Runnable接口和Callable接口。
摘要由CSDN通过智能技术生成

什么是进程?什么是线程?

进程:某个应用程序(指的是打开的某个软件)
线程:某个进程中的执行场景、执行单元

进程和线程的关系?

**进程和进程之间:**两个进程之间没有关系,他们是相互独立的
线程和线程之间:在一个进程的两个线程中,堆内存和方法区共享,但一个线程拥有一个栈内存,每个栈之间互不干扰,各执行各的
**进程和线程之间:**进程是被操作系统创建的,线程是被进程创建的;一个操作系统可以创建很多进程,一个进程可以创建很多线程;不同线程之间堆内存和方法区是共享,但是栈内存是独立的,这也是多线程之间能够并发运行的核心之处。

进程:一个应用程序,可以创建多个线程;不同进程之间内存相互独立,数据不共享;
线程:被进程创建;在相同进程的不同线程中;
总而言之,进程是包含线程的

CPU核心数和线程间的关系

我的电脑是I7-11代处理器,支持8核心16线程,支持最大16个线程真并发。

之前的处理器都是4核心-4线程,为什么现在是8核心不是8线程了呢?
答:Inter为了加快CPU的处理速度,在频率上已经不能再继续努力了,目前是4.4MHZ, 所以就发明了超线程技术,让一个核心有2个逻辑线程,让操作系统误以为有16个线程,此时操作系统就最大支持16线程并发运行(真并发),从我们的任务管理器中可以看到。
任务管理器,操作系统识别到有8个核心,16个逻辑线程,既16个逻辑处理器
CPU支持超线程技术,那么线程数=核心数*2;
不支持超线程,线程数=核心数

通常一个核心能够支持运行1个线程,那8核心就能同时并发支持8个线程运行。

那么如果是老式的CPU,比如某云服务器的1核心云服务器,能否支持多线程呢?
答案:表面上支持,但实际上不支持。他可以以很快的速度切换每个线程,让你感受到仿佛是在多线程执行,但实际上他是以很乖的速度频繁的切换不同的线程,让你有种错觉感。

Java线程对象的生命周期(线程从出生到死亡的六种状态)

Java的Thread类有一个匿名内部枚举类,java.lang.Thread.State,里面定义了6种状态:
NEW: 尚未启动的线程处于此状态;Thread thread = new Thread()

新建状态下,线程还没有调用 start()方法,只是存在一个线程对象而已,Thread t = new Thread()

RUNNABLE: 在Java虚拟机中执行的线程处于此状态 thread.start()

已经调用了start()方法,此时又分为就绪态和运行态,就绪态指的是等待jvm的调度,没有获得cpu时间片;运行态是获得了cpu的时间片,正在运行的线程。

BLOCKED: 被阻塞等待监视器锁定的线程处于此状态

指的是因为某些原因,正在执行的线程放弃CPU,暂时停止运行,变成阻塞态。此时jvm不会给阻塞态分配CPU, 只有当其变成就绪态,才有被分配CPU的可能性。

WAITING: 正在等待另一个线程执行特定的动作的线程处于此状态

一个处于运行态的线程执行了wait()方法,就进入了等待态,这个wait()方法是无参方法
而处于等待的线程,只能被其他线程唤醒,一般是通过别的线程执行notify()/notifyAll()方法来唤醒。

TIME_WAITING: 正在等待另一个线程执行动作达到指定的等待时间的线程处于此状态

一个处于运行态的线程执行了wait(long time)或者sleep(long time)方法,这个线程就会在等待time时间后,重新进入就绪态

TERMINATED: 已退出的线程处于此状态

当一个线程执行完毕,正常结束:run()完毕;强制终止:stop()或destory();异常终止:执行过程发送异常终止。

wait()和wait(long time)区别: 线程执行wait(),进入等待态,将无限期等待,除非别的线程唤醒它,唤醒之后进入就绪态;线程执行wait(long time)进入有限等待态(Time_watiing),当等待的时间到了之后,自动进入就绪态,不需要其它线程唤醒。
图是偷的,别介意

线程的方法详解

1. sleep(long millis)

线程休眠,让直行的线程暂停指定的时间,进入计时等待状态,当时间到了之后,自动唤醒,进入就绪态
处于计时等待状态下的线程不会释放同步锁,常用于模拟网络延迟

2. wait()

线程等待,当一个线程执行了wait()方法,这个线程将进入无限期等待状态(将会释放同步锁),直到别的线程唤醒它为止
注意:这个方法一定要写在同步代码块或者同步方法中
注意:sleep()和wait()的区别:sleep()释放CPU,不释放同步锁。wait()释放CPU,也释放同步锁。

3. notify()/notifyAll()

唤醒方法:notify()随机唤醒一个线程,notifyAll()唤醒全部线程,我们常使用notifyAll()方法
注意:此方法需要和wait()方法成对使用,必须在同步代码块或同步方法中

4. join()

联合线程,表示这个线程等待另一个线程完成(死亡)后才执行,哪个线程调用join()方法,那个线程就被阻塞状态。
这种也被称为联合线程,也就是说把当前线程和当前线程所在的线程联合成一个线程。使用join()方法,可以实现线程之间的同步,确保某些线程在其他线程执行完成后再继续执行。

5. yield()

礼让线程,表示当前线程对象提示jvm, 我愿意主动让出cpu,当线程调用yield()方法后,该线程进入就绪状态。
但不外乎,当这个线程调用了礼让之后进入就绪态,jvm又给他分配cpu,又进入了运行态。
注意:sleep()和yield的区别:
a. 他们都释放了CPU,把运行的机会让给了其他线程
b. sleep方法会给其他线程运行的机会,但不考虑其他线程的优先级;yield()把运行机会给优先级更高的线程运行。
c. 调用sleep()之后进入计时等待状态,随后进入就绪态;调用yield()之后,进入就绪态。

实现多线程的三种方式

1. 继承Thread类:

万物皆对象,线程也是一个对象,线程的第一种实现 方式就是继承Thread类,继承Thread类是最简单的一种方式,继承了Thread类之后,重写Thread类的Run方法即可,当线程启动时,就会执行run方法内的内容。

注意:我们一般不常用这种方式,因为这种方式就不能让类再继承别的类了,通常使用第二种方式,因为实现了一个接口还可以继承别的类,而Thread类内部其实也是实现了Runable接口实现的

/**
 * 继承Thread类,实现run方法
 */
public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println(getName()+ "Hello world");
        }
    }
}
/**
 * 执行线程
 */
public class ThreadDemo {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        t1.setName("线程1");
        t2.setName("线程2");
        t1.start();
        t2.start();
    }
}

执行结果:
线程2Hello world
线程1Hello world
线程2Hello world
线程1Hello world
线程2Hello world
线程1Hello world

2. 实现Runable接口:

public class Thread1 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("我是线程" + Thread.currentThread().getName() + ", 当前打印了: " + i);
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        Thread1 thread = new Thread1();
        Thread thread1 = new Thread(thread, "线程1");//新建态
        thread1.start();//就绪态和运行态
    }
}

执行结果:
我是线程线程1, 当前打印了: 0
我是线程线程1, 当前打印了: 1
我是线程线程1, 当前打印了: 2
我是线程线程1, 当前打印了: 3
我是线程线程1, 当前打印了: 4

3.实现Callable接口

我们发现不管是继承thread类还是实现Runable接口,都面临两个问题:第一个是无法抛出更多的异常,第二个是线程执行完毕之后无法获得线程的返回值。接下来这种方式就可以完成这样的需求。
实现步骤如下:
a. 创建一个类实现Callable接口,实现call方法,这个接口类似于Runnable接口,但比Runnable接口更加强大,增加了异常和返回值。
b. 创建了一个Future Task, 指定Callable对象,作为线程任务
c. 创建线程,指定线程任务
d. 启动线程

public class Demo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建线程任务
        Callable<Integer> call = () ->{
            System.out.println("线程得到Cpu了,线程正在运行");
            int sum = 0;
            for (int i = 0; i < 5; i++) {
                sum += i;
            }
            return sum;
        };
        //将任务封装为FutureTask
        FutureTask<Integer> task = new FutureTask<>(call);
        //启动线程
        new Thread(task){
            @Override
            public synchronized void start() {
                super.start();
                System.out.println("线程新建了,但还没有得到Cpu");
            }
        }.start();
        //=============
        //当线程返回结果之前,可以为所欲为
        System.out.println("为所欲为。。。");
        //=============
        //得到线程的执行结果
        Integer integer = task.get();
        System.out.println("线程的执行结果:" + integer);
    }
}

执行结果:
线程新建了,但还没有得到Cpu
为所欲为。。。
线程得到Cpu了,线程正在运行
线程的执行结果:10

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值