Java 多线程一:线程概念和创建

本文深入探讨Java多线程,涵盖线程概念、进程区别,单核与多核CPU理解,以及并行并发的概念。详细讲解线程的创建,包括继承Thread类、实现Runnable接口,JDK1.5后的Callable接口和线程池的使用。通过实例解析线程的优缺点及何时应用多线程。
摘要由CSDN通过智能技术生成

1. 程序、进程、线程基本概念

1.1 程序与进程

    (1)程序 (Program) 某种语言编写完成特定任务的一组指令的集合。即指一段静态的代码,静态对象。

    (2)进程 (Process)程序的一次执行过程,或是正在运行的一个程序。进程是一个动态的过程:有产生、存在和消亡的过程(生命周期)

    (3)进程作为资源分配的基本单位,系统在运行时会为每个进程分配执行资源(CPU,内存,硬盘,网络,显卡,电源)。

    (4)程序是静态的代码,进程是动态的执行程序。

────────────────────────────────────────────────────────────

1.2 线程 ( Thread )

    (1)进程可进一步细化为线程,指一个程序内部的一条执行路径。

    (2)若一个进程在同一时间并行执行多个线程,就是支持多线程的。

    (3)线程作为调度和执行的单位,进程在运行时会为每个线程分配执行资源。每个线程拥有独立的运行栈和程序计数器( pc ),线程切换的开销更小

    (4)一个进程中的多个线程共享相同的内存单元/内存地址空间,它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患(多线程安全问题)

    (5)例如:一个 Java 应用程序 java.exe,至少有三个线程:main( ) 主线程,gc( ) 垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。

────────────────────────────────────────────────────────────

1.3 单核CPU和多核CPU的理解

    (1)单核CPU,一种假的多线程,因为在一个时间单元内,只能执行一个线程的任务。然而操作系统在很短的时间内在各个程序 ( 进程 ) 之间进行切换,看起来像同时执行多个任务。

    (2)多核 CPU,真的多线程,多个 CPU 在相同时间执行多个线程,多核 CPU 才能更好的发挥多线程的效率。

────────────────────────────────────────────────────────────

1.4 并行与并发

    (1)并行:多个 CPU 同时执行多个任务。比如:多个人同时做不同的事。

    (2)并发:一个 CPU ( 采用时间片 ) 同时执行多个任务。比如:秒杀、多人同做一件事。

并发惯用的解决方式: 扩容服务器;服务器资源排队

────────────────────────────────────────────────────────────

1.5 多线程的优缺点

优点

(1)提高应用程序的响应。对图形化界面更有意义,可增强用户体验。

(2)提高计算机系统 CPU 的利用率

(3)改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改

缺点

(1)降低其他线程的执行概率

(2)用户会感受到软件的卡顿问题

(3)增加系统压力,资源压力

(4)多线程情况下有可能会导致共享资源问题,线程冲突,线程安全,线程死锁。

────────────────────────────────────────────────────────────

1.6 何时需要多线程

    (1)程序需要同时执行两个或多个任务。

    (2)程序需要实现一些需要等待的任务时,如用户输入、文件读写 操作、网络操作、搜索等。

    (3)需要一些后台运行的程序时。

────────────────────────────────────────────────────────────

2. 线程类 Thread

2.1 Thread 类的特性

    (1)Java 的 JVM 允许程序运行多个线程,通过 java.lang.Thread 类来体现。

    (2)Thread 类是 Java 中线程的基类,有一部分线操作相关方法。

    (3)每个线程都是通过某个特定 Thread 对象或其子类对象的 run( ) 方法来完成操作的,run 方法内容是线程目标任务代码,称为线程体。

    (4)Thread 对象通过 start( ) 方法来启动本线程,而非直接调用 run( ),否则不是多线程。

    5注意:不能重复启动已经 start( ) 的线程,如需多次调用,创建多个线程对象分别调用,如果重复调用则将抛出 IllegalThreadStateException (非法线程状态)异常

────────────────────────────────────────────────────────────

2.2 Thread 类的构造器

Thread( )

创建新的 Thread 对象

Thread( String threadname )

创建线程并指定线程实例名

Thread( Runnable target )

指定创建线程的目标对象,它实现了 Runnable 接中的 run 方法

