理解 Java 多线程

Posted 李亦华的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解 Java 多线程相关的知识,希望对你有一定的参考价值。

并发与并行

  1. 并发:指两个或多个事件在同一时间段内发生。
  2. 并行:指两个或多个事件在同一时刻发生(同时发生)。

进程与线程

  1. 进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。

  2. 线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。

  3. 简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。

    img

    img

线程调度

  1. 分时调度:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。
  2. 抢占式调度:优先让优先级高的銭程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。

Java中的主线程

主线程:执行主(main)方法的线程。

单线程

单线程:Java程序中,只有一个线程,执行从main方法开始,从上到下的执行。

举例:

public class Student {

    public static void method1() {
        for (int i = 0; i < 5; i++) {
            System.out.println("我正在执行Student的方法1");
        }
    }

    public static void method2() {
        for (int i = 0; i < 5; i++) {
            System.out.println("我正在执行Student的方法2");
        }
    }

    public Student() {
    }

}
public class Demo01MainThread {
    public static void main(String[] args) {
        Student.method1();
        Student.method2();
    }
}
控制台输出:
我正在执行Student的方法1
我正在执行Student的方法1
我正在执行Student的方法1
我正在执行Student的方法1
我正在执行Student的方法1
我正在执行Student的方法2
我正在执行Student的方法2
我正在执行Student的方法2
我正在执行Student的方法2
我正在执行Student的方法2

从控制台输出可以知道,程序从上到下的执行,从方法1开始执行,到方法2开始执行。

多线程

创建多线程程序的第一种方式

创建Thread类的子类
java.lang.Thread类:是描述线程的类,我们想要实现多线程程序,就必须继承Thread类
实现步骤:

  1. 创建一个 Thread类的子类。
  2. 在Thread类的子类中重写Thread类中的run方法,设置线程任务(开启线程要做什么)。
  3. 创建Thread类的子类对象。
  4. 调用Thread类中的start方法,开启新的线程,执行run方法

void start()使该线程开始执行。Java虚拟机调用该线程的run方法。结果是两个线程并发地运行;当前线程(main线程)和另一个线程(创建的新线程,执行其run方法)。多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。Java程序属于抢占式调度,哪个线程的优先级高,哪个线程优先执行;同一个优先级,随机选择一个执行。

  1. 举例

    public class Person extends Thread {
    
        /**
         * 重写Thread类的run方法,设置任务
         */
        @Override
        public void run() {
            for (int i = 0; i < 6; i++) {
                System.out.println("我正在执行重写的run方法中的线程:" + i);
            }
        }
    
    }
    
    public class Demo02MainThread {
        public static void main(String[] args) {
            // 创建Thread的子类对象
            Thread myThread = new Person();
            // 调用Thread类中的start方法,开启新的线程,执行run方法
            myThread.start();
    
            // main方法中的要执行的线程
            for (int i = 0; i < 6; i++) {
                System.out.println("我正在执行main方法的线程:" + i);
            }
    
        }
    }
    
  2. 控制台输出

    我正在执行main方法的线程:0
    我正在执行main方法的线程:1
    我正在执行重写的run方法中的线程:0
    我正在执行main方法的线程:2
    我正在执行重写的run方法中的线程:1
    我正在执行重写的run方法中的线程:2
    我正在执行重写的run方法中的线程:3
    我正在执行重写的run方法中的线程:4
    我正在执行重写的run方法中的线程:5
    我正在执行main方法的线程:3
    我正在执行main方法的线程:4
    我正在执行main方法的线程:5
    

多线程的原理

img

Thread类的常用方法

