多线程基础

本文详细解析了进程与线程的概念,介绍了创建线程的四种方法,包括实现Runnable接口、继承Thread类、实现Callable接口以及使用匿名内部类。同时,探讨了线程状态及其转换、线程调度算法,并解答了常见线程问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、什么叫做线程与进程

进程:进程是操作系统进行资源分配的最小单位。例如:IO资源、内存资源

线程:线程是操作系统进行资源调度的单位。

线程存在于进程中,一个进程可以对应多个线程;进程与线程之间相互隔离;一个进程的崩溃不会影响其他进程的崩溃,但是一个线程的崩溃可能引起其他线程的崩溃,因此,进程的健壮性要比线程好

内存资源占比:创建进程意味着开辟内存空间、同一个进程下的线程共享部分空间,开辟一个进程相比线程资源占用更多

二、创建线程的四种方式

(1)通过创建Runnable接口创建线程

实现步骤:

step1:实现Runnable接口,并且实现Runnable接口中的run方法

step2:实例化Runnable接口的类

step3:创建Thread对象,并且将Runnable实例作为参数来实例化Thread对象

step4:调用Thread对象的start方法,意味着子线程可以使用

实例演示:

public class ImplementRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("实现run接口");
    }
}
 

public class CreadThread {
    public static void main(String[] args) {
        ImplementRunnable runable=new ImplementRunnable();
        Thread thread = new Thread(runable);
        thread.start();

    }

}

(2)通过继承Thread类来创建线程

实现步骤:

step1:创建类并继承Thread类,重写run方法

step2:实例化该类的对象

step3:对象调用start方法,启用子线程

实例演示:

public class ExtThread extends Thread{
    @Override
    public void run() {
        super.run();
        System.out.println("继承Thread");
    }
}

 

public class CreadThread {
    public static void main(String[] args) {
        ExtThread extThread = new ExtThread();
        extThread.start();

      }

}

(3)通过实现Callable接口创建多线程

实现步骤:

step1:实现Callable接口,并且实现call方法

step2:调用Executors工具类实现线程池(说明Callable是Executors下的工具类)

step3:将实例化的Callable对象提交到线程池(即使用submit方法)

实例演示:

public class ImplementCallable implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        System.out.println("实现Callable接口");
        return 666;
    }
}

public class CreadThread {
    public static void main(String[] args) {
        ImplementCallable implementCallable = new ImplementCallable();
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Future<Integer> submit = executorService.submit(implementCallable);
        try {
            Integer integer = submit.get();
            System.out.println(integer);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

}

(4)通过匿名内部类创建

实例演示:

public class CreadThread {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名内部类创建线程");
            }
        }).start();
      }

}

Runable接口和Thread类的区别:

1)线程类继承自Thread则不能继承其他类;而Runable接口可以

2)线程类继承自Thread相对于Runable来说,使用线程的方法更方便一些

3)实现Runnable接口的线程类的多个线程、可以更方便的访问同一个变量,而Thread类需要内部类来进行替换

Callable 和 Runnable接口的区别

1)Callable中的方法是call(),而Runnable中的方法是run();

2)Callable的任务执行后可返回值,而Runnable的任务是没有返回值的;

3)call()方法可抛出异常,而run()方法不能抛出异常;

三、线程中常用的方法

1)start()

启动一个新线程。start需要首先调用,且start不能被重复调用;如果在start之前调用了某一个方法,只是普通方法的调用,和多线程没有关系。

2)run()

和普通的成员方法一样,可以被重复调用。单独调用run()的话,会在当前线程中执行run(),而并不会启动新线程。

3)yield()

定义在Thread.java中,是Thread的静态方法。作用是让步,它能让当前正在执行的线程由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执行权;但是,并不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权;也有可能是当前线程又进入到“运行状态”继续运行。

4)sleep()

定义在Thread.java中,是Thread的静态方法;sleep方法的作用是让线程休眠指定时间,在时间到达时自动恢复线程的执行;sleep方法不会释放线程锁;在线程重新被唤醒时,它会由“阻塞状态”变成“就绪状态”,从而等待cpu的调度执行。

5)join()

定义在Thread.java中。join() 的作用是让“主线程”等待“子线程”结束之后才能继续运行。

使用方法:在Father线程中调用:sonThread.join();则father线程会阻塞到son线程完成。join的原理是用wait做的,因此其行为类似wait。