Thread( Runnable target, String name )

指定创建线程的目标对象并指定线程实例名

────────────────────────────────────────────────────────────

2.3 Thread 类的常用方法

static Thread currentThread( )

返回当前线程。在 Thread 子类中就是 this,通常用于主线程和 Runnable 实现类。

void start( )

启动线程,并执行对象的 run( ) 方法

void run( )

线程在被调度时执行的操作

String getName()

返回线程的名称

void setName( String name )

设置该线程名称

static void yield( )

线程让步:暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程 ,若队列中没有同优先级的线程,忽略此方法

join( )

当某个程序执行流中调用其他线程的 join() 方法时,调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止,低优先级的线程也可以获得执行

static void sleep( long millis )

(指定时间:毫秒),令当前活动线程在指定时间段内放弃对 CPU 控制,使其他线程有机会被执行,millis 毫秒后重排队。可能会有 InterruptedException 异常

stop( )

强制线程生命期结束,不推荐使用

boolean isAlive( )

返回 boolean,判断线程是否还活着

boolean isDaemon()

判断当前线程是否为守护线程/后台线程。

void setDaemon(boolean on)

设置当前线程是否为守护线程,on 为 true 当前线程为守护线程,

────────────────────────────────────────────────────────────

3. 线程的创建(JDK1.5前)

3.1 方式一:继承 Thread 类(不推荐)

    1)创建一个继承于 Thread 类的子类。

    2)重写 Thread 类的 run( ) 方法。

    3)创建 Thread 类的子类的对象。

    4)通过此对象调用 start( ) 方法: 启动当前线程,调用当前对象的 run( ) 方法。

     5代码实现

public class CreateThreadTest01 {
    public static void main(String[] args) {
// 3. 创建 Thread 类的子类的对象
        MyThread my = new MyThread();
// 4. 通过此对象调用 start( )  方法
// my.run();   不能通过对象.run( ) 调用 run( ) 方法,否则不是多线程
// my.start(); 不能重复调用已经 start( ) 的线程,否则报 IllegalThreadStateException
        my.start();
        for (int i=1; i<101; i++) {
            if (!(i % 2 == 0)) {
                System.out.println(Thread.currentThread().getName()+ ":" + i);
            }
        }
 // 重新创建一个线程对象 , 调用 start( ) 方法
        MyThread my1 = new MyThread();
        my1.start();
    }
}

// 1.创建一个继承于 Thread 类的子类
class MyThread extends Thread{
    // 2.重写 Thread 类的 run 方法,输出 1-100 之间的偶数
    @Override
    public void run() {
        for (int i=1; i<101; i++){
            if (i%2 == 0){
                      System.out.println(Thread.currentThread().getName()+ ":" + i);
            }
        }
    }
}

────────────────────────────────────────────────────────────

3.2 方式二:实现 Runnable 接口

    1)创建一个实现 Runnable 接口的类

    2)实现 Runnable 接口中的 run( ) 方法

    3)创建实现类的对象

    4)将此对象作为参数传递到 Thread 类的构造器中,创建 Thread 类的对象

    5)通过 Thread 类对象调用 start( ) 方法  ①启动当前线程调用当前对象的 run( ) 方法

    6代码实现

public class CreateThreadTest02 {
    public static void main(String[] args) {
        // 3. 创建实现类的对象
        MThread mt01 = new MThread();
        // 4. 将此对象作为参数传递到Thread类的构造器中,创建 Thread 类的对象调用 start
        Thread t1 = new Thread(mt01);
        // 5. 通过创建 Thread 类的对象调用 start 方法:启动线程、调用当前线程的run方法
        t1.start();
    }
}

// 1.创建一个实现 Runnable 接口的类
class MThread implements Runnable{
    // 2.实现 Runnable 接口中的 run 方法
    @Override
    public void run() {
        System.out.println("malong");
    }
}

────────────────────────────────────────────────────────────

3.3 创建线程的两种方式比较(JDK1.5前)

    1优先选择:实现 Runnable 接口的方式

    2)原因:实现 Runnable 接口的方式没有类的单继承性的局限性;通过 Thread( Runnable target ) 可以创建多个线程,多个线程共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。

    3)联系:public class Thread implements Runnable,Thread 类本身也继承了 Runnable 接口。

    4)相同点:都需重写 run 方法

