进程与线程的区别以及线程状态及相互转换和创建线程方式的讲解

线程和进程区别

进程:是系统进行分配和管理资源的基本单位

线程:进程的一个执行单元,是进程内调度的实体、是CPU调度和分派的基本单位,是比进程更小的独立运行的基本单位。线程也被称为轻量级进程,线程是程序执行的最小单位。

一个程序至少一个进程,一个进程至少一个线程。

main函数演示

public class Demo {

    public static void main(String [] args) {
        System.out.println("123");
        try {
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

可以看到一个进程必然包含一个线程
在这里插入图片描述

进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。 而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。

线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式进行。

如何处理好同步与互斥是编写多线程程序的难点。 多进程程序更健壮,进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响, 而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,所以可能一个线程出现问题,进而导致整个程序出现问题

线程的状态及相互转换

查看Thread源码

查看类结构 State类定义
在这里插入图片描述

初始(NEW): 新创建了一个线程对象,但还没有调用start()方法。

运行(RUNNABLE): 处于可运行状态的线程正在JVM中执行,但它可能正在等待来自操作系统的其他资源,例如处理器。 Thread.start()会进入RUNNABLE等待状态,CPU为没和线程分配时间片,可能时间片还没分配过来,还没有执行真正的run方法

public class ThreadStateDemo {

    public static void main(String [] args) throws InterruptedException {

        //RUNNABLE 运行
        Thread thread =new Thread(()->{
            try {
                System.in.read();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        thread.start();
    }
}

阻塞(BLOCKED): 线程阻塞于synchronized锁,等待获取synchronized锁的状态。

public class ThreadStateDemo {

    public static void main(String [] args) throws InterruptedException {

        //BLOCKED 阻塞
        Object obj = new Object();
        Thread thread =new Thread(()->{
            synchronized (obj){
                try {
                    //获取锁后一直休眠
                    Thread.sleep(100000000000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();

        Thread.sleep(2000L);

        Thread thread2 =new Thread(()->{
            synchronized (obj){
            }
        });
        thread2.start();

    }
}

等待(WAITING): Object.wait()、join()、 LockSupport.park(),进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。

public class ThreadStateDemo {

    public static void main(String [] args) throws InterruptedException {

        //WAITING等待
        Object obj = new Object();
        Thread thread =new Thread(()->{
            synchronized (obj){
                try {
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();

    }
}

超市等待(TIME_WAITING): Object.wait(long)、Thread.join()、LockSupport.parkNanos()、LockSupport.parkUntil,该状态不同于WAITING,它可以在指定的时间内自行返回。

终止(TERMINATED): 表示该线程已经执行完毕。

线程状态转换图

在这里插入图片描述

创建线程的方式

方式一:继承Thread 并重写类的run方法

/**
 * 创建线程方式一
 */
public class MyThread extends Thread {

    /**
     * 打印当前线程名称
     */
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }

    public static void main(String [] args){

        MyThread myThread = new MyThread();
        myThread.setName("线程Demo1");
        //启动线程
        myThread.start();
    }
}

方式二:实现Runable接口,并实现run方法

/**
 * 创建线程方式二
 */
public class MyRunnable implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }

    public static void main(String [] args){

        Thread thread = new Thread(new MyRunnable());
        thread.setName("线程demo2");
        thread.start();
    }
}

方式三:匿名内部类

/**
 * 匿名内部类创建线程
 */
public class MyThread  {

    public static void main(String [] args){

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        });

        thread.start();
    }
}

方式四:Lambda表达式

JDK8的语法

public class Lambda {

    public static void main(String [] args){
        new Thread(() ->{
            System.out.println(Thread.currentThread().getName());
        }).start();
    }
}

方式五:线程池创建

虽然运行结束,但程序继续执行

/**
 * 线程池创建线程
 */
public class ThreadPool {

    public static void main(String [] args){
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(()->{
            System.out.println(Thread.currentThread().getName());
        });
    }
}

实际开发中,选第二种:java只允许单继承 但你可以实现多个接口

使用第二种可以增加程序的健壮性,可以被多个程序共享,代码跟数据相对独立

调用start()和run()有什么区别?

1.调用start()方法:

通知“线程规划器”当前线程已经准备就绪,等待调用线程对象的run()方法。这个过程就是让系统安排一个时间来调用Thread中的run()方法,使线程得到运行,启动线程,具有异步执行的效果。

调用start()方法,也就是线程状态转变成可运行状态的过程。

2.调用run()方法:

不是异步执行,而是同步执行,当前线程并不交给“线程规划器”来处理,而是由main主线程来调用run()方法,也就是必须等run()方法中的代码执行完后才可以执行后面的代码。

可以用上面代码测试一下,将start改为run输出的是main 而不是命名的线程

猜你喜欢

转载自blog.csdn.net/q736317048/article/details/113796001