Java线程

进程与线程

进程
在操作系统中,每个独立的执行的程序都是一个进程,计算机上都是安装的多任务操作系统,能同时执行多个应用程序。

在多任务的操作系统中,逻辑上是同时运行的,实际上并不是,所有应用程序都是由CPU执行,对于CPU来说,一个时间点只能运行一个程序。操作系统会为每个进程分配一段有限的CPU占有时间,占有时间都在同一条时间线上。

线程
每个运行的程序都是一个进程,在一个程序中还可以有多个可执行的单元,每个单元称为线程。每个进程中至少存在一个线程。

当启动Java程序时,就会产生一个进程,并创建一个线程,执行主函数的代码。代码会按调用顺序从上往下依次执行,称为单线程程序,如果希望程序多端代码交替运行,则需要创建多个线程。

线程和进程一样都是由CPU执行的,在同一条时间线上,逻辑上是同时执行的。

线程创建

Java中提供了两种多线程的实现方式,一种是继承java.lang包下的Thread类,重写run()方法,在run方法类实现线程代码;另一种方法是实现java.lang.Runable接口,重写run()方法。

继承Thread

//继承Thread
import java.lang.Thread;
public class App {
    public static void main(String[] args) {
        MyThread myThread=new MyThread();
        myThread.run();
        for (int i=0;i<10;i++){
            System.out.println("main is running");
        }
    }

    static class MyThread extends Thread{

        @Override
        public void run() {
            for(int i=0;i<10;i++){
                System.out.println("MyThread is running");
            }

        }
    }
}

在这里插入图片描述

上面代码演示了一个单线的案例,run是继承Thread创建的一个线程,该线程结束后主程序才结束。

多线程需要持续运行的程序才能体现出来,系统分配CPU占用时间。

//多线程演示用start()方法,run会抢占线程
import java.lang.Thread;
public class App {
    public static void main(String[] args) {
        MyThread myThread=new MyThread();
        myThread.start();
        while (true){
            System.out.println("main is running");
        }
    }

    static class MyThread extends Thread{

        @Override
        public void run() {
            while (true){
                System.out.println("MyThread is running");
            }

        }

    }
}

在这里插入图片描述

如图看出两个线程是交替运行,由系统膏分配CPU占用时间。注意:用start()开启线程,不能用run()会抢占线程。

多线程与单线程的区别在于动态占用CPU时间。单线程独占,多线程系统分配。

实现Runnable接口

Thread创建线程的弊端在于必须继承Thread,而已经存在继承关系的类就不能在继承了(Java单继承原理)。Runnable接口的优势在于其只实现了Thread的run方法,只需重写逻辑,并将Runnable作为参数传递给Thread,这样就不用继承Thread。

//将Runnable作为参数传到Thread
public class App {
    public static void main(String[] args) {
        Thread thread=new Thread(new MyRunnable());
        thread.start();

        Thread thread1=new Thread(new MyRunnableOne());
        thread1.start();
    }

    static class MyRunnable implements Runnable{

        @Override
        public void run() {
            while (true){
                System.out.println("Runnable is running");
            }

        }
    }

    static class MyRunnableOne implements Runnable{

        @Override
        public void run() {
            while (true){
                System.out.println("RunnableOne is running");
            }

        }
    }
}

在这里插入图片描述

线程应用

Thread和Runnable都能实现线程,那么有什么区别呢?

Thread是独立的

class MyThreadOne extends Thread{
    private int total=100;
    @Override
    public void run() {
        while (total>=1){
            //获取当前线程
            Thread thread=Thread.currentThread();
            //获取线程名
            String th_name=thread.getName();
            System.out.println(th_name+"正在发售第"+total--+"张票");
        }
    }
}

public class MainApp {

    public static void main(String[] args) {
        MyThreadOne myThreadOne1=new MyThreadOne();
        MyThreadOne myThreadOne2=new MyThreadOne();
        MyThreadOne myThreadOne3=new MyThreadOne();
        myThreadOne1.start();
        myThreadOne2.start();
        myThreadOne3.start();
    }

}

代码实现了继承Thread,内部有100张票,三个线程发售。
在这里插入图片描述
由结果可以发现线程Thread-0和1,2都各自发售了第100次票,显然他们是独立的。

Runnable线程是合作的

package com.company.runnable;

class MyRunnable implements Runnable{
    private int total=100;
    @Override
    public void run() {
        while (total>=1){
            //获取当前线程
            Thread thread=Thread.currentThread();
            //获取线程名
            String th_name=thread.getName();
            System.out.println(th_name+"正在发售第"+total--+"张票");
        }
    }
}

public class MainApp {
    public static void main(String[] args) {
        Thread myThreadOne1=new Thread(new MyRunnable());
        Thread myThreadOne2=new Thread(new MyRunnable());
        Thread myThreadOne3=new Thread(new MyRunnable());
        myThreadOne1.start();
        myThreadOne2.start();
        myThreadOne3.start();

    }
}

代码继承Runnable重写run方法,内部100张票
在这里插入图片描述
由结果可以看出所有线程共享成员变量。

除了上面案例外由多态特性也可以理解,通过实例化Thread创建是不同的对象,对象间是独立的,毫无关联的。而实现Runnable接口,仅仅是重写了run()方法,他们共享成员变量。

线程生命周期及状态转换

