进程概述:

  在这之前,有必要了解一下什么是进程?

  在一个操作系统中,每个独立的执行的程序都可称为一个进程,也就是“正在运行的程序”。如图所示:

线程概述:

  如上所述,每个运行的程序都是一个进程,在一个进程中还可以有多个执行单元同时运行,这些执行单元可以看做程序的执行的一条条线索,被称为线程。操作系统中的每一个进程都至少存在一个线程。

多线程的概念:

  多线程是指一个应用程序中有许多条并发执行的线索,每条线索都被称作一个线程,他们会交替执行,彼此间进行通信。

  多线程是实现并发机制的一种有效手段。进程和线程一样,都是实现并发的一个基本单位。线程是比进程更小的执行单位,线程是进程的基础之上进行进一步的划分。所谓多线程是指一个进程在执行过程中可以产生多个更小的程序单元,这些更小的单元称为线程,这些线程可以同时存在,同时运行,一个进程可能包含多个同时执行的线程。

Java中线程实现的方式

在 Java 中实现多线程有两种手段,一种是继承 Thread 类,另一种就是实现 Runnable 接口。

继承Thread类创建多线程

 1 @SpringBootTest
2 public class Example {
3 public static void main(String[] args) {
4 // 实例化对象
5 MyThread myThread = new MyThread();
6 // 调用线程主体
7 new Thread(myThread,"线程A").start();
8 new Thread(myThread,"线程B").start();
9 }
10 }
11 // 继承Thread类
12 class MyThread extends Thread{
13 //覆写run()方法,作为线程的操作主体
14 public void run(){
15 for (int i = 0; i <5 ; i++) {
16 Thread th = Thread.currentThread();//获取当前线程
17 String name = th.getName();//获取当前线程的名字
18 System.out.println(name +"运行:i = "+i);
19 }
20 }
21 }

程序运行结果:

实现Runnable接口创建多线程

 1 @SpringBootTest
2 public class Example {
3 public static void main(String[] args) {
4 // 实例化对象
5 MyThread myThread = new MyThread();
6 // 调用线程主体
7 new Thread(myThread,"线程A").start();
8 new Thread(myThread,"线程B").start();
9 }
10 }
11 // 实现Runnable接口,作为线程的实现类
12 class MyThread implements Runnable{
13 //重写run()方法,作为线程的操作主体
14 public void run(){
15 for (int i = 0; i <5 ; i++) {
16 Thread th = Thread.currentThread();//获取当前线程
17 String name = th.getName();//获取当前线程的名字
18 System.out.println(name +"运行:i = "+i);
19 }
20 }
21 }

程序运行结果:

  

  从程序可以看出,现在的两个线程对象是交错运行的,哪个线程对象抢到了 CPU 资源,哪个线程就可以运行,所以程序每次的运行结果肯定是不一样的,在线程启动虽然调用的是 start() 方法,但实际上调用的却是 run() 方法定义的主体。

两种实现多线程方式的对比

   虽说百度答案都千篇一律,但没有比自己动手验证更令人印象深刻,毕竟:实践是检验真理的唯一标准!

假设售票厅有四个窗口可发售某日某次列车的100张车票,这时,100张车票可以看做共享资源,四个售票窗口要创建四个线程。

继承Thread类

 1 @SpringBootTest
2 public class Example {
3 public static void main(String[] args) {
4 // 实例化对象
5 MyThread myThread1 = new MyThread();
6 MyThread myThread2 = new MyThread();
7 MyThread myThread3 = new MyThread();
8 MyThread myThread4 = new MyThread();
9 // 调用线程主体
10 myThread1.setName("窗口一");
11 myThread2.setName("窗口二");
12 myThread3.setName("窗口三");
13 myThread4.setName("窗口四");
14 myThread1.start();
15 myThread2.start();
16 myThread3.start();
17 myThread4.start();
18
19 }
20 }
21
22 // 继承Thread类
23 class MyThread extends Thread{
24 private int tickets = 100;
25 //覆写run()方法,作为线程的操作主体
26 public void run(){
27 while (true){ //通过死循环打印语句
28 if (tickets > 0) {
29 Thread th = Thread.currentThread();//获取当前线程
30 String name = th.getName();//获取当前线程的名字
31 System.out.println(name + ":正在发售第" + tickets-- + "张票!");
32 }
33 }
34 }
35 }

