【Java】多线程(相关的概念、两种实现多线程的方式、线程生命周期)

多线程

1、 多线程相关的概念

1、程序(Program)

​ 为了实现一个功能,完成一个任务而选择一种编程语言编写的一组指令的集合。

软件 = 程序 + 程序运行所需要的一些资源文件。 一个软件中可能会有很多个程序构成。

2、进程(Process)

​ 程序的一次运行。操作系统会给这个进程分配资源(例如:内存)。

进程是操作系统分配资源的最小单位,进程与进程之间的内存是独立,无法直接共享。最早的DOS操作系统是单任务的,同一时间只能运行一个进程。后来现在的操作系统都是支持多任务的,可以同时运行多个进程。两个进程之间进行来回切换,通信(交换数据)等操作时,成本比较高。

3、线程(Thread)

​ 线程是进程中的其中一条执行路径。一个进程中至少有一个线程,也可以有多个线程。有的时候也把线程称为轻量级的进程。同一个进程的多个线程之间有些内存是可以共享的(方法区、堆),也有些内存是独立的(栈(包括虚拟机栈和本地方法栈)、程序计数器)。 因为线程之间可能使用共享内存,那么在数据交换成本上就比较低。而且线程之间的切换相对进程对于CPU和操作系统来说,成本比较低。

一个进程中至少有一个线程。

4、并行: 多个处理器同时可以执行多条执行路径。

要求同时进行。针对CPU多核,甚至多个CPU,同时运行多个线程任务。

有同时处理多个任务的能力,并行。

5、并发:多个任务同时执行,但是可能存在先后关系。

有处理多个任务的能力,不一定要同时。

如果某个系统支持两个或者多个动作(Action)同时存在,那么这个系统就是一个并发系统。如果某个系统支持两个或者多个动作同时执行,那么这个系统就是一个并行系统。并发系统与并行系统这两个定义之间的关键差异在于**“存在”**这个词。

在并发程序中可以同时拥有两个或者多个线程。这意味着,如果程序在单核处理器上运行,那么这两个线程将交替地换入或者换出内存。这些线程是同时“存在”的——每个线程都处于执行过程中的某个状态。如果程序能够并行执行,那么就一定是运行在多核处理器上。此时,程序中的每个线程都将分配到一个独立的处理器核上,因此可以同时运行。《并发的艺术》 — 〔美〕布雷谢斯

看能不能同时被(多个)cpu执行,如果可以就说明是并行,而并发是多个线程被(一个)cpu 轮流切换着执行。

并发(Concurrency)是同时处理很多事情(dealing with lots of things at once),并行(Parallelism)是同时执行很多事情(doing lots of things at once);并行是并发的子集。

CPU:一个CPU同一个时间只能够运行一个线程的任务。

如何实现多个线程同时运行的呢?

是因为CPU是非常快,这个速度远远高于内存、硬盘、人的大脑反应的速度。

那么CPU会在多个线程之间,快速的切换,人是感觉不到。

交替执行 多核同时执行

2、两种实现多线程的方式

Java中如何去实现多线程?

(1)Java的程序入口是main,其实也是main线程,主线程。

线程是进程的其中一条执行路径,即一个进程至少有一个线程。那么main线程就是Java程序进程的第一个线程了。

(2)如何开启main线程以外的其他线程呢?

这里写2种,还有其他方式的。

1、继承Thread类

步骤:

(1)编写线程类,去继承Thread类(java.lang.Thread)

(2)重写public void run(){}

在run()中需要编写这个线程需要完成的任务。

(3)创建线程对象

(4)启动线程:start(),是调用start()方法。

class MyThread extends Thread {
     @Override
    public void run(){
        //...
    }
}
class Test{
    public static void main(String[] args){
        MyThread my = new MyThread();
        my.start();//有名字的线程对象启动
        
        new MyThread().start();//匿名线程对象启动
        
        //匿名内部类的匿名对象启动
        new Thread(){
            public void run(){
                //...
            }
        }.start();
        
        //匿名内部类,但是通过父类的变量多态引用,启动线程
        Thread t =  new Thread(){
            public void run(){
                //...
            }
        };
        t.start();
    }
}
public class TestThread2 {
    public static void main(String[] args) {
        System.out.println("hello thread");

        MyThread my = new MyThread();
        //顺序执行 如果my.run()没my.start() 就是顺序执行了
//		my.run();//这么调用,就不是开启多线程,而是普通对象调用方法
        my.start();//从父类Thread中继承的

        //打印main方法的
        for (int i = 1; i <=100; i++) {
            System.out.println("main:" + i);
        }
    }

}
class MyThread extends Thread{

    @Override
    public void run() {
        //例如:这个线程要完成的任务是,打印1-100之间的数字
        for (int i = 1; i <= 100; i++) {
            System.out.println("自定义线程:" + i);
        }
    }

}

可能 你执行一下 我执行一下

2、实现Runnable接口

步骤:

(1)编写线程类,实现Runnable接口

(2)重写public void run(){}

(3)创建线程对象

(4)借助Thread类的对象启动线程

class MyRunnable implements Runnable{
    public void run(){
        //...
    }
}
class Test {
    public static void main(String[] args){
        MyRunnable my = new MyRunnable();
        Thread t1 = new Thread(my);
        Thread t2 = new Thread(my);
        t1.start();
        t2.start();
        
        //两个匿名对象
        new Thread(new MyRunnable()).start();
        
        //匿名内部类的匿名对象作为实参直接传给Thread的构造器
        new Thread(new Runnable(){
            public void run(){
                //...
            }
        }).start();
            
    }
}
public class TestRunnable {
    public static void main(String[] args) {
        MyRunnable my = new MyRunnable();
//		my.start();
        //借人家的
        Thread t = new Thread(my);
        t.start();

        for (int i = 1; i <=100; i++) {
            System.out.println("main:" + i);
        }
    }
}

class MyRunnable implements Runnable {
    // abstract Runnable
    // public abstract void run();
    @Override
    public void run() {
        // 例如:这个线程要完成的任务是,打印1-100之间的数字
        for (int i = 1; i <= 100; i++) {
            System.out.println("自定义线程:" + i);
        }
    }

}

3、 线程的生命周期

线程的生命周期:

(1)新建/出生

new好了一个线程对象,此时它和普通的Java对象并没有区别。

新建线程 new一个线程对象

(2)就绪

就绪状态的线程是具备被CPU调用的能力和状态,也只有这个状态的线程才能被CPU调用。即线程调用了start()。

就绪状态的线程是具备被CPU调用的能力和状态

(3)运行

运行状态就是当前线程正在被CPU调度执行。

运行 当前线程被CPU调度执行中

运行->就绪 ①时间到②yield()

(4)阻塞

从运行状态到阻塞状态有几种情况:

①sleep()

②wait()

③join()

④没锁

⑤suspend()已过时

从阻塞回到就绪状态

①sleep()时间到,sleep()被打断interrupt()

②notify()

③加塞的线程结束

④占用锁的线程释放锁

⑤resume()已过时

(5)死亡

从运行到死亡:①run()正常结束②run()遇到异常但是没处理③其他线程把你stop()(已过时)

在这里插入图片描述

4、参考资料

记录 - 搞定Java核心技术

从Hello到goodbye

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值