并发编程——基础部分(篇一)
这几天在研究Java中的线程机制,结果越是看,反而感觉越是掌握的太少,变得多疑而又自信,因为Java线程机制教会我,“理论上是可靠的,实际是不准确的。”,一直向前走,越会发现精彩而又迷惑的地方,这就是Java中的线程机制这几天所带给我的一些东西,虽然只是研究了很小一部分,但是感觉也很有收获,所以在这里将自己的读书笔记或是可以称为收货记录下来。
凡事都可以问一个为什么,那么我们也可以在这里一路为什么下去。
-
为什么要使用并发?
当第一次多线程这个概念进入我们脑海的时候,我们没有问过为什么会存在这个,也没有想过,但是我们现在需要考虑这个问题,这是为什么呢?多线程的效果是什么,是可以同时让多个任务同时执行,那么我们自然可以想到,比起所有的任务顺序执行,同时执行多个任务会更节省时间。但是如果我的机器只有一个处理器,还会节省时间吗?答案是否定的,因为我们知道处理器一次只能执行一条指令,那么如果是多线程的话,为了达到效果,那么在处理器上肯定会存在线程之间的切换,那么这与所有的任务顺序执行相比,是变得慢了,而不是更快。但是为什么我们还选择多线程呢?因为存在阻塞!这才是问题的核心,对于我们使用的用户界面,如果不使用多线程,那么怎么可能事实响应用户的输入,而且又在做着处理别的事情的工作呢,因此这才是多线程出现比较重要的原因,而且如果我们企图通过在顺序执行中去实时检测用户输入,或是什么,那么我不敢想我们会将程序写的有多么复杂。
- 为什么是线程而不是进程?
实现并发的最直接的方式是在操作系统级别使用进程,进程是运行在自己的地址空间的自包容程序。多任务操作系统可以周期性地将CPU从一个进程切换到另一个进程,来实现同时运行多个程序。操作系统会将进程之间进行隔离,它们之间互不干涉,这使得进程编程相对容易一些。与此相反,Java所使用的这种并发系统会共享诸如内存和I/O这样的资源,因此编写多线程程序最基本的困难在于协调不同线程驱动的任务之间对这些资源的使用,以使得这些资源不会同时被多个任务访问。进程之间虽然隔离,是并发的理想模型,但是进程通常会有数量和开销的限制,以避免它们在不同的并发系统之间的可用性。Java中的线程机制是在由执行程序表示的单一进程中创建任务。
- 来一点线程的干货
Java的线程机制是抢占式的,因此我们永远不知道下一次执行的会是谁?这表示调度机制会周期性地中断线程,切换到另一个线程,从而为每个线程都提供时间片,使得每个线程都会分配到数量合理的时间去驱动任务。但是在协作式系统中,每个任务都会自动地放弃控制,这要求在程序语句中插入某种让步语句。协作式系统的优势是双重的:上下文切换的开销通常比抢占式系统要低廉许多,并且可以同时执行的线程数量在理论上是没有限制的。并发编程使我们可以将程序划分为多个分离的、独立运行的任务。通过使用多线程机制,这些独立任务中的每一个都将由执行线程来驱动。一个线程就是在进程中的一个单一的顺序控制流,因此单个进程可以拥有多个并发执行的任务,但是程序使得每个任务都好像有其自己的CPU一样,其底层机制是切分CPU时间。线程模型为编程带来了便利,简化了在单一程序中同时交织在一起的多个操作的处理。使用线程时,CPU将轮流给每个任务分配其占用时间。多任务和多线程往往是使用多处理器系统的最合理方式。
- 如何创建多任务程序?
1、实现Runnable接口
首先我们需要来描述任务,这可以由实现Runnable接口来提供,只需要实现Runnable接口,编写里面的run()方法将你的任务添加进去即可,然后显式将任务附着到一个线程上,就是将Runnable对象交给Thread构造器。然后启动Thread对象调用start()方法,这样就可以与其它线程“同时”执行了。如下:
- class MTask implements Runnable{
- /**
- * 实现接口里面的run()方法
- */
- public void run() {
- System.out.println("为人民服务!");
- }
- }
- public class MyTask {
- public static void main(String[] args){
- Thread thread = new Thread(new MTask());
- thread.start();
- }
- }
上面首先类MTask实现接口Runnable,在接口方法run()里面描述需要执行的任务(打印字符串),然后在下面的类中,创建了一个线程对象,将任务附着到这个线程上,调用线程启动的方法,就可以实现了。
2、继承Thread类
继承Thread,虽然我们可以通过继承Thread类,来实现并发编程,但是由于Java中的单根继承,因此当我们继承了Thread类时就不能继承其它的类,这有时会是一种限制。如下:
- public class MyTask extends Thread{
- public static void main(String[] args){
- MyTask mt = new MyTask();
- mt.start();
- }
- /**
- * 重写Thread中的run()方法
- */
- public void run(){
- System.out.println("为人民服务!");
- }
- }
如上,可以直接继承Thread类,在里面重写run()方法,来编写需要实现的任务(仍是打印字符串),然后创建该类的对象,执行。
我们可以发现,不管是实现接口Runnable还是继承Thread,最后启动都是调用start()方法,通过start()方法来启动线程的根本原因是,线程总是由操作系统来占有和管理,一个新的线程只能由操作系统来创建和启动,如果自己直接调用run()方法,那么这和其他方法有什么区别呢,还是在调用的线程中执行。
- 使用Executor
Java.util.concurrent包中的执行器(Executor)将为我们管理Thread对象。从而简化并发编程,Executor在客户端和任务执行之间提供了一个间接层;与客户端直接执行任务不同,这个中介对象将执行任务。ExecutorService对象是使用静态的Executor方法创建的。使用静态方法可以创建不同类型的Executor,有(下面仅列出三种)
CachedThreadPool(会在执行过程中创建与所需数量相同的线程,回收旧线程时停止创建新的线程);
FixedThreadPool(可以预先一次性执行代价高昂的线程分配);
SingleThreadExecutor(就像线程数量为1的FixedThreadPool,如果向它提交多任务,那么这些任务将排队,每个任务都会在下一个任务开始前结束,所有任务使用相同的线程,它提供了一种重要的并发保证,这会改变任务的加锁需求)
1、使用CachedThreadPool
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- class Task implements Runnable{
- /**
- * 编写需要执行的任务
- */
- public void run() {
- System.out.println("为人民服务!");
- }
- }
- public class MyTask extends Thread{
- public static void main(String[] args){
- ExecutorService ecs = Executors.newCachedThreadPool();
- ecs.execute(new Task());
- ecs.shutdown();
- }
- }
其中ExecutorService对象通过Executors类的静态方法获得,提供CachedThreadPool,然后我们只需要将创建的新任务交给ecs对象,调用execute()方法即可,那么我们不需要显式创建以及启动线程,因此这就好比我们需要运输东西,没有必要自己再造个车(Thread)来进行运输,只需要将运输任务交给Executor就好,来负责管理Thread。其中后面调用shutdown()方法是为了防止继续给ecs提交新的任务。FixedThreadPool和SingleThreadExecutor的使用和上面很类似。如下:
2、使用FixedThreadPool
使用FixedThreadPool预先一次性执行制定数目的线程分配,假如我们指定分配3个线程,提交多余3个的任务,那么只有3个线程在运行这些任务,示例如下:
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import java.util.concurrent.ThreadFactory;
- class Task implements Runnable{
- /**
- * 编写需要执行的任务
- */
- public void run() {
- System.out.println(Thread.currentThread().getName() + "为人民服务!");
- }
- }
- class M_ThreadFactory implements ThreadFactory{
- public Thread newThread(Runnable r) {
- Thread t = new Thread(r);
- System.out.println("创建了线程:" + t.getName());
- return t;
- }
- }
- public class MyTask extends Thread{
- public static void main(String[] args){
- ExecutorService ecs = Executors.newFixedThreadPool(3, new M_ThreadFactory());
- for(int i = 0; i < 10; i++){
- ecs.execute(new Task());
- }
- ecs.shutdown();
- }
- }
在上面的程序中,忽视其中的类M_ThreadFactory,那个只是用来为了方便证明Executor中只有指定个数个线程(在函数中指定的是3),看一下输出结果,就很明白,如下:
创建了线程:Thread-1
Thread-0为人民服务!
Thread-1为人民服务!
创建了线程:Thread-2
Thread-2为人民服务!
Thread-2为人民服务!
Thread-2为人民服务!
Thread-2为人民服务!
Thread-2为人民服务!
Thread-2为人民服务!
Thread-2为人民服务!
Thread-2为人民服务!
可以明显看到,里面只有Thread-0、Thread-1和Thread-2,这三个线程。
由于SingleThreadExecutor可以视为这个的指定线程数为1的特殊情况,因此这里不进行演示。
现在时间不是很早了。。。。。。写到这里打住,前面这些只是并发方面的一些比较基础的东西,后面还有很多。。。。。。嘿嘿
未完,待续!
相关推荐
【深入理解高并发编程-核心技术原理】是一本专注于讲解高并发编程核心概念和技术的书籍,由阿里P8级别的架构师及Mykit系列开源框架作者撰写。本书内容涵盖源码分析、基础案例、实战案例和面试相关知识,旨在帮助读者...
总之,《Netty案例集锦(并发编程篇)》是一份深入探讨Netty并发编程实践的资料,涵盖了Netty的核心组件、设计模式以及在并发环境下的性能优化策略。通过学习和实践这些案例,开发者能够掌握Netty在实际项目中的应用,...
在Java并发编程中,数据的封装与访问控制、线程安全性的考量、同步机制的使用是重要的基础概念和技巧。以下是从给出的文件内容中提取出的详细知识点: 1. 数据封装与访问控制:确保内部私有数据不被轻易访问,并且...
### Netty教程并发编程知识点详解 #### 一、Netty简介与并发模型基础 ##### 1.1 Netty概述 Netty是一个高性能、异步事件驱动的网络应用程序框架...希望本篇教程能够帮助大家在实际项目中顺利运用Netty进行并发编程。
本篇文章将深入探讨Go并发编程的基础,包括传统的同步方法、无缓冲通道、有缓冲通道以及它们在实际编程中的应用。我们将通过标签中提到的关键概念——channel、goroutine、mutex、atomic来展开讨论。 首先,我们要...
学习"JAVA程序员必读基础篇"的内容,将帮助初学者建立起坚实的Java编程基础,为进一步深入学习Java高级特性,如Spring框架、JDBC数据库访问、多线程编程等打下坚实的基础。通过实践和不断练习,你将能够熟练掌握这个...
1. **并发编程基础** - **并发**:并发是指在一个时间段内,多个线程交替执行,使得系统看起来像是同时处理多个任务。在单核CPU中,通过线程的切换实现并发;在多核CPU中,每个线程可以分配到一个独立的处理器核上...
"13-Java并发编程学习宝典.zip" 包含了一系列关于Java并发编程的学习资源,旨在帮助开发者掌握多线程编程的核心技术和最佳实践。以下是这些资源所涵盖的关键知识点: 1. **多线程基础** - "03 多线程开发如此简单—...
Java面试基础篇+并发篇,这是我总结出来非常全面的Java面试的基础篇和并发编程方面的面试题
Java并发编程与高并发解决方案笔记-基础篇Java并发编程与高并发解决方案笔记-基础篇Java并发编程与高并发解决方案笔记-基础篇Java并发编程与高并发解决方案笔记Java并发编程与高并发解决方案笔记-基础篇-基础篇Java...
详细的讲述了并发、高并发、CPU Cache、CPU多级缓存、CPU多级缓存 - 缓存一致性(MESI)、CPU多级缓存-乱序执行优化、Java内存模型(Java Memory Model,JMM)、并发的优势和风险...等等图文并茂详解
### Netty案例集锦(并发编程篇)有目录 #### 1. Netty案例集锦系列文章介绍 ##### 1.1 Netty的特点 Netty作为一款高性能的网络通信框架,其特点主要体现在以下几个方面: - **API封装简单**:Netty通过一系列...
1. **Java并发编程基础篇**: 这一部分主要介绍了Java并发编程的基础知识,包括线程的基本概念、创建与管理,以及并发编程中的一些核心概念。读者将了解到如何启动和停止线程,如何控制线程同步,以及如何处理线程...
在Java编程领域,并发编程是一项核心技能,尤其是在多核处理器和分布式系统中,它能显著提升应用程序的性能和效率。本篇文章将深入探讨Java并发编程的相关知识点,主要基于提供的两个文件——"Java并发编程实战...
本篇文章将围绕Java多线程与并发编程的核心概念和技术进行深入探讨,帮助读者建立系统的知识体系,并掌握相关的实践方法。 #### 背景介绍 随着互联网应用规模的不断扩大,现代应用面临着高并发请求、CPU密集型操作...
该书内容详尽,不仅深入讨论了并发编程的基础理论,还提供了大量实战技巧和案例分析,对于希望提高自己Java并发编程能力的程序员来说,是一本不可多得的参考书。 书中首先阐述了并发编程在现代计算机系统中的重要性...
### Java并发编程基础知识 1. **线程**: Java中的线程是程序执行的基本单位,每个线程都有自己的堆栈空间和执行上下文。 - 创建线程的方法有两种:继承`Thread`类或实现`Runnable`接口。 - `Thread`类提供了多种...
Perl是一种强大的脚本编程语言,尤其在文本处理和系统...《Perl网络编程基础篇》可能是这样一个起点,它会引导你进入Perl在网络世界中的奇妙之旅。通过阅读和练习其中的示例代码,你将能够掌握Perl网络编程的基本技能。
总的来说,"计算机网络编程基础篇"这个资源包可能涵盖了网络编程的基本概念、套接字编程、服务器和客户端模型、并发处理、错误处理、网络安全以及现代网络服务设计。学习这部分内容,对于理解和构建网络应用程序至关...
### 并发编程模型演进——协程篇 #### 一、并发编程概述 随着计算机硬件的发展和软件需求的增长,并发编程技术变得越来越重要。并发编程是指在一个程序中同时执行多个计算任务的能力,它可以显著提高程序的运行...