Java基础-Java多线程机制

news2024/9/22 15:45:26

(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹)

目录

一、引言

二、多线程的基本概念

1. 线程与进程

2. 多线程与并发

3. 多线程的优势

三、Java多线程的实现方式

1. 继承Thread类

2. 实现Runnable接口

3. 实现Callable接口与Future

四、线程的生命周期

线程状态转换示例

五、同步与互斥

1. 同步的必要性

2. Java中的同步机制

synchronized关键字

Lock接口

volatile关键字

3. 同步的代价

六、线程间通信的深入探索

1. 等待/通知机制

2. Java并发包中的线程间通信

3. 其他并发工具


一、引言

在Java编程中,多线程机制是并发编程的核心部分,它允许程序同时执行多个任务,从而显著提高程序的执行效率和响应速度。多线程不仅在现代应用程序中广泛应用,如服务器后端处理、图形用户界面(GUI)响应、实时数据处理等场景,也是深入理解Java并发包(java.util.concurrent)和其他高级并发工具的基础。本文将从多线程的基本概念、实现方式、生命周期、同步与互斥、线程间通信、线程池等多个方面,对Java多线程机制进行深度解析,并通过代码示例进行具体说明。

二、多线程的基本概念

1. 线程与进程

  • 进程(Process):是系统进行资源分配和调度的基本单位,拥有独立的内存空间和系统资源。每个进程都包含至少一个线程,即主线程。
  • 线程(Thread):是进程中的一个执行实体,也是CPU调度和分派的基本单位。线程共享所属进程的内存空间和系统资源,但每个线程都有独立的执行栈和程序计数器。

2. 多线程与并发

  • 多线程:指在一个程序中同时执行多个线程,每个线程都有自己的执行路径和生命周期。
  • 并发:指在同一时间段内,多个任务交替执行,虽然每个时刻只有一个任务在CPU上执行,但由于CPU切换线程的速度非常快,用户感觉上多个任务在同时执行。

3. 多线程的优势

  • 提高系统响应性能:将耗时的操作放在后台线程中处理,保持主线程的流畅和响应。
  • 提高计算机资源利用率:利用多核处理器的优势,并行执行多个任务。
  • 实现异步编程:主线程可以在等待后台线程完成任务的同时,继续执行其他任务。

三、Java多线程的实现方式

1. 继承Thread类

通过继承java.lang.Thread类并重写其run方法来实现多线程。这种方式简单直接,但存在Java单继承的限制。

public class MyThread extends Thread {  
    @Override  
    public void run() {  
        System.out.println(Thread.currentThread().getName() + " is running.");  
        // 执行具体任务  
    }  
  
    public static void main(String[] args) {  
        MyThread t1 = new MyThread();  
        MyThread t2 = new MyThread();  
        t1.start(); // 启动线程  
        t2.start(); // 启动线程  
    }  
}

2. 实现Runnable接口

通过实现java.lang.Runnable接口的run方法来创建线程。这种方式更为灵活,因为一个类可以实现多个接口,同时也可以通过Thread类的构造器将Runnable实例传递给线程。

public class MyRunnable implements Runnable {  
    @Override  
    public void run() {  
        System.out.println(Thread.currentThread().getName() + " is running.");  
        // 执行具体任务  
    }  
  
    public static void main(String[] args) {  
        Thread t1 = new Thread(new MyRunnable());  
        Thread t2 = new Thread(new MyRunnable());  
        t1.start();  
        t2.start();  
    }  
}

3. 实现Callable接口与Future

Callable接口类似于Runnable,但它可以返回一个结果,并且可以抛出异常。Callable通常与Future一起使用,Future用于表示异步计算的结果。

import java.util.concurrent.*;  
  
public class MyCallable implements Callable<Integer> {  
    @Override  
    public Integer call() throws Exception {  
        // 模拟耗时操作  
        Thread.sleep(1000);  
        return 123;  
    }  
  
    public static void main(String[] args) throws ExecutionException, InterruptedException {  
        ExecutorService executor = Executors.newFixedThreadPool(2);  
        Future<Integer> future = executor.submit(new MyCallable());  
        System.out.println("Waiting for result...");  
        Integer result = future.get(); // 阻塞等待结果  
        System.out.println("Result: " + result);  
        executor.shutdown();  
    }  
}

四、线程的生命周期

线程的生命周期包括新建状态、就绪状态、运行状态、阻塞状态和死亡状态。

  • 新建状态:线程被创建但尚未启动。
  • 就绪状态:线程已准备好执行,但尚未获得CPU时间片。
  • 运行状态:线程获得CPU时间片,正在执行。
  • 阻塞状态:线程由于某种原因(如等待IO操作完成、等待锁资源等)暂停执行。
  • 死亡状态:线程执行完毕或被强制终止,不再执行任何操作。