────────────────────────────────────────────────────────────

3.4 创建 Thread 类的匿名子类的方式

new Thread(){
    @Override
    public void run(){
      //实现代码 ...
    }
}.start();

────────────────────────────────────────────────────────────

4 线程的创建(JDK1.5后)

4.1. 新增方式一:实现 Callable 接口

4.1.1 Callable 接口

    (1)Callable 功能与 Runnable 相比更加丰富和强大。

    (2)相比 run() 方法,可以有返回值。

    (3)方法可以抛出异常,被外面的操作捕获,获取异常的信息。

    (4)支持泛型的返回值。

    (5)需要借助 FutureTask 类,比如获取返回结果。

4.1.2 Future 接口

    (1)对具体 Runnable、Callable 任务的执行结果进行取消、查询是否完成、获取结果等。

    (2)FutrueTask 是 Futrue 接口的唯一的实现类。

    (3)FutureTask 同时实现了 Runnable,Future 接口。它既可以作为 Runnable 被线程执行,又可以作为 Future 得到 Callable 的返回值 。

4.1.3 实现方式

//创建一个实现 Callable 接口的类

class NumThread implements Callable{

//重写 Callable 接口的 call 方法

    @Override

    public Object call() throws Exception {

    }

}

public class ThreadNew01 {

    public static void main(String[] args) {

     //实例化实现类

        NumThread numThread = new NumThread();

             //将此 Callable 接口实现类的对象作为传递到 FutureTask 构造器中, 创建 FutureTask 的对象

        FutureTask futureTask = new FutureTask(numThread);

             //将 FutureTask 的对象作为参数传递到 Thread 类的构造器中, 创建 Thread 对象

        Thread thread = new Thread(futureTask);

    //调用 start 方法

        thread.start();

        try {

            // get() 返回值即为 FutureTask 构造器参数 CaLlable 实现类重写的 call() 的返回值。

            System.out.println(futureTask.get());

        } catch (InterruptedException e) {

            e.printStackTrace();

        } catch (ExecutionException e) {

            e.printStackTrace();

        }

    }

}

────────────────────────────────────────────────────────────

4.2 新增方式二:使用线程池

4.2.1 线程池

    (1)背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程, 对性能影响很大。

    (2)思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。

    (3)好处:

提高响应速度(减少了创建新线程的时间)

降低资源消耗(重复利用线程池中线程,不需要每次都创建)

便于线程管理

    (4)线程池的相关参数:

corePoolSize:核心池的大小

maximumPoolSize:最大线程数

keepAliveTime:线程没有任务时最多保持多长时间后会终止

4.2.2 实现方式

public class ThreadNew02{

    public static void main(String[] args) {

        //1.提供指定线程数量的线程池

        ExecutorService service = Executors.newFixedThreadPool(10);

        ThreadPoolExecutor s1 = (ThreadPoolExecutor)service;

        //设置线程池的属性

        System.out.println(service.getClass());

        s1.setCorePoolSize(15);

        //2.执行指定的线程的操作,需要提供实现 Runnable 接口或 Callable 接口的实现类对象

        service.execute(new ThreadTool01());//适合使用于 Runnable

        service.execute(new ThreadTool02());

        //service.submit(Callable callable);//适合使用于 Callable 和 Runnable 接口

        //3.关闭连接池

        service.shutdown();//关闭连接池

    }

}

4.2.3 线程池相关API

    (1)JDK 5.0 提供了线程池相关 API:ExecutorService 和 Executors

ExecutorService

真正的线程池接口。

常见子类ThreadPoolExecutor

void execute( Runnable command )

执行任务/命令,没有返回值,一般用来执行 Runnable

<T>Future<T> submit( Callable<T> task )

执行任务,有返回值,一般又来执行 Callable

void shutdown()

关闭连接池

Executors

工具类、线程池的工厂类,用于创建并返回不同类型的线程池

Executors.newCachedThreadPool()

创建一个可根据需要创建新线程的线程池

Executors.newFixedThreadPool(int n)

创建一个可重用固定线程数的线程池

Executors.newSingleThreadExecutor()

创建一个只有一个线程的线程池

Executors.newScheduledThreadPool(n)

创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值