public String getName()
// 获取当前线程名称。
public void start()
// 导致此线程开始执行;Java虚拟机调用此线程的run方法。
public void run()
// 此线程要执行的任务在此处定义代码。
public static void sleep(long millis
// 返回对当前正在执行的线程对象的引用。

getName()方法、start()方法、run()方法

public class MyThread extends Thread {
    @Override
    public void run() {
        // 此线程要执行的任务
    }
}
public class Demo01MyThread {

    public static void main(String[] args) {

        MyThread myThread1 = new MyThread();
        myThread1.start();
        String run1Name = myThread1.getName();


        MyThread myThread2 = new MyThread();
        myThread2.start();
        String run2Name = myThread2.getName();

        MyThread myThread3 = new MyThread();
        myThread3.start();
        String run3Name = myThread3.getName();

        System.out.println("线程1名称:" + run1Name);
        System.out.println("线程2名称:" + run2Name);
        System.out.println("线程3名称:" + run3Name);
    }

}
控制台输出:
线程1名称:Thread-0
线程2名称:Thread-1
线程3名称:Thread-2

currentThread()方法

public class MyThread extends Thread {

    @Override
    public void run() {
        // 此线程要执行的任务
        System.out.println(Thread.currentThread());
    }

}
public class Demo02MyThread {

    public static void main(String[] args) {
        MyThread myThread1 = new MyThread();
        myThread1.start();

        MyThread myThread2 = new MyThread();
        myThread2.start();

        MyThread myThread3 = new MyThread();
        myThread3.start();
    }

}
控制台输出:
Thread[Thread-0,5,main]
Thread[Thread-2,5,main]
Thread[Thread-1,5,main]

了解一下setName()方法

setName():设置线程的名称

public class Demo03MyThread {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.setName("李华");
        myThread.start();
    }
}
控制台输出:
Thread[李华,5,main]

sleep()方法

public class MyThread2 extends Thread {
    @Override
    public void run() {
        // 此线程要执行的任务
        for (int i = 0; i < 11; i++) {
            try {
                // 参数为毫秒
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.print("第" + i  + "秒    ");
        }
    }
}
public class Demo01MyThread2 {
    public static void main(String[] args) {
        MyThread2 thread2 = new MyThread2();
        thread2.start();
    }
}
控制台输出:
第0秒    第1秒    第2秒    第3秒    第4秒    第5秒    第6秒    第7秒    第8秒    第9秒    第10秒  

创建多线程的第二种方式

实现Runnable接口,来创建多线程。

实现步骤

  1. 创建一个 Runnable接口的实现类。
  2. 在实现类中重写 Runnable接口的run方法,设置线程任务。
  3. 创建一个 Runnable接口的实现类对象。
  4. 创建Thread类对象,构造方法中传递 Runnable接口的实现类对象。
  5. 调用 Threads类中的 start方法,开启新的线程执行run方法

举例

// 创建一个 Runnable接口的实现类。
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        // 在实现类中重写 Runnable接口的run方法,设置线程任务。
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName() + " ~~~~~~~~~ " + i);
        }
    }
}
public class DemoRunnable {
    public static void main(String[] args) {
        // 创建一个 Runnable接口的实现类对象。
        MyRunnable myRunnable = new MyRunnable();

        // 创建Thread类对象,构造方法中传递 Runnable接口的实现类对象。
        Thread thread = new Thread(myRunnable);

        // 调用 Threads类中的 start方法,开启新的线程执行run方法
        thread.start();

        // main方法线程
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName() + " ~~~~~~~~~ " + i);
        }
    }
}
控制台输出:
main ~~~~~~~~~ 0
Thread-0 ~~~~~~~~~ 0
main ~~~~~~~~~ 1
Thread-0 ~~~~~~~~~ 1
Thread-0 ~~~~~~~~~ 2
main ~~~~~~~~~ 2

使用Runnable相比Thread有哪些优势

  1. 适合多个相同的程序代码的线程去共享同一个资源。
  2. 可以避兔Java中的单继承的局限性。
  3. 増加程序的健壮性,实现解耦(把设置线程任务和开启线程分开)操作,代码可以被多个线程共享,代码和线程独立。
  4. 线程池只能放入实现 Runable或 Callable类线程,不能直接放入继承 Thread的类。

内部类实现创建多线程

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        // 在实现类中重写 Runnable接口的run方法,设置线程任务。
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName() + " ~~~~~~~~~ " + i);
        }
    }
}
public class DemoSimpleThread {
    public static void main(String[] args) {
        // 创建一个 Runnable接口的实现类对象。
        MyRunnable myRunnable = new MyRunnable();

        new Thread(myRunnable) {
            @Override
            public void run() {
                System.out.print("匿名内部类的方式实现多线程的创建:");
                System.out.println(Thread.currentThread().getName() + "线程");
            }
        }.start();

        // main线程
        System.out.println("main线程");
    }
}
控制台输出:
main线程
匿名内部类的方式实现多线程的创建:Thread-0线程

以上是关于理解 Java 多线程的主要内容,如果未能解决你的问题,请参考以下文章

多个请求是多线程吗

java之多线程的理解

java多线程理解及实现

Java多线程深入理解

Java多线程wait()用while代码块的理解

java多线程,多线程加锁以及Condition类的使用