线程状态转换示例

线程的状态转换是线程执行过程中的自然流程。以下是一个简化的示例,用于说明线程状态之间的转换:

public class ThreadLifecycleExample {  
    static class MyThread extends Thread {  
        @Override  
        public void run() {  
            System.out.println(Thread.currentThread().getName() + " is in RUNNABLE state.");  
            synchronized (this) {  
                try {  
                    wait(); // 进入WAITING状态  
                } catch (InterruptedException e) {  
                    Thread.currentThread().interrupt(); // 恢复中断状态  
                }  
            }  
            System.out.println(Thread.currentThread().getName() + " resumes and terminates.");  
        }  
    }  
  
    public static void main(String[] args) throws InterruptedException {  
        MyThread t = new MyThread();  
        t.start(); // t进入RUNNABLE状态  
  
        // 假设主线程执行了一些操作后,决定唤醒t  
        Thread.sleep(1000); // 模拟耗时操作  
        synchronized (t) {  
            t.notify(); // 唤醒t,使其从WAITING状态进入RUNNABLE状态  
        }  
  
        // t最终会执行完毕,进入TERMINATED状态  
    }  
}  
  
// 注意:上述代码中的wait()和notify()调用必须放在同步块中,否则将抛出IllegalMonitorStateException。  
// 此外,由于wait()会释放锁,而notify()不会立即让线程进入RUNNABLE状态(需要CPU调度),  
// 因此实际输出可能因线程调度和JVM实现而有所不同。

 在实际应用中,线程的状态转换远比上述示例复杂,特别是在多线程并发环境下,线程的调度和执行顺序往往难以预测。

五、同步与互斥

1. 同步的必要性

在多线程环境下,多个线程可能会同时访问共享资源(如内存中的变量、文件等),这可能导致数据不一致、脏读、脏写等问题。为了确保数据的一致性和完整性,需要对访问共享资源的操作进行同步控制。

2. Java中的同步机制

Java提供了多种同步机制,包括synchronized关键字、Lock接口及其实现(如ReentrantLock)、volatile关键字等。

synchronized关键字

  • 同步方法:在方法声明中加上synchronized关键字,该方法在同一时刻只能被一个线程执行。
  • 同步代码块:使用synchronized(Object lock) { ... }语法,对特定代码块进行同步,其中lock是锁对象。
public class Counter {  
    private int count = 0;  
  
    // 同步方法  
    public synchronized void increment() {  
        count++;  
    }  
  
    // 同步代码块  
    public void incrementWithBlock(Object lock) {  
        synchronized (lock) {  
            count++;  
        }  
    }  
}

Lock接口

Lock接口提供了比synchronized关键字更灵活的锁定机制,它允许显式地获取和释放锁,以及尝试非阻塞地获取锁。

import java.util.concurrent.locks.Lock;  
import java.util.concurrent.locks.ReentrantLock;  
  
public class CounterWithLock {  
    private int count = 0;  
    private final Lock lock = new ReentrantLock();  
  
    public void increment() {  
        lock.lock(); // 显式获取锁  
        try {  
            count++;  
        } finally {  
            lock.unlock(); // 显式释放锁  
        }  
    }  
}

volatile关键字

volatile关键字用于确保变量的可见性,即当一个线程修改了被volatile修饰的变量的值时,这个新值对其他线程是立即可见的。但volatile不能保证原子性,也不具备互斥性。

public class VolatileExample {  
    private volatile boolean flag = false;  
  
    public void setFlag(boolean flag) {  
        this.flag = flag;  
    }  
  
    public boolean getFlag() {  
        return flag;  
    }  
}

3. 同步的代价

同步虽然能够解决多线程并发带来的问题,但它也引入了额外的开销,如线程等待锁的时间、上下文切换的成本等。因此,在设计多线程程序时,应合理使用同步机制,避免过度同步导致的性能问题。

六、线程间通信的深入探索

在Java中,线程间通信主要依赖于共享内存和相应的同步机制。通过共享内存,线程可以访问和修改同一份数据,而同步机制则确保了在多线程环境下对这些数据的访问是安全且有序的。

1. 等待/通知机制

Java中的wait()notify()/notifyAll()方法是实现线程间通信的经典方式。这些方法是Object类的一部分,因此任何对象都可以作为锁来使用这些机制。

  • wait():使当前线程等待,直到另一个线程调用此对象的notify()方法或notifyAll()方法。调用wait()方法时,当前线程必须持有该对象的锁。调用后,当前线程会释放锁并进入等待状态,直到被唤醒。
  • notify():唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的。
  • notifyAll():唤醒在此对象监视器上等待的所有线程。

