Java高级重点知识点-18-线程

news2024/11/16 18:17:50

文章目录

  • 多线程
  • 线程
  • 线程安全
  • 线程状态
  • 线程间通信
  • 线程池

多线程

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

对于单核CPU系统中,我们可以同时运行多个程序,比如一边编辑记事本,一边听音乐,一边录屏。这些在宏观上是并行的,但是在微观上是并发的。

  1. 线程与进程
  • 进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创
    建、运行到消亡的过程。
  • 线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。

线程调度:

  • 分时调度:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
  • 抢占式调度
    优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
  1. 创建线程类
  • 定义Thread类的子类,并重写该类的run()方法。
  • 创建Thread子类的实例,即创建了线程对象
  • 调用线程对象的start()方法来启动该线程
public class CreateThread extends Thread{

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }
    }

    public static void main(String[] args) {
        CreateThread createThread = new CreateThread();
        createThread.start();
        System.out.println("over");
    }
}

线程

每一个执行线程都有一片自己所属的栈内存空间

  1. Thread类

构造方法:

  • public Thread() :分配一个新的线程对象。
  • public Thread(String name) :分配一个指定名字的新的线程对象。
  • public Thread(Runnable target) :分配一个带有指定目标新的线程对象。
  • public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字。

代码示例:

public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = new Thread();
        Thread thread1 = new Thread("小明线程");
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("我是Runnable接口创建的线程");
            }
        });
        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("我是Runnable接口创建的线程,并且还设置了名字");
            }
        }, "小明线程2");

        System.out.println(thread.getName());
        System.out.println(thread1.getName());
        System.out.println(thread2.getName());
        System.out.println(thread3.getName());
    }
}

在这里插入图片描述
常用方法:

  • public String getName() :获取当前线程名称。
  • public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。
  • public void run() :此线程要执行的任务在此处定义代码。
  • public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
  • public static Thread currentThread() :返回对当前正在执行的线程对象的引用。

代码示例

public class ThreadDemo2 extends Thread{

    public ThreadDemo2(String name) {
        super(name);
    }

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

    public static void main(String[] args) throws InterruptedException {
        ThreadDemo2 thread = new ThreadDemo2("小明线程");
        System.out.println(thread.getName());
        thread.start();
        long start = System.currentTimeMillis();
        Thread.sleep(10000);
        long end = System.currentTimeMillis();
        System.out.println((end - start) + "ms");
        thread.run();
    }
}

在这里插入图片描述
这里我们可以看到当程序执行的时候,直接输出了小明线程、以及线程名为小明线程的对象引用,然后再执行的时候主线程因为调用了 Thread.sleep(10000) 方法暂停了10s,在10s过后打印了一共消耗的时间以及主线程的线程对象引用。

  1. 创建线程的俩种方式以及其区别
  • 一种是继承Thread类方式
public class ThreadDemo2 extends Thread{
    @Override
    public void run() {
        System.out.println(Thread.currentThread());
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadDemo2 thread = new ThreadDemo2();
        System.out.println(thread.getName());
    }
}
  • 一种是实现Runnable接口方式
public class ThreadDemo2 implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread());
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadDemo2 threadDemo2 = new ThreadDemo2();
        Thread thread = new Thread(threadDemo2);
    }
}

区别:

  • 如果类继承Thread,则不适合资源共享,但是类实现了Runable接口的话,则很容易的实现资源共享。
  • 实现Runnable接口适合多个相同的程序代码的线程去共享同一个资源。
  • 实现Runnable接口可以避免java中的单继承的局限性。
  • 实现Runnable接口增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
  • 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。

注意:
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用
java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM其实在就是在操作系统中启动了一个进程

线程安全

多个线程同时运行,当这些线程同时运行相同的代码。程序每次运行结果和单线程运行的结果是一样 的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

  1. 线程同步

通过电影票买票操作进行线程的讲解,假设总共有100张票,分别使用三个窗口进行卖票,一直到票卖光为止。

public class Ticket implements Runnable {
    private int ticket = 100;
    @Override
    public void run() {
        while (true) {
            if (ticket > 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                String name = Thread.currentThread().getName();
                System.out.println(name + "正在卖:" + ticket--);
            }
        }
    }
}

class Demo {
    public static void main(String[] args) {
        //创建线程任务对象
        Ticket ticket = new Ticket();
        //创建三个窗口对象
        Thread t1 = new Thread(ticket, "窗口1");
        Thread t2 = new Thread(ticket, "窗口2");
        Thread t3 = new Thread(ticket, "窗口3");
        //同时卖票
        t1.start();
        t2.start();
        t3.start();
    }
}

