好好学习,天天向上




本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航

前言

俗话说得好“一人拾柴火不旺,众人拾柴火焰高”,一个人的力量毕竟是有限的,想要把一件事情做好,免不了需要一帮人齐心协力。同样的道理,一个复杂程序里面不会只有一个线程在工作,必然是很多个线程在一起工作。那么,这篇文章作为Java并发学习系列的第一篇,就来聊一聊Java并发的基础知识:进程和线程。

进程和线程概念

进程

什么是进程呢?进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。简单的说进程就是一个正在运行的程序,打开任务管理器,可以看到有很多的进程。

比如写文章用的Typora,写程序用的IDEA等,这些都是进程。

线程

那么线程又是什么呢?线程是操作系统能够进行运算调度的最小单位。每个进程里面都包含了一个或多个线程,比如我正在运行的IDEA里面就包含了58个线程。

举个栗子:平时大家应该会用视频软件看视频吧,那么播放视频,下载视频,上传视频,弹幕功能等,这些都是一个个单独线程,这些线程之间可以互相协作,也可以互不干扰的运行。

线程和CPU之间的关系

线程作为操作系统的最小调度单位,同一时间只能运行在一个CPU核心上。现在的操作系统都是分时操作系统,一个线程不会一直占用一个CPU核心,而是等待操作系统的任务调度器分配CPU时间片给一个线程,这个线程才能运行,否则只能在一边等着。就像网吧一样,每台机子就相当于一个CPU核心,上网的人就是线程。每个人不管时间多少都是时间限制的,下机后就给另外的人上,没有机子的人就在一边等着别人下机。不过网吧上网时间是由你自行决定的,而线程的运行时间是操作系统分配的。不过一个CPU时间片特别短,所以你感觉所有的线程都是一起运行的,其实是存在线程切换的。

并行和并发

既然要学习Java并发,那么首先得知道并行和并发的概念吧。

并行

  • 并行(parallel)是同一时间动手做(doing)多件事情的能力

举个栗子:中午了,大家都在食堂打饭,现在有两个窗口,前面排了两队人,每个打饭阿姨同一时间只要应付一个同学,这就是并行

并发

  • 并发(concurrent)是同一时间应对(dealing with)多件事情的能力

还是打饭的例子,现在只有一个阿姨在打饭,但前面还是排了两队人,这样的话,阿姨一次要处理两个同学的打饭请求,这就是并发

Java线程

多线程的好处

前面介绍过,线程是操作系统进行调度的最小单位,一个线程同一时间只能运行在CPU的一个核心上,我们都知道,现在的CPU都是多核的。假如我们的程序只有一个线程,那么只会用到CPU的一个核心,那我们搞多核CPU不就浪费了吗,所以就需要多线程去提高程序的运行效率。打个比方,现在有个软件开发的任务,需要前端,后端,美工,但现在只有一个人身兼多职,其它几个人在边上喝茶,就相当于单线程运行,开发效率肯定不高。要是这几个人分工明确,齐心合作,开发效率自然就上去了。这就是多线程带来的好处。

创建和运行线程

1. 继承Thread ,重写run() 方法

第一种创建线程的方式是新建一个类继承自 Thread ,然后重写父类的 run() 方法,并在里面编写代码即可:

class MyThread extends Thread {

    @Override
public void run() {
System.out.println("创建了线程");
}
}

启动线程的方式很简单,只要创建MyThread类的实例,然后调用start()方法就行了:

new MyThread().start();

其实也可以简单一点,直接使用匿名类的方式,原理都是一样的:

new Thread() {
@Override
public void run() {
System.out.println("创建了线程");
}
}.start();

2. 实现Runnable接口

第一种方式的耦合性比较高,一般采用实现Runnable接口的方式去创建线程:

class MyThread implements Runnable {

    @Override
public void run() {
System.out.println("创建了线程");
}
}

启动线程的方式是创建Thread类的实例,接收一个Runnable参数,因为MyThread类实现了Runnable接口,所以把MyThread类的实例当作参数传入Thread类的构造方法,最后调用Thread类的start()方法就可以了。

new Thread(new MyThread()).start();

3. Callable+FutureTask

前面两种方式创建的线程都没有返回值,要想线程有返回值就可以使用第三种方式去创建线程:

FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return 12345;
}
});
new Thread(task).start();
  • 让一个类实现Callable接口,重写里面的call方法,泛型就是返回值的类型,可以是任意数据类型。这里使用了匿名类的写法。
  • 创建一个FutureTask类的实例,将Callable对象作为参数传入其构造方法。
  • new一个Thread类的实例,将FutureTask对象当作参数传入,调用其start()方法启动线程。

这时候线程就成功创建并启动了,那怎么获取返回值呢?直接调用FutureTask的get()方法就可以了。

task.get();

这样就可以获取到返回值了,不过这里有个需要注意的地方,当调用get()方法的时候,线程就会阻塞在这里,直到成功获取返回值。我们可以来写段代码测试一下是不是这样:

FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(2000);
return 12345;
}
});
new Thread(task).start();
long start = System.nanoTime();
task.get(); //线程会阻塞在这里
long end = System.nanoTime();
long time = (end-start)/1000000;
System.out.println("耗费的时间为:"+time + "毫秒");

运行一下,看看结果:



从结果中可以看出,get()方法确实会阻塞线程。

4. 线程池

最后一种方式就是借助线程池,这也是《阿里巴巴Java开发手册》中推荐使用的方式。



但是比较尴尬的是我现在水平不够,还不会线程池

