使用hadoop mapreduce分析mongodb数据

一、mongdb的安装和使用

1、官网下载mongodb-linux-x86_64-rhel70-3.2.9.tgz

使用hadoop mapreduce分析mongodb数据

2、解压 (可以配置一下环境变量)

3、启动服务端

./mongod –dbpath=/opt/local/mongodb/data –logpath=/opt/local/mongodb/logs –logappend –fork(后台启动)

第一种:不带auth认证的

使用hadoop mapreduce分析mongodb数据

第二种:需要带auth认证的(即需要用户名和密码的)

使用hadoop mapreduce分析mongodb数据

当指定用户名和密码在查看数据,发现就可以看得到了

使用hadoop mapreduce分析mongodb数据

4、启动客户端

./mongo

使用hadoop mapreduce分析mongodb数据

5、客户端shell命令

show dbs 显示mongodb中有哪些数据库

使用hadoop mapreduce分析mongodb数据

db 显示当前正在用的数据库

使用hadoop mapreduce分析mongodb数据

use db 你要使用的数据库名

使用hadoop mapreduce分析mongodb数据

(注:若database不存在,则会创建一个,此时若不做任何操作直接退出,则MongoDB会删除该数据库)

db.auth(username,password) username为用户名,password为密码 登陆你要使用的数据库

db.getCollectionNames() 查看当前数据库有哪些表

使用hadoop mapreduce分析mongodb数据

db.[collectionName].insert({…}) 给指定数据库添加文档记录

使用hadoop mapreduce分析mongodb数据

db.[collectionName].findOne() 查找文档的第一条数据

db.[collectionName].find() 查找文档的全部记录

使用hadoop mapreduce分析mongodb数据

db.[collection].update({查询条件},{$set:{更新内容}}) 更新一条文档记录

使用hadoop mapreduce分析mongodb数据

db.[collection].drop() 删除数据库中的集合

使用hadoop mapreduce分析mongodb数据

db.dropDatabase() 删除数据库

使用hadoop mapreduce分析mongodb数据

二、Mapreduce 分析mongodb的数据实例

1、编写mapreduce的代码前,需要另外添加两个jar包,还有需(jdk1.7以上)

使用hadoop mapreduce分析mongodb数据

2、需求介绍与实现

原数据:

使用hadoop mapreduce分析mongodb数据

结果数据:

使用hadoop mapreduce分析mongodb数据

代码编写:

Job:

使用hadoop mapreduce分析mongodb数据

Mapper:

使用hadoop mapreduce分析mongodb数据

Reduce:

使用hadoop mapreduce分析mongodb数据

最终的结果数据:

使用hadoop mapreduce分析mongodb数据

三、最后给大家推荐一个mongodb数据库的管理工具,挺好用的

TensorFlow实现神经网络入门篇

如果你一直关注数据科学/机器学习,你就不能错过深度学习和神经网络的热潮。互联网公司正在寻找这方面的人,而且从竞赛到开源项目,都有巨额奖金。

如果你对深度学习所提供的前景感到兴奋,但是还没有开始,在这里或许是你开始的第一步。

在这篇文章中,我将介绍TensorFlow。阅读本文后,你将能够理解神经网络的应用,并使用TensorFlow解决现实生活中的问题,本文中的代码是用Python编写的,Python最近的火爆也和深度学习有关。

1:何时使用神经网络?

有关神经网络和深度学习的更详细的解释,请看这里。其“更深”版本正在图像识别,语音和自然语言处理等诸多领域取得巨大突破。

现在的主要问题是何时使用神经网络?关于这点,你必须记住一些事情:

1.1:神经网络需要大量的信息数据来训练。将神经网络想象成一个孩子。它首先观察父母如何走路。然后它才会独立行走,并且每走一步,孩子都会学习如何执行特定的任务。如果你不让它走,它可能永远不会学习如何走路。你可以提供给孩子的“数据”越多,效果就越好。

1.2:当你有适当类型的神经网络来解决问题时。每个问题都有自己的难点。数据决定了你解决问题的方式。例如,如果问题是序列生成,递归神经网络更适合,而如果它是一个图像相关的问题,你可能会采取卷积神经网络。

1.3:硬件要求对于运行深度神经网络模型是至关重要的。神经网络很早以前就被“发现”了,但是近年来,神经网络一直在发光,这是因为计算能力的强大。如果你想用这些网络解决现实生活中的问题,准备购买一些高性能硬件吧!

2:如何解决神经网络问题?

神经网络是一种特殊类型的机器学习(ML)算法。因此,与每个ML算法一样,它遵循数据预处理,模型构建和模型评估等常规ML工作流程。我列出了一个如何处理神经网络问题的待办事项清单。

1.检查神经网络是否可以提升传统算法。

2.做一个调查,哪个神经网络架构最适合即将解决的问题。

3.通过你选择的语言/库来定义神经网络架构。

4.将数据转换为正确的格式,并将其分成批。

5.根据你的需要预处理数据。

6.增加数据以增加规模并制作更好的训练模型。

7.将数据批次送入神经网络。

8.训练和监测训练集和验证数据集的变化。

9.测试你的模型,并保存以备将来使用。

本文中,我将重点关注图像数据。让我们先了解一下,然后再研究TensorFlow。

图像大多排列为3D阵列,尺寸指的是高度,宽度和颜色通道。例如,如果你现在截取了你的电脑的屏幕截图,则会首先将其转换为3D数组,然后将其压缩为PNG或JPG文件格式。

虽然这些图像对于人来说是相当容易理解的,但计算机很难理解它们。这种现象被称为语义鸿沟。我们的大脑可以查看图像,并在几秒钟内了解完整的图片。另一方面,计算机将图像视为一组数字。

在早期,人们试图把图像分解成像“模板”这样的“可理解的”格式。例如,一张脸总是有一个特定的结构,这个结构在每个人身上都有所保留,比如眼睛的位置和鼻子,或我们的脸的形状。但是这种方法并不可行,因为当要识别的对象的数量增加时,“模板”就不会成立。

2012年,深度神经网络架构赢得了ImageNet的挑战,这是一个从自然场景中识别物体的重大挑战。

那么人们通常使用哪种库/语言来解决图像识别问题?一个最近的一项调查发现,最流行的深度学习库是Python提供的API,其次是Lua中,Java和Matlab的。最流行的库是:

Caffe

DeepLearning4j

TensorFlow

Theano

Torch

让我们来看看TensorFlow所提供的功能。什么是TensorFlow?

“TensorFlow是一个使用数据流图进行数值计算的开源软件库。图中的节点表示数学运算,而图边表示在它们之间传递的多维数据阵列(又称张量)。灵活的体系结构允许你使用单个API将计算部署到桌面、服务器或移动设备中的一个或多个CPU或GPU。“

TensorFlow实现神经网络入门篇

如果你之前曾经使用过numpy,那么了解TensorFlow将会是小菜一碟!numpy和TensorFlow之间的一个主要区别是TensorFlow遵循一个“懒惰”的编程范例。它首先建立所有要完成的操作图形,然后当一个“会话”被调用时,它再“运行”图形。构建一个计算图可以被认为是TensorFlow的主要成分。要了解更多关于计算图的数学构成,请阅读这篇文章。

TensorFlow不仅仅是一个强大的神经网络库。它可以让你在其上构建其他机器学习算法,如决策树或k最近邻。

使用TensorFlow的优点是:

1.它有一个直观的结构,因为顾名思义,它有一个“张量流”。 你可以很容易地看到图的每一个部分。

2.轻松地在CPU / GPU上进行分布式计算。

3.平台灵活性。你可以在任何地方运行模型,无论是在移动设备,服务器还是PC上。

3.典型的“张量流”

每个库都有自己的“实施细节”,即按照其编码模式编写的一种方法。例如,在执行scikit-learn时,首先创建所需算法的对象,然后在训练集上构建一个模型,并对测试集进行预测。例如:

# define hyperparamters of ML algorithmclf = svm.SVC(gamma=0.001, C=100.)# train clf.fit(X, y)# test clf.predict(X_test)

正如我刚才所说,TensorFlow遵循一个“懒惰”的方法。

在TensorFlow中运行程序的通常工作流程如下所示:

1.建立一个计算图。这可以是TensorFlow支持的任何数学操作。

2.初始化变量。

3.创建会话。

4.在会话中运行图形。

5.关闭会话。

接下来,让我们写一个小程序来添加两个数字!

# import tensorflowimport tensorflow as tf# build computational grapha = tf.placeholder(tf.int16)b = tf.placeholder(tf.int16)addition = tf.add(a, b)# initialize variablesinit = tf.initialize_all_variables()# create session and run the graphwith tf.Session() as sess: sess.run(init) print "Addition: %i" % sess.run(addition, feed_dict={a: 2, b: 3})# close sessionsess.close()

4.在TensorFlow中实现神经网络

注意:我们可以使用不同的神经网络体系结构来解决这个问题,但是为了简单起见,我们需要实现前馈多层感知器。

神经网络的常见的实现如下:

1.定义要编译的神经网络体系结构。

2.将数据传输到你的模型。

3.将数据首先分成批次,然后进行预处理。

4.然后将其加入神经网络进行训练。

5.显示特定的时间步数的准确度。

6.训练结束后保存模型以供将来使用。

7.在新数据上测试模型并检查其执行情况。

我们的问题是识别来自给定的28×28图像的数字。我们有一部分图像用于训练,剩下的则用于测试我们的模型。所以首先下载数据集,数据集包含数据集中所有图像的压缩文件:train.csv和test.csv。数据集中不提供任何附加功能,只是以“.png”格式的原始图像。

