【Java EE】-多线程编程(一) 认识线程

作者:学Java的冬瓜
博客主页:☀冬瓜的主页
专栏:【JavaEE】
分享:愉快骑行的一天!
主要内容:为什么使用并发编程?进程和线程之间的关系、创建线程的5种方式、怎么在电脑上查看线程和进程运行的情况。

【Java EE】-多线程编程(一) 认识线程_第1张图片

文章目录

  • 一、并发编程
    • 1、为什么使用并发编程?
    • 2、实现并发编程为什么多线程比多进程更快?
    • 3、多线程比多进程快,那是不是线程越多越好呢?
    • 4、关于稳定性
    • 5、关于安全性
  • 二、进程和线程之间的关系
  • 三、创建线程的5种方式
    • 1、继承Thread类,重写run()
    • 2、实现Runnable接口,重写run()
    • 3、继承Thread类,重写run(),使用匿名内部类
    • 4、实现Runnable接口,重写run(),使用匿名内部类
    • 5、使用lambda表达式
  • 四、启动线程start
  • 五、查看进程的操作

一、并发编程

1、为什么使用并发编程?

目的就是为了利用现在多核CPU的资源,让程序跑的快。目前CPU已经有5nm芯片,再往小了做难度很大,因为经典力学失效,开始涉及量子力学。

2、实现并发编程为什么多线程比多进程更快?

1> 进程是操作系统进行资源分配的基本单位。对于进程来说,大量的时间花在了申请资源/释放资源上;线程则省掉了申请资源/释放资源时间,把时间集中用于调度上,因为所有线程共享它们所处进程中的资源,不需要自己再去申请和释放资源。所以线程速度更快。
2> 线程是操作系统进行调度执行的基本单位。进程主要负责资源分配,线程主要负责调度执行。
3> 线程比进程创建、销毁、调度的速度要快。线程是轻量级的进程。

3、多线程比多进程快,那是不是线程越多越好呢?

并不是线程越多,程序越快。当线程足够多时,大量的时间就用于线程之间的调度切换,反而速度减慢,而且如果线程过多,还可能导致资源耗尽(内存,宽带等),导致其它线程无法使用。

4、关于稳定性

多进程比多线程更稳定。因为在多线程中,如果有一个线程异常,可能整个进程就崩了,该进程中的其它线程也会挂掉。而多进程中则进程之间相互独立,互不干扰。因此浏览器一般使用多进程编程模型,从而防止一个页面出现异常时,其它开好的页面全部崩掉。

5、关于安全性

多进程更安全,但是多线程更快,因此使用多线程然后解决多线程的问题是我们需要去训练的。同一个进程中的线程共享进程中的资源,所以很容易引发线程安全问题。而进程安全问题,在进程间通信时,可能会发生。

二、进程和线程之间的关系

  • 同一个进程中的线程共享这个进程的同一份资源(主要是内存和文件描述符表
        内存共享:线程1中new的对象,可以在线程2,3,4中直接使用。内存指针划定了共享内容的范围。
        文件描述符表共享:线程1中打开的文件,可以在线程2,3,4中直接使用。
  • PCB又叫进程控制块,是用来描述进程的,准确的说是一组PCB描述一个进程,每一个PCB描述一个线程。所以一个进程可能有一个线程,也可能有多个线程。进程中的这一组的内存指针和文件描述符表共用一份,状态、上下文、优先级、记账信息则每个pcb各有自己的一份。

如下图:
【Java EE】-多线程编程(一) 认识线程_第2张图片

三、创建线程的5种方式

1、继承Thread类,重写run()

class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("继承Thread类,重写run()!");
    }
}
public class ThreadTest {
    public static void main(String[] args) {
		// 法一:继承Thread,重写run()
        Thread t1 = new MyThread();
        t1.start();   // 创建一个线程
    }
}

2、实现Runnable接口,重写run()

  • Runable作用:描述一个要执行的任务,run() 方法就是要执行的任务细节
class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("实现Runnable接口,重写run()!");
    }
}
public class ThreadTest {
    public static void main(String[] args) {
		// 法二:runnable类实现Runnable接口,重写run(),再实例化线程对象
		// 1.描述一个任务
        Runnable runnable = new MyRunnable();
        // 2.将任务交给线程来执行
        Thread t2 = new Thread(runnable);
        t2.start();
    }
}

3、继承Thread类,重写run(),使用匿名内部类

public class ThreadTest {
    public static void main(String[] args) {
		// 法三:继承Thread类,重写run(),使用匿名内部类
        Thread t3 = new Thread(){
            @Override
            public void run() {
                System.out.println("继承Thread类,重写run(),使用匿名内部类!");
            }
        };
    }
}

4、实现Runnable接口,重写run(),使用匿名内部类

public class ThreadTest {
    public static void main(String[] args) {
		// 法四:实现Runnable接口,重写run(),使用匿名内部类!
        Thread t4 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("实现Runnable接口,重写run(),使用匿名内部类!");
            }
        });
    }
}

5、使用lambda表达式

public class ThreadTest {
    public static void main(String[] args) {
		// 法五:使用lambda表达式
        Thread t5 = new Thread(()->{
            System.out.println("使用lambda表达式!");
        });
    }
}

四、启动线程start

  • 线程创建并执行 run()方法的过程:t.start() 执行时,main调用操作系统api,通过操作系统api创建 t 线程的PCB,并且把要执行的指令交给这个PCB。当这个PCB被调度到CPU上时,就由这个PCB(线程)调用 run() 方法。
  • 线程销毁:当执行完 run()方法后,这个新的线程被销毁。
public class ThreadTest {
    public static void main(String[] args) {
		t1.start();   // start()是创建一个线程
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        System.out.println("Hello main Thread!");
    }
}

运行结果:由于操作系统调度线程是抢占式执行,执行顺序是不确定的!!!

继承Thread类,重写run()!
Hello main Thread!
实现Runnable接口,重写run()!
实现Runnable接口,重写run(),使用匿名内部类!
使用lambda表达式!
继承Thread类,重写run(),使用匿名内部类!

五、查看进程的操作

  • JDK路径下的bin里有个jconsole.exe(一般路径是:C:\Program Files\Java\jdk版本\bin\jconsole.exe),这里就可以连接进程,并查看这个进程中的信息。
    注意:
    如果你要看创建的线程,那就在run() 方法中使用while,从而让它一直执行,然后再查看进程信息。否则一旦新建的这个线程走完run方法,它就销毁了。
    还可以加上个sleep,让打印慢一点,便于观察。
    如下:
// 法三:继承Thread类,重写run(),使用匿名内部类
        Thread t3 = new Thread(){
            @Override
            public void run() {
                while (true) {
                	try {
                    	Thread.sleep(1000);
               		} catch (InterruptedException e) {
                   		e.printStackTrace();
                	}
                    System.out.println("继承Thread类,重写run(),使用匿名内部类!");
                }
            }
        };

你可能感兴趣的:(JAVAEE,java-ee,java,jvm)