Java对象中任何对象都有生命周期,线程也不例外,Thread对象创建完成,线程的生命周期就开始了,当run抛出异常或阶段性关闭,线程就结束了。

线程的生命周期分为五个阶段:新建状态(New)、就绪状态(Runnable)、运行状态(Running)、阻塞状态(Blocked)、死亡状态(Terminated),线程的不同状态表明了线程当前正在运行的活动。

  1. 新建状态
    JVM分配内存,没有任何线程特征。
  2. 就绪状态
    当线程调用start()方法后,线程进入就绪状态,线程位于线程队列中,等待CPU分配。
  3. 运行状态
    获得CPU使用权后执行线程执行体,该程序处于运行状态。系统剥夺CPU使用权后,处于等待状态,等待再次占有CPU,直至程序结束。
  4. 阻塞状态
    运行的线程由于某种原因,被迫让出CPU使用权而进入阻塞状态。此时线程并没有进入等待对列,CPU由其他等待对列中的线程使用,直至解除阻塞状态,进入等待对列,等待系统分配CPU使用权后才能再次执行。
    notify()和·wait()方法可以使线程进入阻塞状态。sleep()方法使阻塞特定时间,join()加入新进程会阻塞原进程,直至新进程运行完毕。

线程调度

程序的多个线程逻辑上使并发的,某个线程要想被执行必须获取CPU占有权,由Java虚拟机按特定的机制为线程分配CPU的使用权叫线程的调度。

线程调度由两种模型:分时调度和抢占式调度。分时调度使在实现线上,按相同维度的时间片轮流分配CPU使用权。抢占式调度是按优先级分配CPU使用权。

线程优先级
Java JVM默认是抢占式调度,优先级决定了CPU分配顺序。
在这里插入图片描述
图中大写的常量设置线程优先级。

线程休眠
sleep(long millis)方法让线程休眠一段时间把CPU让给其他线程。该方法时静态方法,只能控制当前的线程休眠,休眠结束后,该线程进入等待对列。

线程让步
线程让步时正在执行的线程将CPU让给其他线程,yield()方法。

线程插队
线程可以通过join()方法在某个线程之前执行。

线程安全
多线程共享资源时由于延迟或其他原因导致的错误。

class MyRunnable implements Runnable{
    private int total=5;
    @Override
    public void run() {
        while (total>=1){
            //获取当前线程
            Thread thread=Thread.currentThread();
            //获取线程名
            String th_name=thread.getName();
            //添加延迟
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(th_name+"正在发售第"+total--+"张票");
        }
    }
}


public class MainApp {
    public static void main(String[] args) {
        Thread myThreadOne1=new Thread(new MyRunnable());
        Thread myThreadOne2=new Thread(new MyRunnable());
        Thread myThreadOne3=new Thread(new MyRunnable());
        myThreadOne1.start();
        myThreadOne2.start();
        myThreadOne3.start();

    }
}


代码模拟了10毫秒的延迟,线程执行的进度是不一样的。
在这里插入图片描述
由于线程都有10毫秒的休眠,出现了很多重复的情况。

Java提供了同步机制,当多个线程使用同一个共享资源时将执行共享资源的代码放在synchronized关键字修饰的代码块中,这个代码块被称作同步代码块。

synchronized 返回值类型 方法名([参数列表]){代码块}

synchronized (obj){
    while (total>0){
        //获取当前线程
        Thread thread=Thread.currentThread();
        //获取线程名
        String th_name=thread.getName();
        //添加延迟
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(th_name+"正在发售第"+total--+"张票");
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Java中,线程是程序执行的基本单元,用于并发执行任务。每个线程都有自己的生命周期,包括创建、运行、阻塞和终止。线程的创建可以通过继承Thread类或实现Runnable接口来实现。 线程池则是Java中一种高效的线程管理机制,它预先创建一定数量的工作线程,并在需要执行任务时从线程池中获取线程进行处理,当任务完成后,线程会返回到线程池等待下一次调度,而不是立即结束。这样可以避免频繁地创建和销毁线程带来的开销,提高系统的性能和资源利用率。 以下是Java线程线程池的一些关键点: 1. **线程创建**: - **继承Thread类**:创建自定义线程类并重写run()方法。 - **实现Runnable接口**:创建Runnable接口的实现类,提供run()方法,然后用Thread构造函数创建Thread实例。 2. **线程状态**: - 新建(New):线程对象被创建但还未启动。 - 运行(Runnable):线程正在执行run()方法。 - 阻塞(Blocked):线程因某个条件而暂停,如I/O操作等待数据。 - 等待(Waiting):线程在调用wait()方法后,进入等待状态,直到被其他线程唤醒。 - 守护(Terminated):非守护线程完成或主线程结束,守护线程自动退出。 3. **线程池组件**: - ExecutorService:线程池的核心接口,提供了提交任务和控制线程的方法。 - ThreadPoolExecutor:实现了ExecutorService,包含核心线程数、最大线程数、任务队列等配置。 - ScheduledThreadPoolExecutor:支持定时和周期性任务。 4. **线程池的优势**: - **资源复用**:减少线程创建和销毁的开销。 - **线程管理和调度**:灵活设置线程数量、线程优先级和任务执行策略。 - **避免死锁**:由于任务有顺序地等待特定资源,减少了死锁的可能性。 - **可扩展性**:随着任务增加,线程池可以根据需要动态调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xvwen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值