Java并发学习(一):进程和线程的更多相关文章

  1. Java并发编程:进程和线程之由来

    Java多线程基础:进程和线程之由来 在前面,已经介绍了Java的基础知识,现在我们来讨论一点稍微难一点的问题:Java并发编程.当然,Java并发编程涉及到很多方面的内容,不是一朝一夕就能够融会贯通 ...

  2. Java并发编程:进程和线程之由来__进程让操作系统的并发性成为可能,而线程让进程的内部并发成为可能

    转载自海子:http://www.cnblogs.com/dolphin0520/p/3910667.html Java多线程基础:进程和线程之由来 在前面,已经介绍了Java的基础知识,现在我们来讨 ...

  3. java并发编程:进程和线程

    java并发编程涉及到很多内容,当然也包括多线程,再次补充点相关概念 原文地址:http://www.cnblogs.com/dolphin0520/p/3910667.html 一.操作系统中为什么 ...

  4. Java并发基础:进程和线程之由来

    转载自:http://www.cnblogs.com/dolphin0520/p/3910667.html 在前面,已经介绍了Java的基础知识,现在我们来讨论一点稍微难一点的问题:Java并发编程. ...

  5. Java并发编程 | 从进程、线程到并发问题实例解决

    计划写几篇文章讲述下Java并发编程,帮助一些初学者成体系的理解并发编程并实际使用,而不只是碎片化的了解一些Synchronized.ReentrantLock等技术点.在讲述的过程中,也想融入一些相 ...

  6. Java并发学习之中的一个——线程的创建

    本文是学习网络上的文章时的总结,感谢大家无私的分享. 1.与每一个Java语言中的元素一样,线程是对象.在Java中,我们有两种方式创建线程: a.通过直接继承thread类,然后覆盖run方法. b ...

  7. Java并发学习之十九——线程同步工具之Phaser

    本文是学习网络上的文章时的总结.感谢大家无私的分享. JDK 1.7 加入了一个新的工具Phaser.Phaser的在功能上与CountDownLatch有部分重合. 以下使用Phaser类来同步3个 ...

  8. java并发学习--第六章 线程之间的通信

    一.等待通知机制wait()与notify() 在线程中除了线程同步机制外,还有一个最重要的机制就是线程之间的协调任务.比如说最常见的生产者与消费者模式,很明显如果要实现这个模式,我们需要创建两个线程 ...

  9. java并发学习第五章--线程中的锁

    一.公平锁与非公平锁 线程所谓的公平,就是指的是线程是否按照锁的申请顺序来获取锁,如果是遵守顺序来获取,这就是个公平锁,反之为非公平锁. 非公平锁的优点在于吞吐量大,但是由于其不是遵循申请锁的顺序来获 ...

  10. java并发学习--第三章 线程安全问题

    线程的安全问题一直是我们在开发过程中重要关注的地方,出现线程安全问题的必须满足两个条件:存在着两个或者两个以上的线程:多个线程共享着共同的一个资源, 而且操作资源的代码有多句.接下来我们来根据JDK自 ...

随机推荐

  1. P2607[ZJOI2008] 骑士 题解

    题目 Z 国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬. 最近发生了一件可怕的事情,邪恶的 Y 国发动了一场针对 Z 国的侵略战争.战火绵延五 ...

  2. Rancher1.6 部署prometheus

    一.rancher基础配置 镜像:prom/prometheus:latest 映射端口:9090:9090 服务连接: blackbox-exporter cadvisor node-exporte ...

  3. PE文件格式详解(七)

    PE文件格式详解(七)   Ox00 前言 前面好几篇在讲输入表,今天要讲的是输出表和地址的是地址重定位.有了前面的基础,其实对于怎么找输出表地址重定位的表已经非常熟悉了.   0x01 输出表结构 ...

  4. python读取EXCEL是去掉空白行和表头全部重命名

    当读取进来的表格如图所示,转换成图2. import pandas as pd # header:指定作为列名的行,默认0,即取第一行的值为列名.数据为列名行以下的数据:若数据不含列名, # 则设定 ...

  5. 组件toast(类似于element-ui的message组件)的实现

    实现的toast组件可以通过this.$toast()调用 需要的知识: vue.extend(); new Vue().$mount(); //如果mount内没有要挂载的元素vue只会渲染元素而不 ...

  6. ffmpeg播放器实现详解 - 框架搭建

    ffplay是ffmpeg源码中一个自带的开源播放器实例,同时支持本地视频文件的播放以及在线流媒体播放,功能非常强大. FFplay: FFplay is a very simple and port ...

  7. Reface.AppStarter 基本示例

    Reface.AppStarter 向应用层提供以下几项 核心 功能 以模块化组织你的应用程序 自动注册组件至 IOC 容器 自动映射配置文件至配置类 在模块定义类中额外追加组件至 IOC 容器 在模 ...

  8. Ethical Hacking - POST EXPLOITATION(4)

    PIVOTING Use the hacked device as a pivot. Try to gain access to other devices in the network. Tool: ...

  9. webpack源码-依赖收集

    webpack源码-依赖收集 version:3.12.0 程序主要流程: 触发make钩子 Compilation.js 执行EntryOptionPlugin 中注册的make钩子 执行compi ...

  10. kubernetes系列(十) - 通过Ingress实现七层代理

    1. Ingress入门 1.1 Ingress简介 1.2 原理和组成部分 1.3 资料信息 2. Ingress部署的几种方式 2.1 前言 2.1 Deployment+LoadBalancer ...