程序运行结果:

  从运行结果可以看出,每张票都被打印了四次,四个线程没有共享100张票,而是各自售出了100张。

  现实中铁路系统中的票资源是共享的,因此上面的运行结果显然不合理。为了保证资源共享,在程序中只能创建一个售票对象,然后开启多个线程去运行同一个售票对象的售票方法,简单来说就是四个线程运行同一个售票程序,这时候就需要通过Runnable接口来实现。

实现Runnable接口

 1 @SpringBootTest
2 public class Example {
3 public static void main(String[] args) {
4 // 实例化对象
5 MyThread myThread = new MyThread();
6 // 调用线程主体
7 new Thread(myThread,"窗口一").start();
8 new Thread(myThread,"窗口二").start();
9 new Thread(myThread,"窗口三").start();
10 new Thread(myThread,"窗口四").start();
11 }
12 }
13 // 实现Runnable接口,作为线程的实现类
14 class MyThread implements Runnable{
15 private int tickets = 100;
16 //重写run()方法,作为线程的操作主体
17 public void run(){
18 while (true){ //通过死循环打印语句
19 if (tickets > 0) {
20 Thread th = Thread.currentThread();//获取当前线程
21 String name = th.getName();//获取当前线程的名字
22 System.out.println(name + ":正在发售第" + tickets-- + "张票!");
23 }
24 }
25 }
26 }

  只创建了一个MyThread 对象,然后创建了四个线程,在每个线程上面都去调用MyThread 对象中的run()方法,这样就可以确保四个线程访问的是同一个tickets变量,共享100张车票。

程序运行结果:

 

  通过对比可以看出,实现Runnable接口相对于继承Thread类来说,有这如下显著的好处:

  1. 适合多个相同程序代码的线程去处理同一个资源的情况,把线程同程序代码,数据有效的分离,很好的体现了面向对象的设计思想。
  2. 可以避免由于java单继承带来的局限性。由于一个类不能同时有两个父类,使用一个已经继承了某一个类的子类创建线程,所以不能用继承Thread类的方式,那么就只能采用实现Runnable接口的方式。

后台线程

  在上面的案例中,当main()方法创建并启动四个新的线程后,main()方法中的代码执行完毕,这时方法结束,main线程也就随之结束了。实际上,虽然main线程结束了,但整个java程序却没有随之结束,仍会执行售票的代码。对于java程序来说,只要还有一个前台线程在运行,这个进程就不会结束,如果一个进程中只有后台线程运行,这个进程就会结束,前台线程和后台线程是一种相对的概念,新创建的线程默认都是前台线程,如果某个线程对象在启动之前调用了setDaemon语句,这个线程就变成了一个后台线程。

 1 @SpringBootTest
2 //创建DamonThread类,实现Runnable接口
3 class DamonThread implements Runnable{
4
5 //实现接口中的run()方法
6 @Override
7 public void run() {
8 while (true){
9 System.out.println(Thread.currentThread().getName() + "正在运行!");
10 }
11 }
12 }
13 public class Example {
14 public static void main(String[] args) {
15 System.out.println("main线程是后台线程吗?" +Thread.currentThread().isDaemon());
16 //创建一个DamonThread对象dt
17 DamonThread dt = new DamonThread();
18 //创建线程thread共享dt资源
19 Thread thread = new Thread(dt,"后台线程");
20 //判断是否为后台线程
21 System.out.println("thread默认是后台线程吗?" + thread.isDaemon());
22 //将线程thread设置为后台线程
23 thread.setDaemon(true);
24 //开启线程
25 thread.start();
26 for (int i = 0; i < 5; i++) {
27 System.out.println(i);
28 }
29 }
30 }

程序运行结果:

  当开启线程thead后,会执行死循环中的打印语句,将线程thread设置为后台线程后,前台线程就会死亡,JVM会通知后台线程。后台线程从接收指令到做出响应,需要一定的时间,因此,打印了几次“后台线程正在运行!”语句后,后台线程也结束了,由此说明进程中只有后台线程时,进程就会结束。

  注意:要将某个线程设置为后台线程,必须在该线程启动之前,也就是说setDaemon()方法必须在start()方法调用之前,否则会引发异常。

