Java多线程

2023-12-15 10:48:40

Java多线程

Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机, Java 仍是企业和开发人员的首选开发平台。
??

课程内容的介绍

1. 线程中的概念
2. 多线程的实现方式
3. 线程中的常用方法
4. 线程的生命周期
5. 线程同步
6. 其他
??

一、线程的相关概念

1.进程和线程
进程:一个独立的正在执行的程序。
线程:一个进程的最基本的执行单位,执行路径。

??
多进程:在操作系统中,同时运行多个程序。
????????多进程的好处:可以充分利用CPU ,提高 CPU 的使用率。
多线程:在同一个进程 ( 应用程序 ) 中同时执行多个线程。
????????多线程的好处:提高进程的执行使用率,提高了CPU 的使用率。
??
注意:
1. 在同一个时间点一个 CPU 中只可能有一个线程在执行。
2. 多线程不能提高效率、反而会降低效率,但是可以提高 CPU 的使用率。
3. 一个进程如果有多条执行路径,则称为多线程程序。
4. Java 虚拟机的启动至少开启了两条线程,主线程和垃圾回收线程。
5. 一个线程可以理解为进程的子任务。
? ??

? ?

二、线程的实现方式

线程 是程序中执行的线程。 Java 虚拟机允许应用程序同时执行多个执行线程。
??
1.第一种创建方式
实现的步骤:
1. 创建 Thread 类的子类
2. 重写 run 方法
3. 创建线程对象
4. 启动线程
??
案例代码
package com.bobo.thread;

public class ThreadDemo02 {

    /**
     * 线程的第一种实现方式
     *     通过创建Thread类的子类来实现
     * @param args
     */
    public static void main(String[] args) {
        System.out.println("main方法执行了1...");
        // Java中的线程 本质上就是一个Thread对象
        Thread t1 = new ThreadTest01();
        // 启动一个新的线程
        // start方法并不能直接开启一个新的线程,真正开启线程的是 CPU,
        // 当CPU空闲或者分配到此任务的时候就会创建一个新的线程,然后执行run方法中的代码
         t1.start();
         // t1.start(); 线程不能够启动多次 IllegalThreadStateException
        // 如果要创建多个线程,就创建多个Thread对象即可
        Thread t2 = new ThreadTest01();
        t2.start();
        // t1.run(); // 这个是显示的调用Thread的run方法 并没有开启新的线程
        for(int i = 0 ; i< 10 ; i++){
            System.out.println("main方法的循环..."+i);
        }
        System.out.println("main方法执行结束了3...");
    }
}

/**
 * 第一个自定义的线程类
 *    继承Thread父类
 *    重写run方法
 */
class ThreadTest01 extends Thread{

    /**
     * 当线程启动的时候会执行run方法中的代码
     */
    @Override
    public void run() {
        System.out.println("我们的第一个线程执行了2....");
        for(int i = 0 ; i < 10 ; i ++){
            System.out.println("子线程:"+i);
        }
    }
}
??
输出结果
main方法执行了1...
我们的第一个线程执行了2....
我们的第一个线程执行了2....
子线程:0
子线程:1
子线程:2
子线程:3
子线程:4
子线程:5
子线程:6
子线程:7
子线程:8
子线程:9
子线程:0
子线程:1
子线程:2
main方法的循环...0
main方法的循环...1
main方法的循环...2
main方法的循环...3
main方法的循环...4
main方法的循环...5
main方法的循环...6
main方法的循环...7
子线程:3
子线程:4
子线程:5
子线程:6
子线程:7
子线程:8
子线程:9
main方法的循环...8
main方法的循环...9
main方法执行结束了3...
??
通过输出结果我们也能看出来 子线程中的代码和主线程中的代码是 并行 执行的。

??
注意点:
1. 启动线程是使用 start 方法而不是 run 方法。
2. 线程不能启动多次,如果要创建多个线程,那么就需要创建多个 Thread 对象。
??

? ?
课堂案例
在主线程中打印 1 100 ,然后创建一个子线程实现大文件的复制工作。
package com.bobo.thread;