我们将使用TensorFlow来建立一个神经网络模型。所以你应该先在你的系统中安装TensorFlow。根据你的系统规格,请参阅官方安装指南进行安装。

我们将按照上述模板进行操作。用Python 2.7内核创建一个Jupyter笔记本,并按照下面的步骤。

导入所有必需的模块:

%pylab inlineimport osimport numpy as npimport pandas as pdfrom scipy.misc import imreadfrom sklearn.metrics import accuracy_scoreimport tensorflow as tf

设置初始值,以便我们可以控制模型的随机性:

# To stop potential randomnessseed = 128rng = np.random.RandomState(seed)

第一步是设置保管目录路径:

root_dir = os.path.abspath('../..')data_dir = os.path.join(root_dir, 'data')sub_dir = os.path.join(root_dir, 'sub')# check for existenceos.path.exists(root_dir)os.path.exists(data_dir)os.path.exists(sub_dir)

让我们看看数据集。这些格式为CSV格式,并且具有相应标签的文件名:

train = pd.read_csv(os.path.join(data_dir, 'Train', 'train.csv'))test = pd.read_csv(os.path.join(data_dir, 'Test.csv'))sample_submission = pd.read_csv(os.path.join(data_dir, 'Sample_Submission.csv'))train.head()

TensorFlow实现神经网络入门篇

让我们看看我们的数据是什么样的!

img_name = rng.choice(train.filename)filepath = os.path.join(data_dir, 'Train', 'Images', 'train', img_name)img = imread(filepath, flatten=True)pylab.imshow(img, cmap='gray')pylab.axis('off')pylab.show()

TensorFlow实现神经网络入门篇

上面的图像表示为numpy数组,如下所示:

TensorFlow实现神经网络入门篇

为了更简单的数据处理,让我们将所有的图像存储为numpy数组:

TensorFlow实现神经网络入门篇

由于这是一个典型的ML问题,为了测试我们模型的正确功能,我们创建了一个验证集。

split_size = int(train_x.shape[0]*0.7)train_x, val_x = train_x[:split_size], train_x[split_size:]train_y, val_y = train.label.values[:split_size], train.label.values[split_size:]

现在,我们定义一些辅助函数,我们稍后使用它:

TensorFlow实现神经网络入门篇

我们来定义我们的神经网络架构。我们定义了一个三层神经网络:输入,隐藏和输出。输入和输出中神经元的数量是固定的,因为输入的是28×28图像,输出的是10×1向量。我们隐藏层中有500个神经元。这个数字可以根据你的需要而有所不同。阅读文章以获得完整的代码,并深入了解它的工作原理。

hadoop高级工程师通过三年的努力编写的大数据开发快速核心点

Hadoop 多节点集群

由 johnnyhung 创建,youj 最后一次修改 2016-12-09

本章介绍了分布式环境中Hadoop多节点集群的设置。

由于整个集群无法演示,我们将使用三个系统(一个主节点和两个从节点)解释Hadoop集群环境;下面给出他们的IP地址。

  • Hadoop Master:192.168.1.15(hadoop-master)
  • Hadoop Slave:192.168.1.16(hadoop-slave-1)
  • Hadoop Slave:192.168.1.17(hadoop-slave-2)

按照以下步骤进行Hadoop多节点群集设置。

安装Java

Java是Hadoop的主要先决条件。首先,您应该使用“java -version”验证系统中是否存在java。 java版本命令的语法如下。

$ java -version

如果一切正常,它会给你以下输出。

java version "1.7.0_71" Java(TM) SE Runtime Environment (build 1.7.0_71-b13) Java HotSpot(TM) Client VM (build 25.0-b02, mixed mode)

如果系统中没有安装java,请按照给定的步骤安装java。

步骤1

通过访问以下链接下载java(JDK – X64.tar.gz)http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html

然后 jdk-7u71-linux-x64.tar.gz 将下载到您的系统。

第2步

通常你会在下载文件夹中找到下载的java文件。使用以下命令验证它并解压缩jdk-7u71-linux-x64.gz文件。

$ cd Downloads/$ lsjdk-7u71-Linux-x64.gz$ tar zxf jdk-7u71-Linux-x64.gz$ lsjdk1.7.0_71 jdk-7u71-Linux-x64.gz

第3步

要使java可用于所有用户,您必须将其移动到位置“/ usr / local /”。打开根目录,然后键入以下命令。

$ supassword:# mv jdk1.7.0_71 /usr/local/# exit

第4步

要设置PATH和JAVA_HOME变量,请将以下命令添加到〜/.bashrc文件。

export JAVA_HOME=/usr/local/jdk1.7.0_71export PATH=PATH:$JAVA_HOME/bin

现在如上所述从终端验证java -version命令。按照上述过程并在所有集群节点中安装java。

创建用户帐户

在主系统和从系统上创建系统用户帐户以使用Hadoop安装。

# useradd hadoop # passwd hadoop

映射节点

您必须在所有节点上的/ etc / folder中编辑hosts文件,指定每个系统的IP地址,后跟其主机名。

# vi /etc/hostsenter the following lines in the /etc/hosts file.192.168.1.109 hadoop-master 192.168.1.145 hadoop-slave-1 192.168.56.1 hadoop-slave-2

配置基于Key的登录

hadoop高级工程师通过三年的努力编写的大数据开发快速核心点

在每个节点中设置ssh,以便它们可以彼此通信,而不需要提示输入密码。

# su hadoop $ ssh-keygen -t rsa $ ssh-copy-id -i ~/.ssh/id_rsa.pub tutorialspoint@hadoop-master $ ssh-copy-id -i ~/.ssh/id_rsa.pub hadoop_tp1@hadoop-slave-1 $ ssh-copy-id -i ~/.ssh/id_rsa.pub hadoop_tp2@hadoop-slave-2 $ chmod 0600 ~/.ssh/authorized_keys $ exit

Hadoop安装

在主服务器中,使用以下命令下载并安装Hadoop。

# mkdir /opt/hadoop # cd /opt/hadoop/ # wget http://apache.mesi.com.ar/hadoop/common/hadoop-1.2.1/hadoop-1.2.0.tar.gz # tar -xzf hadoop-1.2.0.tar.gz # mv hadoop-1.2.0 hadoop# chown -R hadoop /opt/hadoop # cd /opt/hadoop/hadoop/

Hadoop配置

您必须通过进行以下更改来配置Hadoop服务器。

core-site.xml

打开core-site.xml文件并进行编辑,如下所示。

<configuration><property> <name>fs.default.name</name> <value>hdfs://hadoop-master:9000/</value> </property> <property> <name>dfs.permissions</name> <value>false</value> </property> </configuration>

hdfs-site.xml

hadoop高级工程师通过三年的努力编写的大数据开发快速核心点

打开hdfs-site.xml文件并进行编辑,如下所示。

<configuration><property> <name>dfs.data.dir</name> <value>/opt/hadoop/hadoop/dfs/name/data</value> <final>true</final> </property> <property> <name>dfs.name.dir</name> <value>/opt/hadoop/hadoop/dfs/name</value> <final>true</final> </property> <property> <name>dfs.replication</name> <value>1</value> </property> </configuration>

mapred-site.xml中

打开mapred-site.xml文件并进行编辑,如下所示。

<configuration><property> <name>mapred.job.tracker</name> <value>hadoop-master:9001</value> </property> </configuration>

hadoop-env.sh

打开hadoop-env.sh文件并编辑JAVA_HOME,HADOOP_CONF_DIR和HADOOP_OPTS,如下所示。

注意:根据系统配置设置JAVA_HOME。

export JAVA_HOME=/opt/jdk1.7.0_17 export HADOOP_OPTS=-Djava.net.preferIPv4Stack=true export HADOOP_CONF_DIR=/opt/hadoop/hadoop/conf

在从属服务器 Slave上安装Hadoop

按照给定的命令在所有从属服务器 Slave上安装Hadoop。

# su hadoop $ cd /opt/hadoop $ scp -r hadoop hadoop-slave-1:/opt/hadoop $ scp -r hadoop hadoop-slave-2:/opt/hadoop

在主服务器上配置Hadoop

打开主服务器并按照给定的命令配置它。

# su hadoop $ cd /opt/hadoop/hadoop

配置主节点

$ vi etc/hadoop/mastershadoop-master

配置从节点

$ vi etc/hadoop/slaveshadoop-slave-1 hadoop-slave-2

格式命名Hadoop Master上的节点

# su hadoop $ cd /opt/hadoop/hadoop $ bin/hadoop namenode –format
11/10/14 10:58:07 INFO namenode.NameNode: STARTUP_MSG: /************************************************************ STARTUP_MSG: Starting NameNode STARTUP_MSG: host = hadoop-master/192.168.1.109 STARTUP_MSG: args = [-format] STARTUP_MSG: version = 1.2.0 STARTUP_MSG: build = https://svn.apache.org/repos/asf/hadoop/common/branches/branch-1.2 -r 1479473; compiled by 'hortonfo' on Mon May 6 06:59:37 UTC 2013 STARTUP_MSG: java = 1.7.0_71 ************************************************************/ 11/10/14 10:58:08 INFO util.GSet: Computing capacity for map BlocksMap editlog=/opt/hadoop/hadoop/dfs/name/current/edits………………………………………………….………………………………………………….…………………………………………………. 11/10/14 10:58:08 INFO common.Storage: Storage directory /opt/hadoop/hadoop/dfs/name has been successfully formatted. 11/10/14 10:58:08 INFO namenode.NameNode: SHUTDOWN_MSG: /************************************************************ SHUTDOWN_MSG: Shutting down NameNode at hadoop-master/192.168.1.15 ************************************************************/

