Java之线程篇一

目录

如何理解进程?

进程和线程的区别

线程的优点

线程的缺点

线程异常

线程用途

创建线程

方法一:继承Thread类,重写run()

观察线程

小结

[方法二: 实现Runnable接口,重写run()](#方法二: 实现Runnable接口,重写run())

方法三:继承Thread类,重写run(),使用匿名内部类

方法四:实现Runnable接口,重写run(),使用匿名内部类

方法五:使用lambda表达式


如何理解进程?

用户视角:内核数据结构+对应的代码和数据!

内核视角:承担分配系统资源的实体!

进程和线程的区别

进程是包含线程的,每个进程至少有一个线程,即主线程;

进程和进程之间不共享内存空间,同一个进程的线程之间共享同一个内存空间;

进程是系统分配资源的最小单位,线程是系统调度的最小单位;

线程在进程内部执行;

线程的创建,切换及终止效率更高;

进程有自己的内存地址空间,线程只独享指令流执行的必要资源,如寄存器和栈;

由于同一进程的各线程间共享内存和文件资源,可以不通过内核进行直接通信。

线程的优点

创建一个新线程的代价要比创建一个新进程小得多;

与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多;

线程占用的资源要比进程少很多;

能充分利用多处理器的可并行数量;

在等待慢速 I/O 操作结束的同时,程序可执行其他的计算任务;

计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现;

I/O 密集型应用,为了提高性能,将 I/O 操作重叠。线程可以同时等待不同的 I/O 操作。

线程的缺点

性能损失

一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计算密集型,线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变。

健壮性降低

编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。

缺乏访问控制

进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。

编程难度提高

编写与调试一个多线程程序比单线程程序困难得多

线程异常

因为创建的新线程,与主线程共用地址空间,页表等,所以新线程出现异常就会引起整个线程组异常。

单个线程如果出现除零,野指针问题导致线程崩溃,进程也会随着崩溃。

线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该进程内的所有线程也就随即退出 。

线程用途

合理的使用多线程,能提高 CPU 密集型程序的执行效率

合理的使用多线程,能提高 IO 密集型程序的用户体验(如生活中我们一边写代码一边下载开发工具,就是多线程运行的一种表现)

创建线程
方法一:继承Thread类,重写run()

代码

java 复制代码
class MyThread extends Thread{
    @Override
    public void run() {
        while(true){
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class Demo01 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new MyThread();
        t.start();
        while(true){
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }
}

运行结果

我们会发现,两个线程不分先后顺序的不断打印。

观察线程

使用JDK自带的工具jconsole.exe进行观察线程,以"管理员方式运行"

观察到的main线程

观察到我们创建的Thread线程

小结

Thread类里面的run()方法是线程的入口方法,描述了线程具体要做什么任务;

start()和run()都是Thread类的成员方法;

start()则是真正调用了系统API,在系统中创建出线程,让线程再调用run().

方法二: 实现Runnable接口,重写run()

代码

java 复制代码
class MyRunnable implements Runnable{
    @Override
    public void run() {
        while(true){
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class Demo02 {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable=new MyRunnable();
        Thread t=new Thread(runnable);
        t.start();

        while(true){
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }
}

运行结果

我们会发现,两个线程不分先后顺序的不断打印。

方法三:继承Thread类,重写run(),使用匿名内部类

代码

java 复制代码
public class Demo03 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(){
            @Override
            public void run() {
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        
        t.start();
        while(true){
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }
}

运行结果

我们会发现,两个线程不分先后顺序的不断打印。

方法四:实现Runnable接口,重写run(),使用匿名内部类

代码

java 复制代码
public class Demo04 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        t.start();

        while(true){
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }
}

运行结果

我们会发现,两个线程不分先后顺序的不断打印。

方法五:使用lambda表达式

代码

java 复制代码
public class Demo05 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            while(true){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t.start();

        while(true){
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }
}

运行结果

我们会发现,两个线程不分先后顺序的不断打印。

相关推荐
阿达King哥3 分钟前
Java虚拟机(JVM)平台无关?相关?
java·jvm
不是AI9 分钟前
【Java编程】【计算机视觉】一种简单的图片加/解密算法
java·算法·计算机视觉
森叶16 分钟前
Java NIO & Java 虚拟线程(微线程)与 Go 协程的运行原理不同 为何Go 能在低配机器上承接10万 Websocket 协议连接
java·websocket·nio
程序员小蘇35 分钟前
一天一个java知识点----Tomcat与Servlet
java·servlet·tomcat
Moso_Rx1 小时前
JavaEE——线程安全
java·安全·java-ee
岁岁岁平安1 小时前
SpringMVC入门学习总结(2025.04.16)
java·spring·java-ee·mvc·springmvc
日月星辰Ace1 小时前
@JsonProperty 用于构造方法和属性
java
日月星辰Ace2 小时前
@TestPropertySource 造成 SpringBoot Test 中对同一个 Bean 使用不同实例
java·spring boot
SimonKing2 小时前
短信被截断?5分钟用Java打造企业级短链服务
java·后端·架构
XuanXu2 小时前
SpringBoot3.0启动流程研究
java·spring boot