使用wait()notify()/notifyAll()时,必须注意以下几点:

  • 必须在同步方法或同步代码块中调用这些方法,因为它们依赖于对象锁。
  • 调用wait()的线程会释放锁,并在等待期间无法继续执行。
  • 调用notify()notifyAll()的线程不会立即释放锁,直到它退出同步方法或同步代码块。
  • wait()notify()notifyAll()在调用时必须处理InterruptedException异常。

2. Java并发包中的线程间通信

除了wait()notify()/notifyAll()方法外,Java并发包(java.util.concurrent)还提供了更高级的线程间通信机制,如BlockingQueueCountDownLatchCyclicBarrierSemaphore等。

  • BlockingQueue:支持两个附加操作的队列。这两个附加操作是:在元素从队列中取出时等待队列变为非空,以及在元素添加到队列中时等待队列中有可用空间。BlockingQueue接口是Java并发包中用于生产者-消费者问题的一种重要工具,它提供了一系列线程安全的队列操作。

BlockingQueue的实现包括ArrayBlockingQueueLinkedBlockingQueuePriorityBlockingQueue等。这些实现各有特点,比如ArrayBlockingQueue是一个由数组支持的有界阻塞队列,LinkedBlockingQueue是一个由链表结构组成的有界(但默认大小为Integer.MAX_VALUE)或无界阻塞队列,而PriorityBlockingQueue则是一个支持优先级排序的无界阻塞队列。

3. 其他并发工具

  • CountDownLatch:一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
  • CyclicBarrier:一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点(common barrier point)。在涉及固定大小的线程组时,这些线程必须互相等待,直到所有线程都到达该屏障点,然后从屏障点继续执行。
  • Semaphore:一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个acquire(),然后再获取该许可。每个release()添加一个许可,从而可能释放一个正在acquire()中阻塞的线程。

这些工具各有用途,在解决复杂的并发问题时非常有用。例如,CountDownLatch可以用于等待一组任务的完成,CyclicBarrier可以用于让一组线程在某个点互相等待然后共同继续执行,而Semaphore则可以用于控制对共享资源的访问。

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

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

相关文章

GUL图形化界面操作(上部)

目录 AWT Frame对象&#xff1a;创建窗口 ​编辑 该对象相关的属性 设置窗口大小&#xff08;对象.setsize(int,int)&#xff09; 让窗口可以显现出来&#xff08;对象.setVisible(bool)&#xff09; 设置窗口标题&#xff08;对象.setTitle(String)&#xff09; 设置窗…

MySQL学习(14):索引使用规则

1. 索引语法 #创建索引 create [unique|fulltext] index 索引名 on 表名 (字段名,...); #字段可以有多个 #如果不加unique或fulltext&#xff0c;那么将创建一个普通索引 #在InnoDB引擎小&#xff0c;创建的索引默认是Btree结构#查看索引 show index from 表名;#删除索引 drop…

折叠群控FPV穿越机技术详解

折叠群控FPV穿越机技术是一个结合了多项高端技术和飞行理念的创新系统。以下是该技术的详细解析&#xff1a; 1. 技术概述&#xff1a; - FPV穿越机&#xff1a;FPV&#xff0c;全称First Person View&#xff0c;即“第一人称视角”&#xff0c;是指通过无人机上搭载的摄像头&…

Python+Pytest+Allure+Yaml+Pymysql+Jenkins+GitLab运行原理

PythonPytestAllureYamlPymysqlJenkinsGitLab运行原理逻辑及调用关系 GitLab代码仓&#xff1a; Jenkins工作空间&#xff1a; 代码&#xff1a; 测试报告展示&#xff1a;

医疗器械上市欧美,需要什么样的网络安全相关申报文件?

医疗器械在欧美上市时&#xff0c;需要提交的网络安全相关申报文件主要包括以下几个方面&#xff0c;这些要求基于欧美地区的法律法规和监管机构的指导文件。 一、美国FDA要求 1. 网络安全管理计划 内容&#xff1a;制造商需要提交一份网络安全管理计划&#xff0c;该计划应包含…

blender顶点乱飞的问题解决

初学blender&#xff0c;编辑模式下移动某些顶点&#xff0c;不管是移动还是滑动都会出现定点乱飞的问题&#xff0c;后来才发现是开了吸附工具的原因&#xff01;&#xff01;&#xff01;&#xff01; 像下面这样&#xff0c;其实我只是在Z轴上移动&#xff0c;但是就跑的很…

经纬恒润与奇瑞汽车签订新能源项目重点供应商合作协议,共同开启合作新篇章

近日&#xff0c;2024年国家级芜湖经开区汽车零部件生态大会成功举行&#xff0c;经纬恒润受邀出席&#xff0c;与行业各伙伴齐聚经开区&#xff0c;同绘发展蓝图&#xff0c;助力经开区汽车产业高质量发展。会上&#xff0c;经纬恒润与奇瑞汽车签署合作协议&#xff0c;成为奇…