启动Hadoop服务

以下命令用于启动Hadoop-Master上的所有Hadoop服务。

$ cd $HADOOP_HOME/sbin$ start-all.sh

在Hadoop集群中添加新的DataNode

下面给出了将新节点添加到Hadoop集群时需要遵循的步骤。

网络配置

通过一些适当的网络配置向现有Hadoop集群添加新节点。假设以下网络配置。

对于新节点配置:

IP address : 192.168.1.103 netmask : 255.255.255.0hostname : slave3.in

添加用户和SSH访问

添加用户

在新节点上,通过使用以下命令将“hadoop”用户和Hadoop用户的密码添加到“hadoop123”或任何您想要的密码。

useradd hadooppasswd hadoop

设置从主机到新从属机的密码关联性低。

在主站上执行以下操作

mkdir -p $HOME/.ssh chmod 700 $HOME/.ssh ssh-keygen -t rsa -P '' -f $HOME/.ssh/id_rsa cat $HOME/.ssh/id_rsa.pub >> $HOME/.ssh/authorized_keys chmod 644 $HOME/.ssh/authorized_keysCopy the public key to new slave node in hadoop user $HOME directoryscp $HOME/.ssh/id_rsa.pub hadoop@192.168.1.103:/home/hadoop/

在从站上执行以下操作

登录到hadoop。如果没有,登录到hadoop用户。

su hadoop ssh -X hadoop@192.168.1.103

将公共密钥的内容复制到文件“$ HOME / .ssh / authorized_keys”中,然后通过执行以下命令更改其权限。

cd $HOMEmkdir -p $HOME/.ssh chmod 700 $HOME/.sshcat id_rsa.pub >>$HOME/.ssh/authorized_keys chmod 644 $HOME/.ssh/authorized_keys

检查主机的ssh登录。现在检查是否可以ssh到新节点没有主密码。

ssh hadoop@192.168.1.103 or hadoop@slave3

设置新节点的主机名

您可以在文件/etc /sysconfig /network中设置主机名

On new slave3 machineNETWORKING=yes HOSTNAME=slave3.in

要使更改生效,请重新启动计算机或运行hostname命令到具有相应主机名的新计算机(重新启动是一个好的选项)。

在slave3节点机器上:

主机名 slave3.in

使用以下行在集群的所有计算机上更新/etc/hosts:

192.168.1.102 slave3.in slave3

现在尝试用主机名ping机器,以检查它是否正在解析到IP。

在新节点机器上:

ping master.in

在新节点上启动DataNode

使用$HADOOP_HOME / bin / hadoop-daemon.sh script手动启动datanode守护程序。它将自动联系主节点(NameNode)并加入集群。我们还应该将新节点添加到主服务器中的conf / slaves文件。基于脚本的命令将识别新节点。

登录新节点

su hadoop or ssh -X hadoop@192.168.1.103

使用以下命令在新添加的从属节点上启动HDFS

./bin/hadoop-daemon.sh start datanode

在新节点上检查jps命令的输出。它看起来如下。

$ jps7141 DataNode10312 Jps

从Hadoop集群中删除DataNode

我们可以在运行时从群集中删除节点,而不会有任何数据丢失。 HDFS提供了一个停用功能,确保安全地执行删除节点。要使用它,请按照下面的步骤:

第1步:登录master

登录到安装了Hadoop的主机用户。

$ su hadoop

第2步:更改集群配置

必须在启动集群之前配置排除文件。将名为dfs.hosts.exclude的键添加到我们的$HADOOP_HOME/etc/hadoop/hdfs-site.xml文件中。与此键相关的值提供了NameNode本地文件系统上文件的完整路径,该文件包含不允许连接到HDFS的计算机列表。

例如,将这些行添加到etc/hadoop/hdfs-site.xml文件。

<property> <name>dfs.hosts.exclude</name> <value>/home/hadoop/hadoop-1.2.1/hdfs_exclude.txt</value> <description>DFS exclude</description> </property>

第3步:确定要停用的主机

要停用的每台计算机都应添加到由hdfs_exclude.txt标识的文件中,每行一个域名。这将阻止他们连接到NameNode。如果要删除DataNode2,“/home/hadoop/hadoop-1.2.1/hdfs_exclude.txt”文件的内容如下所示。

slave2.in

第4步:步骤4:强制配置重新加载

运行命令“$ HADOOP_HOME/bin/hadoop dfsadmin -refreshNodes”,不带引号。

$ $HADOOP_HOME/bin/hadoop dfsadmin -refreshNodes

这将强制NameNode重新读取其配置,包括新更新的“excludes”文件。它将在一段时间内停止节点,从而允许将每个节点的块的时间复制到被调度为保持活动的机器上。

在slave2.in上,检查jps命令输出。一段时间后,您将看到DataNode进程被自动关闭。

第5步:关闭节点

在停用过程完成后,停用的硬件可以安全关闭进行维护。运行report命令dfsadmin以检查停用的状态。以下命令将描述退役节点和连接到集群的节点的状态。

$ $HADOOP_HOME/bin/hadoop dfsadmin -report

第6步:编辑再次排除文件

一旦计算机已停用,它们可以从“excludees”文件中删除。再次运行“$ HADOOP_HOME / bin / hadoop dfsadmin -refreshNodes”会将excludees文件读回NameNode;允许DataNodes在维护完成后重新加入群集,或者在群集中再次需要额外的容量等。

特别注意:如果遵循上述过程,并且tasktracker进程仍在节点上运行,则需要关闭它。一种方法是断开机器,就像我们在上面的步骤。

tasktracker可以在任何时间点通过以下命令即时运行/关闭。Master将自动识别过程,并宣布为死亡。没有必要遵循相同的过程来删除tasktracker,因为它与DataNode相比并不重要。DataNode包含要安全删除的数据,而不会有任何数据丢失。

tasktracker可以在任何时间点通过以下命令即时运行/关闭。

$ $HADOOP_HOME/bin/hadoop-daemon.sh stop tasktracker $HADOOP_HOME/bin/hadoop-daemon.sh start tasktracker

自媒体视频剪辑、特效、字幕、录屏、下载、转码等系列工具推荐!

如今是短视频的红利期,谁都不想错过机会。今天就给大家推荐一波系列工具,让大家更好的进行短视频制作,多多创收。

一.短视频剪辑软件

1、小影/爱剪辑/会声会影:比较合适一些刚入门的新手,广告力度也比较大,随处可见,不好的是片头片尾会有自带广告。

自媒体视频剪辑、特效、字幕、录屏、下载、转码等系列工具推荐!

3、EDIUS/PR,专业软件,效果强大,但是可能需要花费一定的时间成本去系统性的学习。

自媒体视频剪辑、特效、字幕、录屏、下载、转码等系列工具推荐!

5.AE、专业特效软件,许多炫酷的视频特效以及片头片尾都是用AE做出来的,也需要一定的时间去学习。

自媒体视频剪辑、特效、字幕、录屏、下载、转码等系列工具推荐!

二、音频处理软件:

Audition,是比较专业的音频处理软件,提供音频混合、编辑、控制和效果处理功能

自媒体视频剪辑、特效、字幕、录屏、下载、转码等系列工具推荐!

Cool Edit Pro,可以用声音来“绘”制:音调、歌曲的一部分、声音、弦乐、颤音、噪音或是调整静音。

自媒体视频剪辑、特效、字幕、录屏、下载、转码等系列工具推荐!

三、字幕处置软件

记事本,好用的字幕软件

srt字幕格式如下:

自媒体视频剪辑、特效、字幕、录屏、下载、转码等系列工具推荐!

四.录屏软件

oCam软件、还有Bandicam,可录制游戏视频。

自媒体视频剪辑、特效、字幕、录屏、下载、转码等系列工具推荐!

五:视频转码软件

格式工厂,方便快捷,可支持多种方式转码

自媒体视频剪辑、特效、字幕、录屏、下载、转码等系列工具推荐!

暴风转码

自媒体视频剪辑、特效、字幕、录屏、下载、转码等系列工具推荐!

六、视频下载软件

易撰,视频下载工具可有效解析各自媒体平台视频链接,你只需要将视频链接添加到工具栏,即可下载短视频。

自媒体视频剪辑、特效、字幕、录屏、下载、转码等系列工具推荐!

图片来自易撰—自媒体数据库

95个做运营、自媒体的工具网站全都在这,收藏两个给自己!

对于互联网运营中的自媒体运营来说,我们需要很多的工具网站,涉及到运营知识学习、素材收集、行业资讯、数据分析、文章排版等等方面。对于能够快速获取我们所需要的内容是非常重要的,所以今天小编就在这里给大家分享一些常用的网站,并不是每个都适合自己,挑几个收藏下来慢慢看。

95个做运营、自媒体的工具网站全都在这,收藏两个给自己!

运营、营销、广告类型网站

运营、营销的知识不可少,推荐几个常用的,当然不止这些,在碎片化的时间当中可以看一些碎片化的知识,逐渐完善自己的知识体系,知道自己的不足。

95个做运营、自媒体的工具网站全都在这,收藏两个给自己!

资讯类网站

大家做自媒体的小伙伴不知道从哪里去看互联网行业的动态,下面分享几个小编常用的网站,上面都有一手的的互联网分析、实时动态方面的资讯,玩转互联网必备。

95个做运营、自媒体的工具网站全都在这,收藏两个给自己!

有趣小众的网站

一些有趣小众的网站也很有意思,在平时我们思路枯竭,寻找创意的时候不妨去上面找找灵感那也是不错的。