6)interrupt()

它的作用是中断当前线程。

7)isDaemon()     setDaemon()

Java 中有两种线程:用户线程和守护线程。可以通过isDaemon()方法来区别它们:如果返回false,则说明该线程是“用户线程”;否则就是“守护线程”。

用户线程一般用户执行用户级任务,而守护线程也就是“后台线程”,一般用来执行后台任务。

需要注意的是:Java虚拟机在“用户线程”都结束后会后退出。

8)setPriority() getPriority()

线程优先级、来指导JVM层面优先来执行那个成程序,但最终的执行顺序需要操作系统来指定。

java 中的线程优先级的范围是1~10,最小值是1、默认的优先级是5、最大值是10。“高优先级线程”会优先于“低优先级线程”执行。

四、线程状态及状态转换

线程总共有5种状态,分别是:

1)新建状态(New)

用new语句创建的线程处于新建状态,此时它和其他Java对象一样,仅仅在堆区中被分配了内存。

2)就绪状态(Runnable)

当一个线程对象创建后,其他线程调用它的start()方法,该线程就进入就绪状态,Java虚拟机会为它创建方法调用栈和程序计数器。处于这个状态的线程位于可运行池中,等待获得CPU的使用权。

3)运行状态(Running)

这个状态的线程占用CPU,执行程序代码。只有处于就绪状态的线程才有机会转到运行状态。

4)阻塞状态(Blocked)

阻塞状态是指线程因为某些原因放弃CPU,暂时停止运行。当线程处于阻塞状态时,Java虚拟机不会给线程分配CPU。直到线程重新进入就绪状态,它才有机会转到运行状态。

阻塞状态可以分为以下3种:

1、位于对象等待池中的阻塞状态

当线程处于运行状态时,如果执行了某个对象的wait()方法,Java虚拟机就会把线程放到这个对象的等待池中,这涉及到“线程通信”的内容。

2、位于对象锁池中的阻塞状态

当线程处于运行状态时,试图获得某个对象的同步锁时,如果该对象的同步锁已经被其他线程占用,Java虚拟机就会把这个线程放到这个对象的锁池中,这涉及到“线程同步”的内容。

3、其他阻塞状态:

当前线程执行了sleep()方法,或者调用了其他线程的join()方法,或者发出了I/O请求时,就会进入这个状态。

5)死亡状态(Dead)

当线程退出run()方法时,就进入死亡状态,该线程结束生命周期。

五、线程调度与线程调度算法

线程调度

1)调整现场优先级:Java线程有优先级,优先级高的线程获得较多的运行机会(运行时间);

static int Max_priority 线程可以具有的最高优先级,值为10;

static int MIN_PRIORIYT 线程可以具有的最低优先级,值为1;

static int NORM_PRIORITY 分配给线程的默认优先级,值为5;

Thread类的setPriority()和getPriority()方法分别用来设置和获取线程的优先级;

2)线程睡眠:Thread.sleep(long millins)使线程转到阻塞状态;

3)线程等待:Object.wait()方法,释放线程锁,使线程进入等待状态,直到被其他线程唤醒(notify()唤醒的是其中一个线程和notifyAll()唤醒的是所以的线程);

4)线程让步:Thread.yeild()方法,暂停当前正在执行的线程,使其进入等待执行状态, 把执行机会让给相同优先级或更高优先级的线程,如果没有较高优先级或相同优先级的线程, 该线程会继续执行;

5)线程加入:join()方法,在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,知道另一个进程运行结束,当前线程再有阻塞状态转为就绪状态;

线程调度算法

1)实时系统:

FIFO(First Input First Output,先进先出算法)

SJF(Shortest Job First,最短作业优先算法)

SRTF(Shortest Remaining Time First,最短剩余时间优先算法)。

2)互式系统:

RR(Round Robin,时间片轮转算法)

HPF(Highest Priority First,最高优先级算法)。

六、实际解决问题

1、main方法运行属于进程还是线程?

main方法属于进程,会创建JVM实例,分配内存空间;也是一个线程,是一个主线程。

2、有三个线程T1,T2,T3,怎样确保他们按顺序执行?

有多个线程中有多种方法让线程按特定顺序执行,可以用线程类的join()方法,在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。为了确保三个线程的顺序应该先启动最后一个(即T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值