在这里插入图片描述
这里我们可以看到,有三个窗口卖同一张票的情况,也有票买完了还在卖的情况,这里就出现了线程不安全的情况。

保证线程安全的三种方法

  • 同步代码块: synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。

格式:

synchronized(同步锁){
	需要同步操作的代码
}
public class Ticket implements Runnable {
    private int ticket = 100;
    Object lock = new Object();
    @Override
    public void run() {
        while (true) {
            synchronized (lock) {
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    String name = Thread.currentThread().getName();
                    System.out.println(name + "正在卖:" + ticket--);
                }
            }
        }
    }
}

class Demo {
    public static void main(String[] args) {
        //创建线程任务对象
        Ticket ticket = new Ticket();
        //创建三个窗口对象
        Thread t1 = new Thread(ticket, "窗口1");
        Thread t2 = new Thread(ticket, "窗口2");
        Thread t3 = new Thread(ticket, "窗口3");
        //同时卖票
        t1.start();
        t2.start();
        t3.start();
    }
}

在这里插入图片描述
这里我们使用 对象锁 的方式来实现同步锁,可以看到当我们有线程拿到了同步锁之后,其他线程不能对公共资源进行访问了,只能进行等候,直到当前线程释放掉同步锁。

  • 同步方法:使用synchronized修饰的方法

格式:

public synchronized void method(){
	可能会产生线程安全问题的代码
}
package demo5;

public class Ticket implements Runnable {
    private int ticket = 100;
    @Override
    public synchronized void run() {
        while (true) {
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    String name = Thread.currentThread().getName();
                    System.out.println(name + "正在卖:" + ticket--);
                }
        }
    }
}

class Demo {
    public static void main(String[] args) {
        //创建线程任务对象
        Ticket ticket = new Ticket();
        //创建三个窗口对象
        Thread t1 = new Thread(ticket, "窗口1");
        Thread t2 = new Thread(ticket, "窗口2");
        Thread t3 = new Thread(ticket, "窗口3");
        //同时卖票
        t1.start();
        t2.start();
        t3.start();
    }
}

在这里插入图片描述
这里的同步锁,对于非静态方法来说,同步锁就是this,对于静态方法来说同步锁就是当前方法使用所在类的字节码对象(类名.class)

  • 锁机制:Lock锁也称同步锁,加锁与释放锁方法化
    更加符合面向对象的思想
package demo5;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Ticket implements Runnable {
    private int ticket = 100;
    Lock lock = new ReentrantLock();

    @Override
    public synchronized void run() {
        while (true) {
            lock.lock();
            if (ticket > 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                String name = Thread.currentThread().getName();
                System.out.println(name + "正在卖:" + ticket--);
            }
            lock.unlock();
        }
    }
}

class Demo {
    public static void main(String[] args) {
        //创建线程任务对象
        Ticket ticket = new Ticket();
        //创建三个窗口对象
        Thread t1 = new Thread(ticket, "窗口1");
        Thread t2 = new Thread(ticket, "窗口2");
        Thread t3 = new Thread(ticket, "窗口3");
        //同时卖票
        t1.start();
        t2.start();
        t3.start();
    }
}

在这里插入图片描述
通过多态的思想创建了一个Lock对象,通过 lock.lock() 方法对存在线程安全代码块进行上锁,通过lock.unlock() 方法对已经执行完方法的线程进行解锁,保证了只存在唯一的线程来操作公共资源,实现了线程安全。

线程状态

线程状态导致状态发生条件
NEW(新建)线程刚被创建,但是并未启动。还没调用start方法。
Runnable(可运行)线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。
Blocked(锁阻塞)当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。
Waiting(无限等待)一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
Timed Waiting(计时等待)同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、Object.wait。
Teminated(被终止)因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。

线程间通信

多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。

  1. 等待唤醒机制

通过等待唤醒机制来保证线程间通信有效利用资源
在一个线程进行了规定操作后,就进入等待状态(wait()), 等待其他线程执行完他们的指定代码过后 再将其唤醒(notify());在有多个线程进行等待时, 如果需要,可以使用 notifyAll()来唤醒所有的等待线程。

  • wait:线程不再活动,不再参与调度。
  • notify:则选取所通知对象的 wait set 中的一个线程释放;
  • notifyAll:则释放所通知对象的 wait set 上的全部线程。

线程池

是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
好处:

  • 降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
  • 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
  • 提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