95个做运营、自媒体的工具网站全都在这,收藏两个给自己!

图片素材网站

做运营、自媒体的小伙伴们都有这样的体验,写文章1小时,找素材3小时,最全的图片素材网站都在下面了,选上一两个作为自己常用的网站,没事的时候看一看,看到喜欢的就下载下来分好类,到用的时候直接拿来用就行了,配图不用再纠结。

95个做运营、自媒体的工具网站全都在这,收藏两个给自己!

动态图片网站

有的运营同学喜欢在文章中配一些动态图片,让文章读起来更生动,那就少不了动态图片网站了,推荐几个给大家。

95个做运营、自媒体的工具网站全都在这,收藏两个给自己!

排版工具

对于运营微信公众号的小编可能对于排版工具一点也不陌生,好的排版能够不仅能够改善用户的阅读体验,同时还可以通过排版的逻辑性提高用户的读完率。

95个做运营、自媒体的工具网站全都在这,收藏两个给自己!

数据统计分析网站

做运营绝对少不了数据统计分析,平时我们在写文章的时候需要引用一些数据来源,下面这些网站可以帮到你,还可以帮助大家了解一些互联网的趋势,强烈建议学习,因为运营是需要数据作为支撑的,有的数据是内部的,但是宏观的数据能够让我们知道趋势和方向。

95个做运营、自媒体的工具网站全都在这,收藏两个给自己!

工具网站知识辅助的工具,是给我们提供思路、增长知识的,做互联网运营需要的知识体系非常庞大,需要不断积累,不断交流,最重要的也是要有恒心和坚持,希望运营路上的小伙伴关注与我交流,我们一起进步,我会不定期在这里分享我在运营过程中积累的干货方法!

利用Gitblit+Jenkins+Docker+SpringBoot实现项目自动发布

一、准备工作

1、Linux系统服务器(这里以centos为例)

2、到Gitblit官网下载对应的安装包,到Jenkins官网下载对应的安装包

3、自己的项目源代码

二、开始搭建

需要安装JDK1.8(配置安装略),百度即可。

1、gitblit上传至服务器后,命令行进入文件目录下,可以看到如下:

利用Gitblit+Jenkins+Docker+SpringBoot实现项目自动发布

Gitblit文件目录

在命令行启动Gitblit即可:./gitblit.sh

利用Gitblit+Jenkins+Docker+SpringBoot实现项目自动发布

启动Gitblit

服务启动可在浏览器查看: (默认端口为8443,可根据自己的需要进行修改)

利用Gitblit+Jenkins+Docker+SpringBoot实现项目自动发布

Gitblit部署成功

新建代码库,并上传项目源码。

利用Gitblit+Jenkins+Docker+SpringBoot实现项目自动发布

新建代码库

2、安装Jenkins,并启动

Jenkins官网有安装教程,可参考。

安装完成后访问:

利用Gitblit+Jenkins+Docker+SpringBoot实现项目自动发布

Jenkins操作界面

配置jenkins

利用Gitblit+Jenkins+Docker+SpringBoot实现项目自动发布

利用Gitblit+Jenkins+Docker+SpringBoot实现项目自动发布

jdk配置和git配置

利用Gitblit+Jenkins+Docker+SpringBoot实现项目自动发布

maven配置

新建jenkins Job ,选择构建自由风格的

利用Gitblit+Jenkins+Docker+SpringBoot实现项目自动发布

新建 job

利用Gitblit+Jenkins+Docker+SpringBoot实现项目自动发布

新建完成后配置job,需要配置git仓库和执行脚本,URL为刚才在gitblit里的代码仓库地址

利用Gitblit+Jenkins+Docker+SpringBoot实现项目自动发布

利用Gitblit+Jenkins+Docker+SpringBoot实现项目自动发布

构建脚本

可在空白处添加执行脚本,编译,发布命令。保存,退出。

3、安装Docker

在命令行执行:yum install docker即可。

需要用到tomcat容器,$ docker pull tomcat

4、立即构建

至此,基本的搭建和操作步骤基本就完成了。在jenkins中点击“立即构建”,然后构建成功后访问自己要部署的应用即可。

利用Gitblit+Jenkins+Docker+SpringBoot实现项目自动发布

立即构建

项目访问:自己项目部署的地址

使用TensorFlow动手实现的简单的股价预测模型

本文这是一个关于TensorFlow的动手教程,旨在让大家了解TensorFlow。股票价格的实际预测是一项非常复杂的任务,尤其是像本文中的按分钟预测。

导入并预处理数据

我们的团队从我们的抓取服务器中的数据并csv格式的保存。数据集包含n = 41266分钟的数据,从2017年4月到8月,500只股票,以及标准普尔500指数成份股。指数和股票以宽格式排列。

数据集:http://files.statworx.com/sp500.zip

使用TensorFlow动手实现的简单的股价预测模型

数据已经被清理并预处理完毕,缺失的股票和指数价格已经被LOCF(末次观测值结转法),这样文件里就没有任何缺失值了。

使用pyplot.plot(‘SP500’)可以快速查看标准普尔时间序列:

使用TensorFlow动手实现的简单的股价预测模型

标准普尔500指数的时间序列图

注:这实际上是标准普尔500指数的主要指标,也就是说,它的值向未来移动了1分钟。因为我们想要预测下一分钟的指数,这个操作是必须的。

准备训练和测试数据

数据集被分成训练集和测试集。训练数据为总数据集的80%。数据不进行打乱,而是按顺序切片。训练数据可以从2017年4月选取到2017年7月底,测试数据则选取到2017年8月底为止。

使用TensorFlow动手实现的简单的股价预测模型

对时间序列的交叉验证有很多不同的方法,比如不带有refitting或更复杂概念的滚动预测,或者时间序列引导重采样。后者涉及时间序列的周期性分解的重复样本,以便模拟遵循与原始时间序列相同的周期性模式样本,但这并不是完全的复制他们的值。

数据缩放

大多数神经网络架构能受益于标准化或归一化输入(有时也是输出)。因为网络的神经元使用tanh或sigmoid(最常见的激活函数)分别定义在[-1, 1]或[0, 1]区间。目前,ReLu(Rectified Linear Units)激活函数十分常用,它在激活值的轴上没有上界。但无论如何,我们都会调整输入和目标值。在Python中使用sklearn中的MinMaxScaler可实现缩放。

使用TensorFlow动手实现的简单的股价预测模型

备注:缩放哪部分的数据以及何时缩放都需要谨慎决定。常见的错误是在训练和测试拆分完成之前缩放整个数据集。因为缩放调用了统计数据,例如向量的最大或最小值。而在现实生活中进行时间序列预测时,预测时没有来自未来观测的信息。因此,必须对训练数据进行缩放统计计算,然后必须应用于测试数据。否则,在预测时使用未来的信息,通常偏向于正向预测指标。

TensorFlow简介

TensorFlow是一个深度学习和神经网络中处于领先地位的计算框架。它底层基于C++,通常通过Python进行控制(也有用于R语言的)。TensorFlow以底层计算任务的图形表示进行操作。这种方法允许用户将数学运算指定数据,变量和运算符作为图中的元素。由于神经网络实际上是数据图和数学运算,因此TensorFlow非常适合神经网络和深度学习。看看这个简单的例子:

使用TensorFlow动手实现的简单的股价预测模型

一个非常简单的图表,将两个数字相加

在上图中,添加两个数字。这些数字存储在两个变量,a和b中。这些数字存储在两个变量a和b中,这两个值通过图形流动,到达了标有加号的正方形节点然后相加。相加的结果被存储到变量c中。其实a,b和c可以被视为占位符。任何被输入到a和b的值都会相加并储存到c中。这就是TensorFlow的工作原理。用户通过占位符和变量来定义模型(神经网络)的抽象表示。然后占位符用实际数据“填充”,并发生实际计算。以下代码在实现上图的简单示例:

使用TensorFlow动手实现的简单的股价预测模型

导入了TensorFlow库,然后用tf.placeholder()定义了两个占位符。使它之们对应于上图中左侧的两个蓝色圆圈。之后,通过定义数学加法tf.add()。计算结果为c = 9。设置占位符后,可以在篮圈中使用任何整数值来执行a和b。当然,这只是简单的例子。神经网络真正需要的图形和计算要复杂得多。

占位符

我们需要从占位符。为了适应我们的模型,我们需要两个占位符:X包含网络的输入(在T = t时所有标准普尔500成份股的价格)和Y网络输出(T = t + 1标准普尔500指数的指数)。

占位符的形状为[None, n_stocks]和[None],表示输入是一个二维矩阵,输出是一维向量。要正确地设计出神经网络所需的输入和输出维度,了解这些是至关重要的。

使用TensorFlow动手实现的简单的股价预测模型

None表示,在这一点上我们还不知道在每个批处理中流过神经网络图的观测值的数量,使用它是为了保持灵活性。我们稍后将定义batch_size控制每次训练的批处理观察次数。

向量

除了占位符,向量是TensorFlow的另一个基础。占位符用于在图中存储输入数据和目标数据,而向量被用作图中的灵活容器在图形执行过程中允许更改。权重和偏置被表示为向量以便在训练中调整。向量需要在模型训练之前进行初始化。稍后我们会详细讨论。

该模型由四个隐藏层组成。第一层包含1024个神经元,略大于输入大小的两倍。随后的每个隐藏层是上一层的一半,也就是512、256和128个神经元。每个后续层的神经元数量的减少压缩了网络在先前层中识别的信息。当然,也有其他网络架构和神经元配置,但不在这篇文章的介绍范围之内。