2-43 基于matlab的RBF网络自适应控制应用

基于matlab的RBF网络自适应控制应用&#xff0c;使用RBF网络逼近一级倒立摆模型中未知的非线性函数部分&#xff0c;即用RBF网络代替一级倒立摆模型中未知非线性函数部分&#xff0c;从而设计自适应控制&#xff0c;实现一级倒立摆的平衡控制。程序已调通&#xff0c;可直接运行…

卡夫卡(Kafka)框架详解:从背景到应用实践

卡夫卡&#xff08;Kafka&#xff09;框架详解&#xff1a;从背景到应用实践 引言 在大数据和分布式系统日益普及的今天&#xff0c;数据处理和消息传递成为了支撑复杂业务系统的关键基础设施。Apache Kafka&#xff0c;作为一个高性能的分布式消息队列系统&#xff0c;因其高…

qt 如何制作动态库插件

首先 首先第一点要确定我们的接口是固定的&#xff0c;也就是要确定 #ifndef RTSPPLUGIN_H #define RTSPPLUGIN_H #include "rtspplugin_global.h" typedef void (*func_callback)(uint8_t* data,int len,uint32_t ssrc,uint32_t ts,const char* ipfrom,uint16_t f…

C/C++樱花树代码

目录 写在前面 系列文章 C简介 完整代码 代码分析 写在后面 写在前面 C实现精美的樱花树&#xff0c;只需这100行代码&#xff01; 系列文章 序号目录直达链接1爱心代码https://want595.blog.csdn.net/article/details/1363606842李峋同款跳动的爱心https://want595.b…

深入理解TCP:互联网通信的基石

深入理解TCP&#xff1a;互联网通信的基石 引言TCP的普遍应用TCP连接的建立与维护三次握手&#xff08;Three-Way Handshake&#xff09;连接的可靠性与超时重传数据传输与流量控制连接的终止状态转换 TCP与UDP的对比TCP协议栈的层级结构应用层&#xff08;Application Layer&a…

24下软考初级-网络管理员100条知识点速记!

宝子们&#xff01;上半年软考已经结束一段时间了&#xff0c;准备备考下半年软考初级-网络管理员的小伙伴可以开始准备了&#xff0c;这里给大家整理了100条网管知识点汇总&#xff0c;涵盖全书90%重点&#xff0c;先把这个存下&#xff01;再慢慢看书&#xff0c;边看书边背这…

androidTest 与 Test详解

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。 &#x1f60a; 座右铭&#xff1a;不…

Server - 配置 Kubernetes 多任务流程 KubeFlow Pipeline 的开发教程

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/140281680 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 Kubern…

错误解决 error CS0117: ‘Buffer‘ does not contain a definition for ‘BlockCopy‘

Unity 2022.3.9f1 导入 Runtime OBJ Importer 后出现&#xff1a; error CS0117: ‘Buffer’ does not contain a definition for ‘BlockCopy’ 解决办法&#xff1a; 源代码&#xff1a; int DDS_HEADER_SIZE 128; byte[] dxtBytes new byte[ddsBytes.Length - DDS_HEAD…

深度学习系列一

激活函数 sigmod 梯度消失问题&#xff1a; sigmoid函数的导数在输入值较大或较小时接近于0。在反向传播过程中&#xff0c;这些小梯度会相乘&#xff0c;导致深层网络的梯度变得非常小。结果是&#xff0c;深层网络的参数几乎不会更新&#xff0c;训练变得非常困难。这就是为…

基于flask的天气数据可视化系统1.0

基于flask的天气数据可视化系统1.0 一、效果展示二、flask简介三、图表绘制四、前端页面编写五、完整代码一、效果展示 该flask项目相对简单入门,使用了flask框架、bootstrap前端技术,数据使用的是上一篇scrapy爬取城市天气数据中爬取到的数据。 二、flask简介 Flask是一个…

在linux中,如何搭建nacos2.4.0的版本,修改nacos密码

由于最近服务器经常被攻击&#xff0c;看了一下发现是nacos版本过低&#xff0c;导致被抓了肉鸡&#xff0c;导致服务器的网端被跑满&#xff0c;选择重新搭建nacos&#xff0c;进入服务器后&#xff0c;首先确定服务器是否安装java&#xff0c;先执行java -version&#xff0c…

如何应对SQL注入攻击?

引言 在现今的网络世界中&#xff0c;安全性已成为至关重要的话题。SQL注入&#xff08;SQL Injection&#xff09;是一种常见且危险的网络攻击方式&#xff0c;攻击者通过向SQL查询中插入恶意代码来操控数据库&#xff0c;从而获取敏感信息或破坏数据。了解SQL注入的各种类型…