代码示例:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("我要一个教练");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("教练来了: " + Thread.currentThread().getName());
        System.out.println("教我游泳,交完后,教练回到了游泳池");
    }
}


class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(5);
        MyRunnable r = new MyRunnable();
        // 从线程池中获取线程对象,然后调用MyRunnable中的run()
        service.submit(r);
        // 再获取个线程对象,调用MyRunnable中的run()
        service.submit(r);
        service.submit(r);
        service.submit(r);
        service.submit(r);
        // 关闭线程池
        //service.shutdown();
    }
}

在这里插入图片描述
这里我们首先通过java.util.concurrent.Executors类中的静态方法 newFixedThreadPool 创建了线程池对象,并且指定了对应的线程数,通过 ExecutorService对象调用 Future<?> submit(Runnable task) 方法使用了线程池中的线程对象。

注意:
submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中。
欢迎java热爱者了解文章,作者将会持续更新中,期待各位友友的关注和收藏。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1884777.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

物联网的技术和应用有哪些?

随着科技的飞速发展&#xff0c;物联网已经成为连接世界的重要纽带&#xff0c;塑造着我们未来的生活。我们一起深入探索物联网的前沿技术和前瞻性应用&#xff0c;一窥未来的可能性。 获取物联网解决方案&#xff0c;YesPMP平台一站式物联网开发服务。 提示&#xff1a;智慧家…

【计算机网络期末复习】例题汇总(一)

重点例题选择填空简答题与传输媒体的接口的特性 重点 计算机网络的性能指标计算机网络体系结构 例题 选择 填空 交换机内部各个端口之间是用【总线电路】连接的&#xff0c;集线器各端口之间是【存储转发】的 交换机依据【依据帧的目的地址在路由表中查找匹配 】&#xff0c…

基于协同过滤的航空票务推荐系统的设计与实现(飞机票推荐系统)

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

OFDM的缺点与关键技术

子载波间干扰英文简写ICI&#xff0c;ICI可能由各种原因引起 在多径信道中&#xff0c;CP小于最大附加时延时收发系统载波频率偏差和采样偏差收发系统相对移动&#xff0c;存在多普勒频移 ICI是制约OFDM系统性能的主要重要因素之一 对频率偏差敏感----->同步技术&#xff0…

【C++】——【 STL简介】——【详细讲解】

目录 ​编辑 1. 什么是STL 2. STL的版本 3. STL的六大组件 1.容器(Container)&#xff1a; 2.算法(Algorithm)&#xff1a; 3.迭代器(Iterator)&#xff1a; 4.函数(Function)&#xff1a; 5.适配器(Adapter)&#xff1a; 6.分配器(Allocator)&#xff1a; 4. STL的…

振弦采集仪的工程安全监测实践与案例分析

振弦采集仪的工程安全监测实践与案例分析 振弦采集仪是一种常用的工程安全监测仪器&#xff0c;通过测量被监测结构的振动频率与振型&#xff0c;可以实时监测结构的安全状况。本文将结合实践经验和案例分析&#xff0c;探讨振弦采集仪在工程安全监测中的应用。 一&#xff0c…

前端Bug 修复手册

1.前端长整数精度丢失问题 &#xff08;1&#xff09;问题 在前后端联调时&#xff0c;发现后端有一个接口返回的值和前端页面上展示的值不一致。 后端Java实现的接口如下&#xff0c;返回一个json格式的大整数 123456789123456789&#xff1a; 但是前端请求这个接口后&…

firewalld(5)--direct