import java.io.*;

public class ThreadDemo03 extends Thread{

    /**
     * 在主线程中打印1到100,然后创建一个子线程实现大文件的复制工作。
     * @param args
     */
    public static void main(String[] args) {
        System.out.println("主线程开始了...");
        // 创建线程对象
        ThreadDemo03 t1 = new ThreadDemo03();
        t1.start();
        printNum(1,100);//主线程
        System.out.println("主线程结束了...");
    }

    @Override  //重写run方法,
    public void run() {
        long start = System.currentTimeMillis();
        System.out.println("子线程开始文件复制...");
        // 实现文件的复制
        try {
            copyFile(new File("D:/IO/TEST.png"),new File("D:/NewIO/TEST.png"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("子线程结束文件复制...耗时:" + (end-start));
    }

    /**
     * 通过循环打印数字
     * @param start
     * @param end
     */
    public static void printNum(int start , int end){
        //通过循环打印数字
        for (int i = start ; i <= end ; i ++){
            System.out.print(i + "\t");
            if(i % 5 == 0){
                System.out.println();
            }
        }
    }

    /**
     * 实现文件的复制操作
     * @param srcFile  要复制的源文件
     * @param descFile  要复制到的目标文件
     */
    public static void copyFile(File srcFile,File descFile) throws  Exception{

        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
        //通过缓冲流去读
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(descFile));
        byte[] bytes = new byte[1024*1024];
        int num = 0 ;
        while((num = bis.read(bytes)) != -1){
            bos.write(bytes,0,num);
        }
        bos.flush();;
        bos.close();
        bis.close();

    }
}
? ?
通过内部类的方式简化我们的线程的创建。
package com.bobo.thread;

public class ThreadDemo04 {

    /**
     * 如果我们创建的线程类 在程序中我们只需要创建一个线程就不需要再使用的情况下
     *    我们可以通过内部类的方式来简化操作
     * @param args
     */
    public static void main(String[] args) {
        Thread t1 = new Thread(){
            @Override  //重写run方法
            public void run() {
                System.out.println("子线程执行了...");
            }
        };
        t1.start();
    }
}
? ?
或者
package com.bobo.thread;

public class ThreadDemo05 {

    /**
     * 如果我们创建的线程类 在程序中我们只需要创建一个线程就不需要再使用的情况下
     *    我们可以通过内部类的方式来简化操作
     * @param args
     */
    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run() {
                System.out.println("子线程执行了.......");
            }
        }.start();
    }
}
??
2.第二种创建方式
在第一种实现方式中,我们是将线程的创建和线程执行的业务都封装在了 Thread 对象中,我们可以通过Runable 接口来实现线程程序代码和数据有效的分离。
Thread(Runnable target)
分配一个新的 Thread对象。
??
实现的步骤:
1. 创建 Runable 的实现类。
2. 重写 run 方法。
3. 创建 Runable 实例对象 ( 通过实现类来实现 )。
4. 创建 Thread 对象,并把第三部的 Runable 实现作为 Thread 构造方法的参数。
5. 启动线程。
??
package com.bobo.runable;

public class RunableDemo01 {

    /**
     * 线程的第二种方式
     *     本质是创建Thread对象的时候传递了一个Runable接口实现
     * @param args
     */
    public static void main(String[] args) {
        System.out.println("main执行了...");
        // 创建一个新的线程  Thread对象
        Runnable r1 = new RunableTest();
        //有别于第一种实现方式,通过Runable接口实现的线程可以多个线程同时操作一个Runable接口对象
        Thread t1 = new Thread(r1);
        // 启动线程
        t1.start();
        // 创建一个新的线程
        System.out.println("main结束了...");
    }
}

/**
 * 线程的第二种创建方式
 *   创建一个Runable接口的实现类
 */
class RunableTest implements Runnable{  //实现一个Runnable接口

    @Override
    public void run() {
        System.out.println("子线程执行了...");
    }
}
??

文章来源:https://blog.csdn.net/m0_52514893/article/details/134924417
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。