使用TensorFlow动手实现的简单的股价预测模型

理解输入层,隐藏层和输出层之间所需的可变维度是很重要的。作为多层感知器(MLP)的一个经验法则,前一层的第二维是当前图层中权重矩阵的第一维。听起来很复杂,但其实只是每一层将其输出作为输入传递到下一层。偏置的维度等于当前层权重矩阵的第二维度,它对应于该层中的神经元的数量。

设计网络体系结构

在定义所需的权重和偏置向量之后,需要指定网络拓扑结构和网络结构。因此,占位符(数据)和向量(权重和偏置)需要被组合成一个连续的矩阵乘法系统。

此外,网络的隐藏层还要被激活函数转换。激活函数是网络体系结构的重要组成部分,因为它们将非线性引入系统。有几十个可能的激活函数,其中最常见的是整流线性单元(ReLU),它也将在这个模型中使用。

使用TensorFlow动手实现的简单的股价预测模型

下图展示了网络架构。该模型由三个主要部分组成。输入层,隐藏层和输出层。这种架构被称为前馈网络。前馈表示该批数据仅从左向右流动。其他网络体系结构(如递归神经网络)也允许数据在网络中“反向”流动。

使用TensorFlow动手实现的简单的股价预测模型

前馈网络架构

损失函数

网络的损失函数用于度量生成的网络预测与实际观察到的训练目标之间的偏差。回归问题,常用均方误差(MSE)函数。MSE计算预测和目标之间的平均方差。一般来说,任何可微函数都可以做预测和目标之间的偏差度量。

使用TensorFlow动手实现的简单的股价预测模型

但是,MS有利于解决一些常见优化问题。

优化器

优化器是在训练期间调整网络的权重和偏置向量的必要计算。这些计算调用了梯度计算,它们指示训练期间权重和偏置需要改变的方向,以最小化网络的损失函数。稳定快速优化器的开发是神经网络深入学习研究的一个重要领域。

使用TensorFlow动手实现的简单的股价预测模型

我们这里使用了Adam优化器r,这是当前深度学习开发的默认优化器之一。它的名称来源于适应性矩估计,可以看作另两个流行的优化器AdaGrad和RMSProp的组合。

初始化器

初始化器用于在训练之前初始化网络的向量。由于神经网络是使用数值优化技术进行训练的,所以优化问题的出发点是寻找解决底层问题的关键。在TensorFlow中有不同的初始化器,每个都有不同的初始化方法。在这里,我使用了tf.variance_scaling_initializer(),这是默认的初始化策略。

使用TensorFlow动手实现的简单的股价预测模型

请注意,TensorFlow可以为图中的不同向量定义多个初始化函数。但大多数情况下,统一的初始化就足够了。

拟合神经网络

在定义了网络的占位符,向量,初始化器,损失函数和优化器之后,可以对模型进行训练了。通常通过小批量训练完成。在小批量训练期间,从训练数据中抽取n = batch_size随机数据样本并馈送到网络中。训练数据集被分成n / batch_size个批量按顺序馈入网络。此时的占位符,X和Y发挥作用。他们存储输入和目标数据,并将其作为输入和目标在网络中显示。

采样数据X批量流经网络,到达输出层。在那里,TensorFlow将模型预测与当前批量的实际观测目标Y进行比较。之后,TensorFlow进行优化步骤并更新与所选学习方案相对应的网络参数。在更新权重和偏置之后,下一个批量被采样,并重复此过程。直到所有的批量都被提交给网络。完成所有批量被称为完成一次epoch。

epoch达到最大或者用户定义的其他停止标准,网络的训练就会停止。

使用TensorFlow动手实现的简单的股价预测模型

在训练期间,我们评估测试集上的网络预测数据,测试集数据不被网络学习,每5批量就留下一个,并将其可视化。此外,这些图像被导出到磁盘,然后组合成训练过程的视频(如下)。该模型快速学习测试数据中的时间序列的形状和位置,并且能够在几个epoch之后产生准确的预测。

视频加载中…

我们可以看到网络很快适应时间序列的基本形状,并继续学习更精细的数据模式。这也对应于在模型训练期间降低学习速率的Adam学习方案,防止错过优化最小值。经过10个epoch之后,我们与测试数据非常接近了。最后的测试MSE等于0.00078(已经非常低了,目标曾缩放过)。测试集预测的平均绝对百分比误差等于5.31%,这是相当不错的。当然,这个结果只在测试数据中,在现实中没有实际的样本去度量。

使用TensorFlow动手实现的简单的股价预测模型

缩放后的预测与实际标准普尔散点图

请注意,有很多方法可以进一步改善这个结果:层和神经元的设计,选择不同的初始化和激活方案,引入神经元退出层等等。此外,不同类型的深度学习模型(如递归神经网络)可能在此任务上会有更好的性能。

要写正则的时候打开,先收藏

正则表达式中的特殊字符

字符 含意

做为转意,即通常在””后面的字符不按原来意义解释,如/b/匹配字符”b”,当b前面加了反斜杆后//,转意为匹配一个单词的边界。

-或-

对正则表达式功能字符的还原,如”*”匹配它前面元字符0次或多次,/a*/将匹配a,aa,aaa,加了””后,/a*/将只匹配”a*”。

^ 匹配一个输入或一行的开头,/^a/匹配”an A”,而不匹配”An a”

$ 匹配一个输入或一行的结尾,/a$/匹配”An a”,而不匹配”an A”

* 匹配前面元字符0次或多次,/ba*/将匹配b,ba,baa,baaa

+ 匹配前面元字符1次或多次,/ba*/将匹配ba,baa,baaa

? 匹配前面元字符0次或1次,/ba*/将匹配b,ba

(x) 匹配x保存x在名为$1…$9的变量中

x|y 匹配x或y

{n} 精确匹配n次

{n,} 匹配n次以上

{n,m} 匹配n-m次

[xyz] 字符集(character set),匹配这个集合中的任一一个字符(或元字符)

[^xyz] 不匹配这个集合中的任何一个字符

[] 匹配一个退格符

 匹配一个单词的边界

B 匹配一个单词的非边界

cX 这儿,X是一个控制符,/cM/匹配Ctrl-M

d 匹配一个字数字符,/d/ = /[0-9]/

D 匹配一个非字数字符,/D/ = /[^0-9]/

匹配一个换行符

匹配一个回车符

s 匹配一个空白字符,包括 , , , , 等

S 匹配一个非空白字符,等于/[^ ]/

匹配一个制表符

匹配一个重直制表符

w 匹配一个可以组成单词的字符(alphanumeric,这是我的意译,含数字),包括下划线,如[w]匹配”$5.98″中的5,等于[a-zA-Z0-9]

W 匹配一个不可以组成单词的字符,如[W]匹配”$5.98″中的$,等于[^a-zA-Z0-9]。

常用正则表达式例子

^d+$  //匹配非负整数(正整数 + 0)

^[0-9]*[1-9][0-9]*$  //匹配正整数

^((-d+)|(0+))$  //匹配非正整数(负整数 + 0)

^-[0-9]*[1-9][0-9]*$  //匹配负整数

^-?d+$    //匹配整数

^d+(.d+)?$  //匹配非负浮点数(正浮点数 + 0)

^(([0-9]+.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*.[0-9]+)|([0-9]*[1-9][0-9]*))$  //匹配正浮点数

^((-d+(.d+)?)|(0+(.0+)?))$  //匹配非正浮点数(负浮点数 + 0)

^(-(([0-9]+.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*.[0-9]+)|([0-9]*[1-9][0-9]*)))$  //匹配负浮点数

^(-?d+)(.d+)?$  //匹配浮点数

^[A-Za-z]+$  //匹配由26个英文字母组成的字符串

^[A-Z]+$  //匹配由26个英文字母的大写组成的字符串

^[a-z]+$  //匹配由26个英文字母的小写组成的字符串

^[A-Za-z0-9]+$  //匹配由数字和26个英文字母组成的字符串

^w+$  //匹配由数字、26个英文字母或者下划线组成的字符串

^[w-]+(.[w-]+)*@[w-]+(.[w-]+)+$    //匹配email地址

^[a-zA-z]+://匹配(w+(-w+)*)(.(w+(-w+)*))*(?S*)?$  //匹配url

[一-龥] //匹配中文字符的正则表达式

[^-ÿ] //匹配双字节字符(包括汉字在内)

[s| ]* //匹配空行的正则表达式

/<(.*)>.*</>|<(.*) />/ //匹配HTML标记的正则表达式

