多线程【一】:进程和线程&&并发并行&&创建线程的方法

进程和线程

一、进程的定义

进程是程序的一次执行,进程是一个程序及其数据在处理机上顺序执行时所发生的活动,进程是具有独立功能的程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。
进程是系统进行资源分配和调度的独立单位。每一个进程都有它自己的内存空间和系统资源

二、程序并发执行

为使程序能并发执行,系统必须进行以下的一系列操作:
(1)创建进程,系统在创建一个进程时,必须为它分配其所必需的、除处理机以外的所有资源,如内存空间、I/O设备,以及建立相应的PCB;
(2)撤消进程,系统在撤消进程时,又必须先对其所占有的资源执行回收操作,然后再撤消PCB;
(3)进程切换,对进程进行上下文切换时,需要保留当前进程的CPU环境,设置新选中进程的CPU环境,因而须花费不少的处理机时间。

三、线程

引入线程主要是为了提高系统的执行效率,减少处理机的空转时间和调度切换的时间,以及便于系统管理。使OS具有更好的并发性。
线程的例子:
音乐软件:既播放着音乐,点击菜单栏仍会响应。

四、进程和线程总结

  1. 进程作为资源分配的基本单位

  2. 线程作为资源调度的基本单位,是程序的执行单元,执行路径(单线程:一条执行路径,多线程:多条执行路径)。是程序使用CPU的最基本单位。

  3. 线程有3个基本状态:执行、就绪、阻塞
    线程状态图:
    在这里插入图片描述

  4. 线程有5种基本操作:派生、阻塞、激活、调度、结束

  5. 线程的属性:
    (1)轻型实体;
    (2)独立调度和分派的基本单位;
    (3)可并发执行;
    (4)共享进程资源。

  6. 线程的两个基本类型:
    (1)用户级线程:管理过程全部由用户程序完成,操作系统内核心只对进程进行管理。
    (2)系统级线程(核心级线程):由操作系统内核进行管理。操作系统内核给应用程序提供相应的系统调用和应用程序接口API,以使用户程序可以创建、执行以及撤消线程。

多线程不是提高程序的执行速度,其实是为了提高应用程序的使用率,程序的执行其实都是在抢CPU的资源,CPU的执行权。多个进程是在抢这个资源,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到CPU的执行权

五、使用多线程提高效率需要考虑的因素

(1)所有线程执行时并发+并行
(2)线程创建、销毁时比较耗时
(3)线程的调度由系统决定(线程越多,系统调度越频繁;线程就绪态转变为运行态,也是有性能及时间消耗)
(4)单个线程运行的任务量

并发和并行

一、并发

并发性是指同一时间间隔内发生两个或多个事件。
并发是在同一实体上的多个事件。
并发是针对线程的。

二、并行

并行性是指同一时刻内发生两个或多个事件。
并行是在不同实体上的多个事件。
并行是针对进程的。

创建线程的方法

一、继承 Thread 类

可以通过继承 Thread 来创建一个线程类,该方法的好处是 this 代表的就是当前线程,不需要通过Thread.currentThread() 来获取当前线程的引用。
Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。
1.Thread的常见构造方法:

方法说明
Thread()创建线程对象
Thread(Runnable target)使用Runnable对象创建线程对象
Thread(String name)创建线程对象并命名
Thread(Runnable target,String name)使用Runnable对象创建线程对象,并命名
Thread(ThreadGroup group,Runnable target)线程可以被用来分组管理,分好的组即是线程组

代码示例:

Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");

2.Thread 的几个常见属性:

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority
是否后台线程isDaemon
是否存活isAlive()
是否被中断isInterrupted()

ID 是线程的唯一标识,不同线程不会重复
名称是各种调试工具用到
状态表示线程当前所处的一个情况
3.从下面的源代码中可以发现,Thread类实现了Runnable接口,它们之间具有多态关系。

public class Thread implements Runnable 

4.调用的随机性:
代码的运行结果与代码执行顺序或调用顺序是无关的。

public class MyThread extends Thread {
    @Override
    public void run(){
        System.out.println("MyThread");
    }
}
class Test{
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        System.out.println("运行结束!");
    }
}
/*
打印结果
运行结束!
MyThread
*/

线程是一一个子任务,CPU以不确定的方式,或者说是以随机的时间来调用线程中的run
方法,所以就会出现先打印“运行结束!”后输出“MyThread" 这样的结果了。
5.start();启动线程:
start()方法首先启动了线程,然后再由JVM去调用该线程的run方法。
run方法直接调用,不会启动线程,只是在当前main线程中调用了run方法
在这里插入图片描述
6.创建线程:
(1)定义一个类,其继承Thread类。
(2)重写Thread类的run()方法,并将该线程要执行的操作声明在该方法中。
(3)创建该Thread子类对象的实例。
(4)调用start()方法,启动该线程。

class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());//打印该线程的名称
    }
}
public class TestDemo1 {
    public static void main(String[] args) {
        MyThread my1 = new MyThread();//创建对象
        my1.start();//启动该线程
        MyThread my2 = new MyThread();
        my2.start();//启动该线程
    }
}
/*
打印结果:
Thread-0
Thread-1
*/

二、实现 Runnable 接口

通过实现 Runnable 接口,并且调用 Thread 的构造方法时将 Runnable 对象作为 target 参数传入来创建线程对象。
该方法的好处是可以规避类的单继承的限制;但需要通过 Thread.currentThread() 来获取当前线程的引用。
(1)定义一个类,实现Runnable接口;
(2)重写其run()方法,跟Thread类中的run()方法一样,将线程要执行的操作声明在该run()方法中;
(3)创建该Runnable实现类的实例;
(4)将该实例作为Thread的target来创建Thread对象,这个Thread对象即真正的线程对象;
(5)调用start()方法,启动该线程。

class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

public class TestDemo1 {
    public static void main(String[] args) {
        MyThread my = new MyThread();//创建Runnble()实现类的实例
        Thread t1 = new Thread(my);//将该实例传到Thread类的构造器中创建Thread对象
        t1.start();
        Thread t2 = new Thread(my);
        t2.start();
    }
}

三、Callable的使用

import java.util.concurrent.*;

/**
 * Callable创建线程:
 * Future/FutureTask
 */
public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Integer> c = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return 123;
            }
        };
        FutureTask<Integer> task = new FutureTask<>(c);
        //Thread使用Callable
        new Thread(task).start();
        System.out.println("main");
        Integer r = task.get();//直到线程执行完毕(join效果差不多),但可以获取线程的返回值
        System.out.println(r);
        //线程池中使用Callable
        ExecutorService pool = Executors.newFixedThreadPool(4);
        Future<Integer> future = pool.submit(c);
        System.out.println("main");
        Integer r2 = future.get();//阻塞等待
        System.out.println(r2);
    }
}

四、run()和start()方法区别

start()方法首先启动了线程,然后再由JVM去调用该线程的run()方法。
run()方法直接调用,不会启动线程,只是在当前main线程中调用了run()方法。

两种创建多线程的方法,我们一般使用实现 Runnable 接口。因为它可以避免java中的单继承的限制。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值