Java多线程--两种实现方式的更多相关文章

  1. python 多线程两种实现方式,Python多线程下的_strptime问题,

    python 多线程两种实现方式 原创 Linux操作系统 作者:杨奇龙 时间:2014-06-08 20:24:26  44021  0 目前python 提供了几种多线程实现方式 thread,t ...

  2. java的两种同步方式, Synchronized与ReentrantLock的区别

    java在编写多线程程序时,为了保证线程安全,需要对数据同步,经常用到两种同步方式就是Synchronized和重入锁ReentrantLock. 相似点: 这两种同步方式有很多相似之处,它们都是加锁 ...

  3. JAVA多线程三种实现方式

    JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable.Future实现有返回结果的多线程.其中前两种方式线程执行完后都没 ...

  4. Java 多线程 三种实现方式

    Java多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable.Future实现有返回结果的多线程.其中前两种方式线程执行完后都没 ...

  5. 【Python】python 多线程两种实现方式

    目前python 提供了几种多线程实现方式 thread,threading,multithreading ,其中thread模块比较底层,而threading模块是对thread做了一些包装,可以更 ...

  6. Java多线程-两种常用的线程计数器CountDownLatch和循环屏障CyclicBarrier

    Java多线程编程-(1)-线程安全和锁Synchronized概念 Java多线程编程-(2)-可重入锁以及Synchronized的其他基本特性 Java多线程编程-(3)-从一个错误的双重校验锁 ...

  7. Java HashMap两种遍历方式

    第一种: Map map = new HashMap(); Iterator iter = map.entrySet().iterator(); while (iter.hasNext()) { Ma ...

  8. JAVA - 多线程 两种方法的比较

    一.继承Thread类 实现方法: (1).首先定义一个类去继承Thread父类,重写父类中的run()方法.在run()方法中加入具体的任务代码或处理逻辑.(2).直接创建一个ThreadDemo2 ...

  9. java多线程 —— 两种实际应用场景模拟

    最近做的偏向并发了,因为以后消息会众多,所以,jms等多个线程操作数据的时候,对共享变量,这些要很注意,以防止发生线程不安全的情况. (一) 先说说第一个,模拟对信息的发送和接收.场景是这样的: 就像 ...

随机推荐

  1. Hello Python!用 Python 写一个抓取 CSDN 博客文章的简单爬虫

    网络上一提到 Python,总会有一些不知道是黑还是粉的人大喊着:Python 是世界上最好的语言.最近利用业余时间体验了下 Python 语言,并写了个爬虫爬取我 csdn 上关注的几个大神的博客, ...

  2. [leetcode]187. Repeated DNA Sequences寻找DNA中重复出现的子串

    很重要的一道题 题型适合在面试的时候考 位操作和哈希表结合 public List<String> findRepeatedDnaSequences(String s) { /* 寻找出现 ...

  3. 收下这款 Vue 项目模版,它将让你的开发效率在 2021 年提高 50%

    这是什么 vue-automation 是一款开箱即用的 Vue 项目模版,它基于 Vue CLI 4 众所周知,虽然 Vue CLI 提供了脚手架的功能,但由于官方的脚手架过于简单,运用在实际项目开 ...

  4. Java学习日报7.18

    /** * *//** * @author 86152 * */ package trangle;import java.util.Scanner; public class Trangle{ pub ...

  5. 容器编排系统K8s之NetworkPolicy资源

    前文我们了解了k8s的网络插件flannel的基础工作逻辑,回顾请参考:https://www.cnblogs.com/qiuhom-1874/p/14225657.html:今天我们来聊一下k8s上 ...

  6. 风炫安全WEB安全学习第二十二节课 DOM型XSS讲解

    风炫安全WEB安全学习第二十二节课 DOM型XSS讲解 Dom型XSS演示 通过Javascript,可以重构整个HTML文档,你可以添加.移除.改变或重排页面上的项目 要改变页面的某个东西,Java ...

  7. 强大生产力工具Alfred

    今天要给大家介绍的工具是Alfred,一款Mac下的高效生产力产品.它能做什么呢?简单的说就是:让你能够通过打几个字,就可以完成原本需要一顿操作的事情.举一个简单的栗子:如果我们要在Google搜索一 ...

  8. SpringBoot配置文件(1)

    配置文件 1.配置文件 SpringBoot使用一个全局的配置文件 application.properties application.yml 配置文件名是固定的: 他的作用是修改SpringBoo ...

  9. kubernets之服务的实现方式

    一  服务如何通过kubernetes集群的组件来实现其功能 1.1  节点上的所有的服务相关的功能实现都是通过节点上面的kube-proxy来实现的,服务提供了一个或者多个服务IP以及端口对客户端开 ...

  10. 负载均衡和故障转换(Failover)的连接RAC方法

    TAF:Transparent Application Failover,透明的应用切换,即在切换的过程中,用户感知不到.可以实现会话的切换(无法实现事务的切换,即没有提交的事务会回滚),即在不断开连 ...