(^s*)|(s*$ //匹配首尾空格的正则表达式

匹配特定数字:

^[1-9]d*$    //匹配正整数

^-[1-9]d*$   //匹配负整数

^-?[1-9]d*$   //匹配整数

^[1-9]d* ¦0$  //匹配非负整数(正整数 + 0)

^-[1-9]d* ¦0$   //匹配非正整数(负整数 + 0)

^[1-9]d*.d* ¦0.d*[1-9]d*$   //匹配正浮点数

^-([1-9]d*.d* ¦0.d*[1-9]d*)$  //匹配负浮点数

^-?([1-9]d*.d* ¦0.d*[1-9]d* ¦0?.0+ ¦0)$  //匹配浮点数

^[1-9]d*.d* ¦0.d*[1-9]d* ¦0?.0+ ¦0$   //匹配非负浮点数(正浮点数 + 0)

^(-([1-9]d*.d* ¦0.d*[1-9]d*)) ¦0?.0+ ¦0$  //匹配非正浮点数(负浮点数 + 0)

评注:处理大量数据时有用,具体应用时注意修正

匹配特定字符串:

^[A-Za-z]+$  //匹配由26个英文字母组成的字符串

^[A-Z]+$   //匹配由26个英文字母的大写组成的字符串

^[a-z]+$   //匹配由26个英文字母的小写组成的字符串

^[A-Za-z0-9]+$  //匹配由数字和26个英文字母组成的字符串

^w+$   //匹配由数字、26个英文字母或者下划线组成的字符串

tensorflow项目学习路径

一、构建路线个人感觉对于任何一个深度学习库,如mxnet、tensorflow、theano、caffe等,基本上我都采用同样的一个学习流程,大体流程如下:(1)训练阶段:数据打包 -》网络构建、训练 -》模型保存 -》可视化查看损失函数、验证精度(2)测试阶段:模型加载 -》测试图片读取 -》预测显示结果(3)移植阶段:量化、压缩加速 -》微调 -》C + +移植打包 -》上线这边我就以tensorflow为例子,讲解整个流程的大体架构,完成一个深度学习项目所需要熟悉的过程代码。二、训练、测试阶段1、tensorflow打包数据这一步对于tensorflow来说,也可以直接自己在线读取:.jpg图片、标签文件等,然后通过phaceholder变量,把数据送入网络中,进行计算。不过这种效率比较低,对于大规模训练数据来说,我们需要一个比较高效的方式,tensorflow建议我们采用tfrecoder进行高效数据读取。学习tensorflow一定要学会tfrecoder文件写入、读取,具体示例代码如下:# coding=utf-8 # tensorflow高效数据读取训练 import tensorflow as tfimport cv2# 把train.txt文件格式,每一行:图片路径名 类别标签 # 奖数据打包,转换成tfrecords格式,以便后续高效读取 def encode_to_tfrecords(lable_file, data_root, new_name=’data.tfrecords’,resize=None):writer = tf.python_io.TFRecordWriter(data_root + ‘/’+ new_name) num_example = 0with open(lable_file, ‘r’) as f: for l in f.readlines():l = l.split() image = cv2.imread(data_root + “/”+ l[0])if resize is not None:image = cv2.resize(image, resize) # 为了 height, width, nchannel = image.shape label = int(l[1]) example = tf.train.Example(features=tf.train.Features(feature={‘height’: tf.train.Feature(int64_list=tf.train.Int64List(value=[height])),’width’: tf.train.Feature(int64_list=tf.train.Int64List(value=[width])),’nchannel’: tf.train.Feature(int64_list=tf.train.Int64List(value=[nchannel])),’image’: tf.train.Feature(bytes_list=tf.train.BytesList(value=[image.tobytes()])),’label’: tf.train.Feature(int64_list=tf.train.Int64List(value=[label])) })) serialized = example.SerializeToString() writer.write(serialized) num_example += 1print lable_file, “样本数据量:”, num_example writer.close()# 读取tfrecords文件 def decode_from_tfrecords(filename, num_epoch=None):filename_queue = tf.train.string_input_producer([filename],num_epochs=num_epoch) # 因为有的训练数据过于庞大,被分成了很多个文件,所以第一个参数就是文件列表名参数 reader = tf.TFRecordReader() _, serialized = reader.read(filename_queue) example = tf.parse_single_example(serialized, features={‘height’: tf.FixedLenFeature([], tf.int64),’width’: tf.FixedLenFeature([], tf.int64),’nchannel’: tf.FixedLenFeature([], tf.int64),’image’: tf.FixedLenFeature([], tf.string),’label’: tf.FixedLenFeature([], tf.int64) }) label = tf.cast(example[‘label’], tf.int32) image = tf.decode_raw(example[‘image’], tf.uint8) image = tf.reshape(image, tf.pack([ tf.cast(example[‘height’], tf.int32), tf.cast(example[‘width’], tf.int32), tf.cast(example[‘nchannel’], tf.int32)]))# label=example[‘label’] return image, label# 根据队列流数据格式,解压出一张图片后,输入一张图片,对其做预处理、及样本随机扩充 def get_batch(image, label, batch_size, crop_size):# 数据扩充变换 distorted_image = tf.random_crop(image, [crop_size, crop_size, 3]) # 随机裁剪 distorted_image = tf.image.random_flip_up_down(distorted_image) # 上下随机翻转 # distorted_image = tf.image.random_brightness(distorted_image,max_delta=63)#亮度变化 # distorted_image = tf.image.random_contrast(distorted_image,lower=0.2, upper=1.8)#对比度变化 # 生成batch # shuffle_batch的参数:capacity用于定义shuttle的范围,如果是对整个训练数据集,获取batch,那么capacity就应该够大 # 保证数据打的足够乱 images, label_batch = tf.train.shuffle_batch([distorted_image, label],batch_size=batch_size,num_threads=16, capacity=50000, min_after_dequeue=10000)# images, label_batch=tf.train.batch([distorted_image, label],batch_size=batch_size) # 调试显示 # tf.image_summary(‘images’, images) return images, tf.reshape(label_batch, [batch_size])# 这个是用于测试阶段,使用的get_batch函数 def get_test_batch(image, label, batch_size, crop_size):# 数据扩充变换 distorted_image = tf.image.central_crop(image, 39. / 45.) distorted_image = tf.random_crop(distorted_image, [crop_size, crop_size, 3]) # 随机裁剪 images, label_batch = tf.train.batch([distorted_image, label],batch_size=batch_size)return images, tf.reshape(label_batch, [batch_size])# 测试上面的压缩、解压代码 def test():encode_to_tfrecords(“data/train.txt”, “data”, (100, 100)) image, label = decode_from_tfrecords(‘data/data.tfrecords’) batch_image, batch_label = get_batch(image, label, 3) # batch 生成测试 init = tf.initialize_all_variables()with tf.Session() as session:session.run(init) coord = tf.train.Coordinator() threads = tf.train.start_queue_runners(coord=coord)for l in range(100000): # 每run一次,就会指向下一个样本,一直循环 # image_np,label_np=session.run([image,label])#每调用run一次,那么 ””’cv2.imshow(“temp”,image_np) cv2.waitKey()”’# print label_np # print image_np.shape batch_image_np, batch_label_np = session.run([batch_image, batch_label])print batch_image_np.shapeprint batch_label_np.shape coord.request_stop() # queue需要关闭,否则报错 coord.join(threads)# test() 2、网络架构与训练经过上面的数据格式处理,接着我们只要写一写网络结构、网络优化方法,把数据搞进网络中就可以了,具体示例代码如下:# coding=utf-8 import tensorflow as tffrom data_encoder_decoeder import encode_to_tfrecords, decode_from_tfrecords,get_batch, get_test_batchimport cv2import osclass network(object): def __init__(self): with tf.variable_scope(“weights”):self.weights = {# 39*39*3->36*36*20->18*18*20 ‘conv1’: tf.get_variable(‘conv1’, [4, 4, 3, 20],initializer=tf.contrib.layers.xavier_initializer_conv2d()),# 18*18*20->16*16*40->8*8*40 ‘conv2’: tf.get_variable(‘conv2’, [3, 3, 20, 40],initializer=tf.contrib.layers.xavier_initializer_conv2d()),# 8*8*40->6*6*60->3*3*60 ‘conv3’: tf.get_variable(‘conv3’, [3, 3, 40, 60],initializer=tf.contrib.layers.xavier_initializer_conv2d()),# 3*3*60->120 ‘fc1’: tf.get_variable(‘fc1’, [3 * 3 * 60, 120], initializer=tf.contrib.layers.xavier_initializer()),# 120->6 ‘fc2’: tf.get_variable(‘fc2’, [120, 6], initializer=tf.contrib.layers.xavier_initializer()), }with tf.variable_scope(“biases”):self.biases = {‘conv1’: tf.get_variable(‘conv1′, [20, ],initializer=tf.constant_initializer(value=0.0, dtype=tf.float32)),’conv2’: tf.get_variable(‘conv2′, [40, ],initializer=tf.constant_initializer(value=0.0, dtype=tf.float32)),’conv3’: tf.get_variable(‘conv3′, [60, ],initializer=tf.constant_initializer(value=0.0, dtype=tf.float32)),’fc1’: tf.get_variable(‘fc1′, [120, ],initializer=tf.constant_initializer(value=0.0, dtype=tf.float32)),’fc2’: tf.get_variable(‘fc2’, [6, ], initializer=tf.constant_initializer(value=0.0, dtype=tf.float32)) }def inference(self, images):# 向量转为矩阵 images = tf.reshape(images, shape=[-1, 39, 39, 3]) # [batch, in_height, in_width, in_channels] images = (tf.cast(images, tf.float32) / 255. – 0.5) * 2 # 归一化处理 # 第一层 conv1 = tf.nn.bias_add(tf.nn.conv2d(images, self.weights[‘conv1′], strides=[1, 1, 1, 1], padding=’VALID’),self.biases[‘conv1′]) relu1 = tf.nn.relu(conv1) pool1 = tf.nn.max_pool(relu1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding=’VALID’)# 第二层 conv2 = tf.nn.bias_add(tf.nn.conv2d(pool1, self.weights[‘conv2′], strides=[1, 1, 1, 1], padding=’VALID’),self.biases[‘conv2′]) relu2 = tf.nn.relu(conv2) pool2 = tf.nn.max_pool(relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding=’VALID’)# 第三层 conv3 = tf.nn.bias_add(tf.nn.conv2d(pool2, self.weights[‘conv3′], strides=[1, 1, 1, 1], padding=’VALID’),self.biases[‘conv3′]) relu3 = tf.nn.relu(conv3) pool3 = tf.nn.max_pool(relu3, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding=’VALID’)# 全连接层1,先把特征图转为向量 flatten = tf.reshape(pool3, [-1, self.weights[‘fc1’].get_shape().as_list()[0]]) drop1 = tf.nn.dropout(flatten, 0.5) fc1 = tf.matmul(drop1, self.weights[‘fc1’]) + self.biases[‘fc1’] fc_relu1 = tf.nn.relu(fc1) fc2 = tf.matmul(fc_relu1, self.weights[‘fc2’]) + self.biases[‘fc2’]return fc2def inference_test(self, images):# 向量转为矩阵 images = tf.reshape(images, shape=[-1, 39, 39, 3]) # [batch, in_height, in_width, in_channels] images = (tf.cast(images, tf.float32) / 255. – 0.5) * 2 # 归一化处理 # 第一层 conv1 = tf.nn.bias_add(tf.nn.conv2d(images, self.weights[‘conv1′], strides=[1, 1, 1, 1], padding=’VALID’),self.biases[‘conv1′]) relu1 = tf.nn.relu(conv1) pool1 = tf.nn.max_pool(relu1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding=’VALID’)# 第二层 conv2 = tf.nn.bias_add(tf.nn.conv2d(pool1, self.weights[‘conv2′], strides=[1, 1, 1, 1], padding=’VALID’),self.biases[‘conv2′]) relu2 = tf.nn.relu(conv2) pool2 = tf.nn.max_pool(relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding=’VALID’)# 第三层 conv3 = tf.nn.bias_add(tf.nn.conv2d(pool2, self.weights[‘conv3′], strides=[1, 1, 1, 1], padding=’VALID’),self.biases[‘conv3′]) relu3 = tf.nn.relu(conv3) pool3 = tf.nn.max_pool(relu3, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding=’VALID’)# 全连接层1,先把特征图转为向量 flatten = tf.reshape(pool3, [-1, self.weights[‘fc1’].get_shape().as_list()[0]]) fc1 = tf.matmul(flatten, self.weights[‘fc1’]) + self.biases[‘fc1’] fc_relu1 = tf.nn.relu(fc1) fc2 = tf.matmul(fc_relu1, self.weights[‘fc2’]) + self.biases[‘fc2’]return fc2# 计算softmax交叉熵损失函数 def sorfmax_loss(self, predicts, labels):predicts = tf.nn.softmax(predicts) labels = tf.one_hot(labels, self.weights[‘fc2’].get_shape().as_list()[1]) loss = -tf.reduce_mean(labels * tf.log(predicts)) # tf.nn.softmax_cross_entropy_with_logits(predicts, labels) self.cost = lossreturn self.cost# 梯度下降 def optimer(self, loss, lr=0.001):train_optimizer = tf.train.GradientDescentOptimizer(lr).minimize(loss)return train_optimizerdef train():encode_to_tfrecords(“data/train.txt”, “data”, ‘train.tfrecords’, (45, 45)) image, label = decode_from_tfrecords(‘data/train.tfrecords’) batch_image, batch_label = get_batch(image, label, batch_size=50, crop_size=39) # batch 生成测试 # 网络链接,训练所用 net = network() inf = net.inference(batch_image) loss = net.sorfmax_loss(inf, batch_label) opti = net.optimer(loss)# 验证集所用 encode_to_tfrecords(“data/val.txt”, “data”, ‘val.tfrecords’, (45, 45)) test_image, test_label = decode_from_tfrecords(‘data/val.tfrecords’, num_epoch=None) test_images, test_labels = get_test_batch(test_image, test_label, batch_size=120, crop_size=39) # batch 生成测试 test_inf = net.inference_test(test_images) correct_prediction = tf.equal(tf.cast(tf.argmax(test_inf, 1), tf.int32), test_labels) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) init = tf.initialize_all_variables()with tf.Session() as session:session.run(init) coord = tf.train.Coordinator() threads = tf.train.start_queue_runners(coord=coord) max_iter = 100000iter = 0if os.path.exists(os.path.join(“model”, ‘model.ckpt’)) is True:tf.train.Saver(max_to_keep=None).restore(session, os.path.join(“model”, ‘model.ckpt’))while iter < max_iter:loss_np, _, label_np, image_np, inf_np = session.run([loss, opti, batch_label, batch_image, inf])# print image_np.shape # cv2.imshow(str(label_np[0]),image_np[0]) # print label_np[0] # cv2.waitKey() # print label_np if iter % 50 == 0: print ‘trainloss:’, loss_npif iter % 500 == 0:accuracy_np = session.run([accuracy])print ‘***************test accruacy:’, accuracy_np, ‘*******************’tf.train.Saver(max_to_keep=None).save(session, os.path.join(‘model’, ‘model.ckpt’)) iter += 1coord.request_stop() # queue需要关闭,否则报错 coord.join(threads)train()3、可视化显示(1)首先再源码中加入需要跟踪的变量:style = “font-size:18px;”> tf.scalar_summary(“cost_function”, loss) # 损失函数值</span> (2)然后定义执行操作:style = “font-size:18px;”> merged_summary_op = tf.merge_all_summaries() < / span >(3)再session中定义保存路径:style = “font-size:18px;”> summary_writer = tf.train.SummaryWriter(‘log’, session.graph) < / span >(4)然后再session执行的时候,保存:[python]viewplaincopysummary_str, loss_np, _ = session.run([merged_summary_op, loss, opti])summary_writer.add_summary(summary_str, iter)(5)最后只要训练完毕后,直接再终端输入命令:python / usr / local / lib / python2.7 / dist – packages / tensorflow / tensorboard / tensorboard.py – -logdir = log然后打开浏览器网址:[python]viewplaincopy< spanstyle = “font-size:18px;”> http: // 0.0.0.0:6006 < / span >即可观训练曲线。4、测试阶段测试阶段主要是直接通过加载图模型、读取参数等,然后直接通过tensorflow的相关函数,进行调用,而不需要网络架构相关的代码;通过内存feed_dict的方式,对相关的输入节点赋予相关的数据,进行前向传导,并获取相关的节点数值。# coding=utf-8 import tensorflow as tfimport osimport cv2def load_model(session, netmodel_path, param_path):new_saver = tf.train.import_meta_graph(netmodel_path) new_saver.restore(session, param_path) x = tf.get_collection(‘test_images’)[0] # 在训练阶段需要调用tf.add_to_collection(‘test_images’,test_images),保存之 y = tf.get_collection(“test_inf”)[0] batch_size = tf.get_collection(“batch_size”)[0]return x, y, batch_sizedef load_images(data_root):filename_queue = tf.train.string_input_producer(data_root) image_reader = tf.WholeFileReader() key, image_file = image_reader.read(filename_queue) image = tf.image.decode_jpeg(image_file)return image, keydef test(data_root=”data/race/cropbrown”):image_filenames = os.listdir(data_root) image_filenames = [(data_root + ‘/’+ i) for i in image_filenames]# print cv2.imread(image_filenames[0]).shape # image,key=load_images(image_filenames) race_listsrc = [‘black’, ‘brown’, ‘white’, ‘yellow’]with tf.Session() as session:coord = tf.train.Coordinator() threads = tf.train.start_queue_runners(coord=coord) x, y, batch_size = load_model(session, os.path.join(“model”, ‘model_ori_race.ckpt.meta’), os.path.join(“model”, ‘model_ori_race.ckpt’)) predict_label = tf.cast(tf.argmax(y, 1), tf.int32)print x.get_shape()for imgf in image_filenames:image = cv2.imread(imgf) image = cv2.resize(image, (76, 76)).reshape((1, 76, 76, 3))print “cv shape:”, image.shape# cv2.imshow(“t”,image_np[:,:,::-1]) y_np = session.run(predict_label, feed_dict={x: image, batch_size: 1})print race_listsrc[y_np] coord.request_stop() # queue需要关闭,否则报错 coord.join(threads)4、移植阶段(1)一个算法经过实验阶段后,接着就要进入移植商用,因此接着需要采用tensorflow的capi函数,直接进行预测推理,首先我们先把tensorflow编译成链接库,然后编写cmake,调用tensorflow链接库:style = “font-size:18px;”> bazelbuild – copt // tensorflow:libtensorflow.so在bazel – bin / tensorflow目录下会生成libtensorflow.so文件5、C + + API调用、cmake编写:三、熟悉常用API1、LSTM使用import tensorflow.nn.rnn_celllstm = rnn_cell.BasicLSTMCell(lstm_size) # 创建一个lstm cell单元类,隐藏层神经元个数为lstm_size state = tf.zeros([batch_size, lstm.state_size]) # 一个序列隐藏层的状态值 loss = 0.0for current_batch_of_words in words_in_dataset:output, state = lstm(current_batch_of_words, state) # 返回值为隐藏层神经元的输出 logits = tf.matmul(output, softmax_w) + softmax_b # matmul矩阵点乘 probabilities = tf.nn.softmax(logits) # softmax输出 loss += loss_function(probabilities, target_words) < / span >1、one – hot函数:[python]viewplaincopy< spanstyle = “font-size:18px;”> # ont hot 可以把训练数据的标签,直接转换成one_hot向量,用于交叉熵损失函数 import tensorflow as tfa = tf.convert_to_tensor([[1], [2], [4]])b = tf.one_hot(a, 5) < / span >>> b的值为[python]viewplaincopy< spanstyle = “font-size:18px;”> [[[0. 1. 0. 0. 0.]] [[0. 0. 1. 0. 0.]] [[0. 0. 0. 0. 1.]]] < / span >2、assign_sub[python]viewplaincopy< spanstyle = “font-size:18px;”>import tensorflow as tfx = tf.Variable(10, name=”x”)sub = x.assign_sub(3) # 如果直接采用x.assign_sub,那么可以看到x的值也会发生变化 init_op = tf.initialize_all_variables()with tf.Session() as sess:sess.run(init_op)print sub.eval()print x.eval() < / span >可以看到输入sub = x = 7采用state_ops的assign_sub也是同样sub = x = 7也就是说assign函数返回结果值的同时,变量本身的值也会被改变3、变量查看# 查看所有的变量 for l in tf.all_variables(): print l.name < / span >4、slice函数:import cv2import tensorflow as tf# slice 函数可以用于切割子矩形图片,参数矩形框的rect,begin=(minx,miny),size=(width,height) minx = 20miny = 30height = 100width = 200image = tf.placeholder(dtype=tf.uint8, shape=(386, 386, 3))rect_image = tf.slice(image, (miny, minx, 0), (height, width, -1))cvimage = cv2.imread(“1.jpg”)cv2.imshow(“cv2”, cvimage[miny:(miny + height), minx:(minx + width), :])with tf.Session() as sess:tfimage = sess.run([rect_image], {image: cvimage}) cv2.imshow(‘tf’, tfimage[0])cv2.waitKey() < / span >5、正太分布随机初始化[python]viewplaincopytf.truncated_normal6、打印操作运算在硬件设备信息[python]viewplaincopytf.ConfigProto(log_device_placement=True)7、变量域名的reuse:[python]viewplaincopyimport tensorflow as tfwith tf.variable_scope(‘foo’): # 在没有启用reuse的情况下,如果该变量还未被创建,那么就创建该变量,如果已经创建过了,那么就获取该共享变量 v = tf.get_variable(‘v’, [1])with tf.variable_scope(‘foo’, reuse=True): # 如果启用了reuse,那么编译的时候,如果get_variable没有遇到一个已经创建的变量,是会出错的 v1 = tf.get_variable(‘v1’, [1])8、allow_soft_placement的使用:allow_soft_placement = True,允许当在代码中指定tf.device设备,如果设备找不到,那么就采用默认的设备。如果该参数设置为false,当设备找不到的时候,会直接编译不通过。9、batchnormalize调用:[python]viewplaincopytf.contrib.layers.batch_norm(x, decay=0.9, updates_collections=None,epsilon=self.epsilon, scale=True, scope=self.name)

Java并发学习之四种线程创建方式的实现与对比

线程创建的几种方式

在并发编程中,最基本的就是创建线程了,那么一般的创建姿势是怎样的,又都有些什么区别

Java并发学习之四种线程创建方式的实现与对比

一般来讲线程创建有四种方式:

  1. 继承Thread
  2. 实现Runnable接口
  3. 实现Callable接口,结合 FutureTask使用
  4. 利用该线程池ExecutorService、Callable、Future来实现

所以本篇博文从布局来讲,分为两部分

  1. 实例演示四种使用方式
  2. 对比分析四种使用方式的异同,以及适合的应用场景

I. 实例演示

目标: 创建两个线程并发实现从1-1000的累加

1. 继承Thread实现线程创建

实现逻辑如下

Java并发学习之四种线程创建方式的实现与对比

输出结果

Java并发学习之四种线程创建方式的实现与对比

一般实现步骤:

  • 继承Thread类
  • 覆盖run()方法
  • 直接调用Thread#start()执行

逻辑比较清晰,只需要注意覆盖的是run方法,而不是start方法

2. 实现Runnable接口方式创建线程

Java并发学习之四种线程创建方式的实现与对比

输出结果

Java并发学习之四种线程创建方式的实现与对比

一般实现步骤:

  • 实现Runnable接口
  • 获取实现Runnable接口的实例,作为参数,创建Thread实例
  • 执行Thread#start()启动线程

说明

相比于继承Thread,这里是实现一个接口,最终依然是借助Thread#start()来启动线程

然后就有个疑问:

两者是否有本质上的区别,在实际项目中如何抉择?

3. 实现Callable接口,结合FutureTask创建线程

Callable接口相比于Runnable接口而言,会有个返回值,那么如何利用这个返回值呢?

demo如下

Java并发学习之四种线程创建方式的实现与对比

输出结果

Java并发学习之四种线程创建方式的实现与对比

一般实现步骤:

  • 实现Callable接口
  • 以Callable的实现类为参数,创建FutureTask实例
  • 将FutureTask作为Thread的参数,创建Thread实例
  • 通过Thread#start启动线程
  • 通过FutreTask#get()阻塞获取线程的返回值

说明

Callable接口相比Runnable而言,会有结果返回,因此会由FutrueTask进行封装,以期待获取线程执行后的结果;

最终线程的启动都是依赖Thread#start

4. 线程池方式创建

demo如下,创建固定大小的线程池,提交Callable任务,利用Future获取返回的值

Java并发学习之四种线程创建方式的实现与对比

输出

Java并发学习之四种线程创建方式的实现与对比

一般实现逻辑:

  • 创建线程池(可以利用JDK的Executors,也可自己实现)
  • 创建Callable 或 Runnable任务,提交到线程池
  • 通过返回的Future#get获取返回的结果

II. 对比分析

1. 分类

上面虽然说是有四种方式,但实际而言,主要划分为两类

  • 继承Thread类,覆盖run方法填写业务逻辑
  • 实现Callable或Runnable接口,然后通过Thread或线程池来启动线程

此外,还有一种利用Fork/Join框架来实现并发的方式,后续专门说明,此处先略过

2. 区分说明

继承和实现接口的区别

先把线程池的方式拎出来单独说,这里主要对比Thread, Callable, Runnable三中方式的区别

个人理解,线程的这两种方式的区别也就只有继承和实现接口的本质区别:

一个是继承Thread类,可以直接调用实例的start()方法来启动线程;另一个是实现接口,需要借助Thread#start()来启动线程

继承因为java语言的限制,当你的任务需要继承一个自定义的类时,会有缺陷;而实现接口却没有这个限制


至于网上很多地方说的实现Runnable接口更利于资源共享什么的,比如下面这种作为对比的

Java并发学习之四种线程创建方式的实现与对比

输出:

Java并发学习之四种线程创建方式的实现与对比

MyRun实现Runnable接口,然后创建一个实例,将这个实例作为多个Thread的参数构造Thread类,然后启动线程,发现这几个线程共享了MyRun#ato变量

然而上面这个实现接口改成继承Thread,其他都不变,也没啥两样

Java并发学习之四种线程创建方式的实现与对比

输出如下

Java并发学习之四种线程创建方式的实现与对比

上面除了说明使用Runnable更利于资源共享神马的,其实并没有之外,还有一个比较有意思的,为什么会输出-1?

如果我这个任务是售票的话,妥妥的就超卖了,这个问题留待后续详解


Runnable, Callable两种区别

这两个就比较明显了,最根本的就是

  • Runnable 无返回结果
  • Callable 有返回结果

从根源出发,就直接导致使用姿势上的区别

举个形象的例子说明两种方式的区别:

小明家今儿没米了,小明要吃饭怎么办?

小明他妈对小明说,去你大爷家吃饭吧,至于小明到底吃没吃着,小妈他妈就不管了,这就是Runnable方式;

小明他妈一想,这一家子都要吃饭,我先炒个菜,让小明去大爷家借点米来,所以就等着小明拿米回来开锅,这就是Callable方式

1.Runnable

Runnable不关心返回,所以任务自己默默的执行就可以了,也不用告诉我完成没有,我不care,您自己随便玩,所以一般使用就是

Java并发学习之四种线程创建方式的实现与对比

换成JDK8的 lambda表达式就更简单了

Java并发学习之四种线程创建方式的实现与对比

2.Callable

相比而言,callbale就悲催一点,没法这么随意了,因为要等待返回的结果,但是这个线程的状态我又控制不了,怎么办?借助FutrueTask来玩,所以一般可以看到使用方式如下:

Java并发学习之四种线程创建方式的实现与对比


Thread启动和线程池启动方式

这个就高端了,线程池一听就感觉厉害了,前面的四中方式有三种都是Thread#start()来启动线程,这也是我们最常用的方式,这里单独说一下线程池的使用姿势

  • 首先是创建一个线程池
  • 利用ExecutorService#submit()提交线程
  • Future<Object>接收返回

Java并发学习之四种线程创建方式的实现与对比

说明,这里提交线程之后,并不表示线程立马就要执行,也不表示一定可以执行(这个留待后续线程池的学习中探讨)


III. 小结

四种创建方式

1. 继承Thread类,覆盖run方法,调用Thread#start启动

2. 实现Runnable接口,创建实例,作为Thread构造参数传入,调用Thread#start启动

Java并发学习之四种线程创建方式的实现与对比

3. 实现Callable接口,创建实例,作为FutureTask<>构造参数创建FutureTask对象,将FutureTask对象作为Thread构造参数传入,调用Thread#start启动

Java并发学习之四种线程创建方式的实现与对比

4. 创建一个线程池,利用ExecutorService#submit()提交线程,Future<Object>接收返回

Java并发学习之四种线程创建方式的实现与对比


区别与应用场景

  • 继承和实现接口的方式唯一区别就是继承和实现的区别,不存在共享变量的问题
  • 需要获取返回结果时,结合 FutureTask和Callable来实现
  • Thread和Runnable的两种方式,原则上想怎么用都可以,个人也没啥好推荐的,随意使用
  • 线程池需要注意线程复用时,对ThreadLocal中变量的串用问题(本篇没有涉及,等待后续补上)