简介 direct 是 firewalld 服务的一个功能,它允许用户以更直接的方式配置防火墙规则,绕过通常的 firewalld 区域(zone)和服务的抽象层。然而,这个功能已经被弃用(deprecated),并将在未来的版本中移除。 弃用原因:直接配置可能导致与 firewalld 的其他功能(如区域和…

万界星空科技铜管加工行业MES系统解决方案

一、行业背景与挑战 随着铜管加工行业的快速发展&#xff0c;传统的管理模式已难以满足日益增长的生产需求。为满足市场的高效率、高质量、低成本要求&#xff0c;企业急需一套智能化的管理系统来提升生产效率、优化资源配置和确保产品质量。因此&#xff0c;我们针对铜管加工行…

波司登:2023/24财年业绩表现亮眼,用“新”提质推动高质量发展

2023年是全面贯彻党的二十大精神的开局之年&#xff0c;尽管国际国内经贸环境错综复杂&#xff0c;但中国经济回升向好、长期向好的基本趋势没有改变&#xff0c;新质生产力激活发展新动能&#xff0c;文化自信自强赋能国货品牌乘势而上&#xff0c;内需市场回暖向好&#xff0…

测试驱动开发(TDD)方法详解

目录 前言1. 什么是测试驱动开发1.1 TDD的基本原则1.2 TDD的优势 2. 测试驱动开发的流程2.1 编写测试2.2 运行测试2.3 编写实现代码2.4 重构代码 3. 常用工具和框架3.1 单元测试框架3.2 Mock框架3.3 集成工具 4. TDD在实际项目中的应用4.1 应用场景4.2 面临的挑战4.3 最佳实践 …

单一WiFi的RSSI指纹和行人航位推算(PDR)方法

问题背景 室内定位技术在现代生活中具有重要意义,应用广泛,如导航、物流跟踪、紧急救援等。然而,现有的室内定位技术在城市高密度平层环境中面临诸多挑战,主要包括: 多路径效应:信号在墙壁、家具等障碍物之间反射,导致信号路径复杂化。信号衰减与干扰:建筑物内的结构会…

高职人工智能专业实训课之“生成对抗网络(GAN)”

一、前言 生成对抗网络&#xff08;GAN&#xff09;作为人工智能领域的一项重要技术&#xff0c;已经在图像生成、风格迁移、数据增强等多个领域展现出巨大的潜力和应用价值。为了满足高职院校对GAN专业实训课程的需求&#xff0c;唯众人工智能教学实训凭借其前沿的教育技术平…

名企面试必问30题(十三)——项目中遇到最大的困难和挑战

1.思路 从面试官的视角来看&#xff0c;您所遇到困难的大小能够直接反映出您水平的层次。 其一&#xff0c;如果您遇到的最大挑战是诸如保障功能上线、需求分析这类基础且偏执行的产品工作&#xff0c;那么面试官或许会给您的能力水平贴上执行者的标签。 其二&#xff0c;如果…

内容营销专家刘鑫炜:自媒体时代,网站真的落伍了吗?

自媒体时代&#xff0c;虽然自媒体平台如雨后春笋般涌现&#xff0c;为内容创作者提供了更为广阔的空间&#xff0c;但并不意味着网站已经落伍。相反&#xff0c;网站仍然是信息传播、品牌建设、电子商务等多个领域的重要载体&#xff0c;具有不可替代的作用。 网站在信息传播方…

测试:自动化测试

文章目录 概念web测试selenium的工作原理selenium的常用函数元素定位操作测试对象窗口弹窗 概念 自动化的类型很多&#xff0c;那选择哪一种测试的收益是比较好的呢&#xff1f; 这里引出一个自动化测试金字塔 理想的自动化测试金字塔表达了自动化测试的理想状态&#xff0c;…

010-GeoGebra基础篇-动态验证三角形外接圆的圆心是否可以位于三角形的外部

接下来我们将进行一些稍微高级一点操作&#xff0c;一边学习新东西的同时&#xff0c;也开始对数学、物理等内容的研究。 目录 一、项目截图二、涉及内容三、问题设置1. 问题提出2. 验证方案 三、做图步骤1. 绘制定点A、B&#xff1b;2. 绘制动点C&#xff1b;&#xff08;1&am…

mysql-5.6.26-winx64免安装版本

mysql为什么要使用免安装 MySQL 提供免安装版本主要有以下几个原因和优势&#xff1a; 便捷性&#xff1a;用户无需经历安装过程&#xff0c;直接解压即可使用。这对于需要快速部署环境或者在不支持安装权限的系统上使用MySQL非常有用。灵活性&#xff1a;免安装版允许用户将…

马斯克公布xAI Grok-2大语言模型将于8月推出;GPT-5仍需时日

&#x1f989; AI新闻 &#x1f680; 马斯克公布xAI Grok-2大语言模型将于8月推出 摘要&#xff1a;7月1日&#xff0c;马斯克在X平台宣布&#xff0c;其人工智能初创公司xAI的新大语言模型Grok-2将于8月推出。此前&#xff0c;xAI已发布了Grok-1.5和Grok-1.5 Vision模型。马…

C#/WPF 自制白板工具

随着电子屏幕技术的发展&#xff0c;普通的黑板已不再适用现在的教学和演示环境&#xff0c;电子白板应运而生。本篇使用WPF开发了一个电子白板工具&#xff0c;功能丰富&#xff0c;非常使用日常免费使用&#xff0c;或者进行再次开发。 示例代码如下&#xff1a; Stack<St…