首页 文章 精选 留言 我的

精选列表

搜索[云数据库 Redis®],共10034篇文章
优秀的个人博客,低调大师

Manifest.xml 入门基础 (三) <uses-sdk>标签

Manifest.xml 入门基础 (三) <uses-sdk>标签 <uses-sdk android:minSdkVersion="integer" android:targetSdkVersion="integer" android:maxSdkVersion="integer" /> 用于表明应用程序与一个或多个版本 Android 平台的兼容性,以整数型的 API 级别来表示。 应用程序声明的 API 级别将与给定的 Android 系统进行比较,不同 Android 设备的 API Level 可能各不相同。 不要去理会名称(指 uses-sdk),本元素是用于指定 API 级别 的,而不是指 SDK (软件开发包)或 Android 平台的版本号。 API 级别总是以一个整数值来表示。 API 级别是无法从相应的 Android 版本号推导出来的(比如,它既不是主版本号,也不是主版本号加小版本号)。 1.android:minSdkVersion 整数值,指定运行应用程序所需的最低 API 级别。 如果系统 API 级别低于本属性设定的值, Android 系统将阻止用户安装此应用程序。 本属性必需指定。 提醒: 如果未声明本属性,系统假定缺省值为“1”,这表示应用程序与所有版本的 Android 兼容。 如果应用程序无法兼容所有版本(比如,用到了 API 级别 3 才开始引入的 API ), 也没有声明正确的 android:minSdkVersion, 那么安装在低于 API 级别 3 的系统中后,一旦在运行过程中试图访问不可用的 API,应用程序就会崩溃。 因此,请确保在 minSdkVersion 属性中声明正确的 API 级别。 2.android:targetSdkVersion 整数值,指定应用程序的目标 API 级别。如果不设置本属性,缺省值等于已给定的 minSdkVersion。 本属性将告知系统应用程序已经针对此目标版本进行了测试,系统不会启用任何与此目标版本相关的向前兼容特性。 应用程序仍然能够在较低版本(最低至 minSdkVersion)的系统上运行。 随着 Android 的版本更新,某些特性甚至组件外观都会发生变化。 不过,当平台的 API 级别高于应用程序声明的 targetSdkVersion时,系统会启用兼容性特性,应用程序仍然可以按预计方式运行。 通过把 targetSdkVersion 设定为所需的平台版本,你可以禁用兼容特性。 比如,将本值设置为大于等于“11”,当运行于 Android 3.0 以上版本时, 系统就能在应用程序上使用新加入的默认主题(Holo),并且在大屏幕设备上时还会禁用 屏幕兼容模式 (因为 API 级别 11 隐含了对大屏幕的支持)。 根据本属性设定的值,有系统可能会启用很多兼容性特性。 有相当多的特性在参考文档 Build.VERSION_CODES 中分版本进行了说明。 为了能随着 Android 版本的发布而对应用程序进行维护,你应该及时调大本属性值以匹配最新的 API 级别, 然后在对应版本的平台上对程序进行充分的测试。 3.android:maxSdkVersion 整数值,指定应用程序设计可运行的最高 API 级别。 为了说明本属性在系统更新后对应用程序的影响,请参阅以下例子: 某应用程序在 manifest 中声明了 maxSdkVersion=”5” ,并已在 Google Play 进行了发布。 有一个用户下载并安装了此应用程序,他的设备运行的是 Android 1.6 (API 级别 4)。 几星期后,用户通过无线系统把系统升级到了 Android 2.0(API 级别 5)。 升级完成后,系统会检查应用程序的 maxSdkVersion 并成功进行了重新校验,应用程序能够正常工作。 但是一段时间之后,设备又收到了另一个系统更新,这次是升级到 Android 2.0.1(API 级别 6)。 这次更新后系统无法再对应用程序进行重新校验了,因为当前系统的 API 级别 (6)已经高于应用程序支持的最大版本(5)了。 系统会阻止用户使用此应用程序,实际上将会从设备上删除它。 警告: 不建议声明本属性。 首先,没有必要用本属性来阻止应用程序在新版本 Android 平台上的使用。 理论上讲,新版本的平台完全是向后全兼容的。 应用程序应该可以在新版本平台上正常运行,新版本不仅提供标准的API,还能开发最佳的体验。 其次请注意,在某些场合声明本属性,可能会 导致系统升级到高版本 API 后把应用程序删除。 大多数设备都会通过无线网络定期接收到系统更新,因此在设置本属性之前,你应该考虑对应用程序的影响。 什么是 API 级别 API 级别是一个整数值,它唯一标识了某版本 Android 平台所提供的 API 的版本。 Android 平台提供了一整套 API,应用程序可以用这些 API 与底层系统进行交互。 这套API由以下部分组成: 核心包和类 声明和访问资源时所用的 XML 格式的元素和属性 一些预设的 Intent 应用程序可请求的权限以及系统预设的权限 Android 平台每个较高版本都会包含之前的 API 更新。 API 的更新机制使得新版本的 API 可以与早期版本保持兼容。 也就是说,大部分的 API 改动都是新增功能,或是引入新 API ,或是替代原有功能。 作为 API 升级过程中的一部分工作,被替换的旧功能被视为过时功能但不会被删除。 因此,现有的应用程序仍然可以使用这些过时的功能。 在极少数情况下,一部分 API 可能会被修改或删除,通常这种改动只是为了确保 API 的稳定性及应用程序或系统的安全性。 早期版​​本的 API 剩余部分都将原封不动地带入新版本。 API 级别在 Android 中的用途 当用户尝试安装某应用程序时,或者在系统升级后对应用程序重新校验时, Android 系统首先会检查应用程序 manifest 中的 <uses-sdk> 属性, 并将其值与系统内部的 API 级别进行比较。 只有满足以下条件时,系统才会允许应用程序的安装: 如果声明了android:minSdkVersion属性,则其值必须小于等于当前系统的 API 级别值。 如果未声明,系统就会假定该应用程序运行需要的最低 API 级别是 1 。 如果声明了android:maxSdkVersion属性,则其值必须大于等于当前系统的 API 级别值。 如果未声明,系统就会假定该应用程序没有最高 API 级别的限制。

优秀的个人博客,低调大师

Apache Spark技术实战(二)KafkaWordCount &amp;PackratParsers实例 &amp;Spark Cassandra C...

<一>KafkaWordCount 概要 Spark应用开发实践性非常强,很多时候可能都会将时间花费在环境的搭建和运行上,如果有一个比较好的指导将会大大的缩短应用开发流程。Spark Streaming中涉及到和许多第三方程序的整合,源码中的例子如何真正跑起来,文档不是很多也不详细。 本篇主要讲述如何运行KafkaWordCount,这个需要涉及Kafka集群的搭建,还是说的越仔细越好。 搭建Kafka集群 步骤1:下载kafka 0.8.1及解压 wget https://www.apache.org/dyn/closer.cgi?path=/kafka/0.8.1.1/kafka_2.10-0.8.1.1.tgz tar zvxf kafka_2.10-0.8.1.1.tgz cd kafka_2.10-0.8.1.1 步骤2:启动zookeeper bin/zookeeper-server-start.sh config/zookeeper.properties 步骤3:修改配置文件config/server.properties,添加如下内容 host.name=localhost # Hostname the broker will advertise to producers and consumers. If not set, it uses the # value for "host.name" if configured. Otherwise, it will use the value returned from # java.net.InetAddress.getCanonicalHostName(). advertised.host.name=localhost 步骤4:启动Kafka server bin/kafka-server-start.sh config/server.properties 步骤5:创建topic bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test 检验topic创建是否成功 bin/kafka-topics.sh --list --zookeeper localhost:2181 如果正常返回test 步骤6:打开producer,发送消息 bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test ##启动成功后,输入以下内容测试 This is a message This is another message 步骤7:打开consumer,接收消息 bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topic test --from-beginning ###启动成功后,如果一切正常将会显示producer端输入的内容 This is a message This is another message 运行KafkaWordCount KafkaWordCount源文件位置 :examples/src/main/scala/org/apache/spark/examples/streaming/KafkaWordCount.scala 尽管里面有使用说明,见下文,但如果不是事先对Kafka有一定的了解的话,决然不知道这些参数是什么意思,也不知道该如何填写。 /** * Consumes messages from one or more topics in Kafka and does wordcount. * Usage: KafkaWordCount * is a list of one or more zookeeper servers that make quorum * is the name of kafka consumer group * is a list of one or more kafka topics to consume from * is the number of threads the kafka consumer should use * * Example: * `$ bin/run-example \ * org.apache.spark.examples.streaming.KafkaWordCount zoo01,zoo02,zoo03 \ * my-consumer-group topic1,topic2 1` */ object KafkaWordCount { def main(args: Array[String]) { if (args.length < 4) { System.err.println("Usage: KafkaWordCount ") System.exit(1) } StreamingExamples.setStreamingLogLevels() val Array(zkQuorum, group, topics, numThreads) = args val sparkConf = new SparkConf().setAppName("KafkaWordCount") val ssc = new StreamingContext(sparkConf, Seconds(2)) ssc.checkpoint("checkpoint") val topicpMap = topics.split(",").map((_,numThreads.toInt)).toMap val lines = KafkaUtils.createStream(ssc, zkQuorum, group, topicpMap).map(_._2) val words = lines.flatMap(_.split(" ")) val wordCounts = words.map(x => (x, 1L)) .reduceByKeyAndWindow(_ + _, _ - _, Minutes(10), Seconds(2), 2) wordCounts.print() ssc.start() ssc.awaitTermination() } } 讲清楚了写这篇博客的主要原因之后,来看一看该如何运行KafkaWordCount 步骤1:停止运行刚才的kafka-console-producer和kafka-console-consumer 步骤2:运行KafkaWordCountProducer bin/run-example org.apache.spark.examples.streaming.KafkaWordCountProducer localhost:9092 test 3 5 解释一下参数的意思,localhost:9092表示producer的地址和端口, test表示topic,3表示每秒发多少条消息,5表示每条消息中有几个单词 步骤3:运行KafkaWordCount bin/run-example org.apache.spark.examples.streaming.KafkaWordCount localhost:2181 test-consumer-group test 1 解释一下参数, localhost:2181表示zookeeper的监听地址,test-consumer-group表示consumer-group的名称,必须和$KAFKA_HOME/config/consumer.properties中的group.id的配置内容一致,test表示topic,1表示线程数。 <二>PackratParsers实例 概要 通过一个简明的Demo程序来说明如何使用scala中的PackratParsers DemoApp import scala.util.parsing.combinator.PackratParsers import scala.util.parsing.combinator.syntactical._ object Dotter extends StandardTokenParsers with PackratParsers { //定义分割符 lexical.delimiters ++= List(".",";","+","-","*") //合法的输入模式,支持加,减,乘 lazy val pgm : PackratParser[Int] = expr | minus|multiply //定义模式加 lazy val expr :PackratParser[Int]= num~"+"~num ^^ {case n1~"+"~n2 => n1.toInt + n2.toInt} //定义模式减 lazy val minus :PackratParser[Int]= num~"-"~num ^^ {case n1~"-"~n2 => n1.toInt - n2.toInt} lazy val multiply :PackratParser[Int]= num~"*"~num ^^ {case n1~"*"~n2 => n1.toInt * n2.toInt} lazy val num = numericLit def parse(input: String) = phrase(pgm)(new PackratReader(new lexical.Scanner(input))) match { case Success(result, _) => println("Success!"); println(result);Some(result) case n @ _ => println(n);println("bla"); None } def main(args: Array[String]) { //定义list,::表示添加,Nil表示list结束 val prg = "12*2"::"24-4"::"3+5"::Nil prg.map(parse) } } parser中的表达式说明 A<~B 只保留左侧内容 A<~B 只保留A A~>B 只保留右侧内容 A~>B 只保留B ^^ 根据匹配结果生成语法短语 ^^^ 将语法短语转换成为另外的值,注意与^^的区别 ~ 连接符 A ̃B 表示模式匹配是B紧跟于A之后 | 或者 A|B 表示模式要么由A组成,要么由B组成 编译执行 将上述源码保存到文件dotter.scala。 编译 scalac dotter.scala 执行 scala -cp . Dotter <三>Spark Cassandra Connector的安装和使用 前提 假设当前已经安装好如下软件: jdk sbt git scala 安装cassandra 以archlinux为例,使用如下指令来安装Cassandra: yaourt -S cassandra 启动cassandra: cassandra -f 创建keyspace和table, 运行/usr/bin/cqlsh进入cql console,然后执行下述语句创建keyspace和table: CREATE KEYSPACE test WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1 }; CREATE TABLE test.kv(key text PRIMARY KEY, value int); 添加记录,继续使用cql console: INSERT INTO test.kv(key, value) VALUES ('key1', 1); INSERT INTO test.kv(key, value) VALUES ('key2', 2); 验证记录已经插入成功,执行如下cql: select * from test.kv; 下载编译spark-cassandra-connector 下载最新的spark-cassandra-connector源码: git clone https://github.com/datastax/spark-cassandra-connector.git 编译: sbt package 这中间要等待比较长的时间,请保持足够的耐心。 运行spark-shell 首先请确保cassandra已经正常安装和运行,如有问题请返回开始的章节安装Cassandra。 如何添加相应的library来支持spark-cassandra-connector,并没有一个明确的文档说明,折腾了一个下午,终于弄出了一个最简的配置。 bin/spark-shell --driver-class-path /root/working/spark-cassandra-connector/spark-cassandra-connector/target/scala-2.10/spark-cassandra-connector_2.10-1.1.0-SNAPSHOT.jar: /root/.ivy2/cache/org.apache.cassandra/cassandra-thrift/jars/cassandra-thrift-2.0.9.jar: /root/.ivy2/cache/org.apache.thrift/libthrift/jars/libthrift-0.9.1.jar: /root/.ivy2/cache/org.apache.cassandra/cassandra-clientutil/jars/cassandra-clientutil-2.0.9.jar: /root/.ivy2/cache/com.datastax.cassandra/cassandra-driver-core/jars/cassandra-driver-core-2.0.4.jar: /root/.ivy2/cache/io.netty/netty/bundles/netty-3.9.0.Final.jar: /root/.ivy2/cache/com.codahale.metrics/metrics-core/bundles/metrics-core-3.0.2.jar: /root/.ivy2/cache/org.slf4j/slf4j-api/jars/slf4j-api-1.7.7.jar: /root/.ivy2/cache/org.apache.commons/commons-lang3/jars/commons-lang3-3.3.2.jar: /root/.ivy2/cache/org.joda/joda-convert/jars/joda-convert-1.2.jar: /root/.ivy2/cache/joda-time/joda-time/jars/joda-time-2.3.jar: /root/.ivy2/cache/org.apache.cassandra/cassandra-all/jars/cassandra-all-2.0.9.jar: /root/.ivy2/cache/org.slf4j/slf4j-log4j12/jars/slf4j-log4j12-1.7.2.jar 上述指令假设spark-cassandra-connector的源码是下载在$HOME/working目录下,请根据自己的情况作适当修改。 我是如何猜测到需要指定这些包依赖的呢?说白了,也很简单,就是执行以下指令,然后再查看相就的java进程中的运行参数。 #运行spark-cassandra-connector测试集 sbt test sbt it:test 当上述指令还在运行的时候,使用ps来查看java运行的参数,这样就反过来知道所需要的包依赖了。 ps -ef|grep -i java 测试程序 由于spark-shell会默认创建sc,所以首先需要停止掉默认的sc,然后利用新的配置来创建可以连接到cassandra的sc,示例代码如下: sc.stop import com.datastax.spark.connector._ import org.apache.spark._ val conf = new SparkConf() conf.set("spark.cassandra.connection.host", "127.0.0.1") val sc = new SparkContext("local[2]", "Cassandra Connector Test", conf) val table = sc.cassandraTable("test", "kv") table.count 如果一切正常会显示出如下结果: res3: Long = 2 小结 进入实战阶段,挑战会越来越多,保持足够的信心和耐心很重要。 本篇内容和实战一中的kafka cluster组织在一起的话,就会形成一个从前台到后台存储的完整处理链条。

优秀的个人博客,低调大师

从头学起android&amp;lt;android基本的绘图.四十六.&amp;gt;

在一般的图形渲染用户通常只需要重写onDraw()该方法可以是。但是假设,才能真正完成绘图操作。此外,我们需要掌握的四大核心经营类: android.graphics.Bitmap:主要表示的是一个图片的存储空间。所包括的图片能够来自于文件或由程序创建; android.graphics.Paint:基本的画图工具类,能够指定画图的样式; android.graphics.Canvas:是一个操作画图以及Bitmap的平台,相当于提供了一个画板的功能,在onDraw()方法的參数中也定义了此类型的參数,能够依靠此类完毕详细的画图操作。 android.graphics.drawable.Drawable:绘制图形的公共父类,能够绘制各种图形、图层等。 自己定义MyView组件 <span style="font-size:18px;"><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" > <com.example.paintpic.MyView android:id="@+id/Myview" android:layout_width="wrap_content" android:layout_height="wrap_content" ></com.example.paintpic.MyView> </LinearLayout> </span> 主界面 <span style="font-size:18px;">package com.example.paintpic; import android.os.Bundle; import android.app.Activity; import android.view.Menu; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } } </span> MyView设置 <span style="font-size:18px;">package com.example.paintpic; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View; public class MyView extends View { public MyView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub // canvas是画板对象,背景是白色的 canvas.drawColor(Color.WHITE); // paint画笔工具 Paint paint = new Paint(); // 设置画笔颜色为蓝色 paint.setColor(Color.BLUE); // 为画板上画圆。圆心(50,50)半径50 canvas.drawCircle(50, 50, 50, paint); // 设置画笔颜色的颜色为红色 paint.setColor(Color.RED); // 画矩形 canvas.drawRect(100, 0, 160, 80, paint); // 画笔颜色为绿色 paint.setColor(Color.GREEN); // 画直线 canvas.drawLine(50, 110, 200, 110, paint); paint.setColor(Color.YELLOW); // Rect rect=new Rect(120, 120, 300, 300); // canvas.drawRect(rect, paint); // 使用RectF画多边形 RectF oval = new RectF(); oval.set(10.0f, 140.0f, 110.0f, 200.0f); canvas.drawOval(oval, paint); oval = new RectF(); oval.set(150.0f, 140.0f, 210.0f, 200.0f); canvas.drawArc(oval, 150.0f, 140.0f, true, paint); } } </span> 使用Canvas和point组件能够绘制出各种各样的图形,多用于游戏的开发。感兴趣的朋友能够自己了解下 根据第预测:bitmap图形组件 版权声明:本文博客原创文章,博客,未经同意,不得转载。 本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/4628322.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

Spark的这些事&amp;lt;三&amp;gt;——spark常用的Transformations 和Actions

Transformations map,filter spark最长用的两个Transformations:map,filter,下面就来介绍一下这两个。 先看下面这张图: 从上图中可以清洗的看到 map和filter都是做的什么工作,那我们就代码演示一下。 val input = sc.parallelize(List(1,2,3,4)) val result1 = input.map(x=>x*x) val result2 = input.filter(x=>x!=1) print(result1.collect().mkString(",")) print("\n") print(result2.collect().mkString(",")) print("\n") 执行结果如下: 16/08/17 18:48:31 INFO DAGScheduler: ResultStage 0 (collect at Map.scala:17) finished in 0.093 s 16/08/17 18:48:31 INFO DAGScheduler: Job 0 finished: collect at Map.scala:17, took 0.268871 s 1,4,9,16 ........ 16/08/17 18:48:31 INFO DAGScheduler: ResultStage 1 (collect at Map.scala:19) finished in 0.000 s 16/08/17 18:48:31 INFO DAGScheduler: Job 1 finished: collect at Map.scala:19, took 0.018291 s 2,3,4 再回头看下上面那张图,是不是明白什么意思了! flatMap 另外一个常用的就是flatMap,输入一串字符,分割出每个字符 来用代码实践一下: val lines = sc.parallelize(List("hello world","hi")) val words = lines.flatMap (lines=>lines.split(" ")) print(words.first()) print("\n") 执行结果: 16/08/17 19:23:24 INFO DAGScheduler: Job 2 finished: first at Map.scala:24, took 0.016987 s hello 16/08/17 19:23:24 INFO SparkContext: Invoking stop() from shutdown hook 分隔符如果改一下的话: val words = lines.flatMap (lines=>lines.split(",")) 结果会怎样呢? 16/08/17 19:33:14 INFO TaskSchedulerImpl: Removed TaskSet 2.0, whose tasks have all completed, from pool hello world 16/08/17 19:33:14 INFO SparkContext: Invoking stop() from shutdown hook 和想象的一样吧~ distinct,distinct,intersection,subtract 还有几个比较常用的:distinct,distinct,intersection,subtract 来看看代码实践: val rdd1 = sc.parallelize(List("coffee","coffee","panda","monkey","tea")) val rdd2 = sc.parallelize(List("coffee","monkey","kitty")) rdd1.distinct().take(100).foreach(println) 结果: 16/08/17 19:52:29 INFO DAGScheduler: ResultStage 4 (take at Map.scala:30) finished in 0.047 s 16/08/17 19:52:29 INFO TaskSchedulerImpl: Removed TaskSet 4.0, whose tasks have all completed, from pool 16/08/17 19:52:29 INFO DAGScheduler: Job 3 finished: take at Map.scala:30, took 0.152405 s monkey coffee panda tea 16/08/17 19:52:29 INFO SparkContext: Starting job: take at Map.scala:32 代码: rdd1.union(rdd2).take(100).foreach(println) 结果: 6/08/17 19:52:29 INFO DAGScheduler: Job 5 finished: take at Map.scala:32, took 0.011825 s coffee coffee panda monkey tea coffee monkey kitty 16/08/17 19:52:30 INFO SparkContext: Starting job: take at Map.scala:34 16/08/17 19:52:30 INFO DAGScheduler: Registering RDD 11 (intersection at Map.scala:34) 16/08/17 19:52:30 INFO DAGScheduler: Registering RDD 12 (intersection at Map.scala:34) 代码: rdd1.intersection(rdd2).take(100).foreach(println) 结果: 16/08/17 19:52:30 INFO TaskSetManager: Finished task 0.0 in stage 9.0 (TID 9) in 31 ms on localhost (1/1) 16/08/17 19:52:30 INFO TaskSchedulerImpl: Removed TaskSet 9.0, whose tasks have all completed, from pool 16/08/17 19:52:30 INFO DAGScheduler: ResultStage 9 (take at Map.scala:34) finished in 0.031 s 16/08/17 19:52:30 INFO DAGScheduler: Job 6 finished: take at Map.scala:34, took 0.060785 s monkey coffee 16/08/17 19:52:30 INFO SparkContext: Starting job: take at Map.scala:36 代码: rdd1.subtract(rdd2).take(100).foreach(println) 结果: 16/08/17 19:52:30 INFO DAGScheduler: Job 6 finished: take at Map.scala:34, took 0.060785 s monkey coffee 16/08/17 19:52:30 INFO SparkContext: Starting job: take at Map.scala:36 再看看上面的图,很容易理解吧 Actions 常用的Transformations就介绍到这里,下面介绍下常用的Action: reduce,countByValue,takeOrdered,takeSample,aggregate 首先看一下:reduce val rdd5 = sc.parallelize(List(1,2,3,4)) print("reduce action:"+rdd5.reduce((x,y)=>x+y)+"\n") 16/08/18 11:51:16 INFO DAGScheduler: Job 15 finished: reduce at Function.scala:55, took 0.012698 s reduce action:10 16/08/18 11:51:16 INFO SparkContext: Starting job: aggregate at Function.scala:57 countByValue print(rdd1.countByValue() + "\n") 16/08/18 11:51:16 INFO DAGScheduler: Job 11 finished: countByValue at Function.scala:48, took 0.031726 s Map(monkey -> 1, coffee -> 2, panda -> 1, tea -> 1) 16/08/18 11:51:16 INFO SparkContext: Starting job: takeOrdered at Function.scala:50 takeOrdered rdd1.takeOrdered(10).take(100).foreach(println) 16/08/18 11:51:16 INFO DAGScheduler: Job 12 finished: takeOrdered at Function.scala:50, took 0.026160 s coffee coffee monkey panda tea 16/08/18 11:51:16 INFO SparkContext: Starting job: takeSample at Function.scala:52 aggregate 这个要重点介绍一下: Spark文档中aggregate函数定义如下 def aggregate[U](zeroValue: U)(seqOp: (U, T) ⇒ U, combOp: (U, U) ⇒ U)(implicit arg0: ClassTag[U]): U Aggregate the elements of each partition, and then the results for all the partitions, using given combine functions and a neutral “zero value”. This function can return a different result type, U, than the type of this RDD, T. Thus, we need one operation for merging a T into an U and one operation for merging two U’s, as in scala.TraversableOnce. Both of these functions are allowed to modify and return their first argument instead of creating a new U to avoid memory allocation. seqOp操作会聚合各分区中的元素,然后combOp操作把所有分区的聚合结果再次聚合,两个操作的初始值都是zeroValue. seqOp的操作是遍历分区中的所有元素(T),第一个T跟zeroValue做操作,结果再作为与第二个T做操作的zeroValue,直到遍历完整个分区。combOp操作是把各分区聚合的结果,再聚合。aggregate函数返回一个跟RDD不同类型的值。因此,需要一个操作seqOp来把分区中的元素T合并成一个U,另外一个操作combOp把所有U聚合。 val rdd5 = sc.parallelize(List(1,2,3,4)) val rdd6 = rdd5.aggregate((0, 0)) ((x, y) =>(x._1 + y, x._2+1), (x, y) =>(x._1 + y._1, x._2 + y._2)) print ("aggregate action : " + rdd6 + "\n" ) 我们看一下结果: 16/08/18 11:51:16 INFO DAGScheduler: Job 16 finished: aggregate at Function.scala:57, took 0.011686 s aggregate action : (10,4) 16/08/18 11:51:16 INFO SparkContext: Invoking stop() from shutdown hook 我们可以根据以上执行的例子来理解aggregate 用法: 第一步:将rdd5中的元素与初始值遍历进行聚合操作 第二步:将初始值加1进行遍历聚合 第三步:将结果进行聚合 根据本次的RDD 背部实现如下: 第一步:其实是0+1 1+2 3+3 6+4 然后执行:0+1 1+1 2+1 3+1 此时返回(10,4) 本次执行是一个节点,如果在集群中的话,多个节点,会先把数据打到不同的分区上,比如(1,2) (3,4) 得到的结果就会是(3,2) (7,2) 然后进行第二步combine就得到 (10,4) 这样你应该能理解aggregate这个函数了吧 以上就是对常用的Transformations 和Actions介绍,对于初学者来说,动手代码实践各个函数,才是明白其功能最好的方法。 PS :源码 Spark的这些事系列文章: Spark的这些事<一>——Windows下spark开发环境搭建 Spark的这些事<二>——几个概念 Spark的这些事<三>——spark常用的Transformations 和Actions

优秀的个人博客,低调大师

Spark的这些事&amp;lt;一&amp;gt;——Windows下spark开发环境搭建

一、首先准备需要安装的软件 scala-2.10.4 下载地址:http://www.scala-lang.org/download/2.10.4.html scala-SDK-4.4.1-vfinal-2.11-win32.win32.x86_64 下载地址:http://scala-ide.org/ spark-1.6.2-bin-hadoop2.6 下载地址:http://spark.apache.org/ 当然还有jdk这里就不说了 scala-2.10.4下载后直接安装~ scala-SDK-4.4.1-vfinal-2.11-win32.win32.x86_64和spark-1.6.2-bin-hadoop2.6下载后直接解压 scalaIDE解压后就是一个eclipse,这个大家都比较熟悉了。打开IDE,在解压后的spark包中的lib文件夹下找到spark-assembly-1.6.2-hadoop2.6.0,添加到IDE中。 然后环境就搭建完成了~ 下面就来开发一个测试程序试一下: package com.day1.spark import org.apache.spark.SparkConf import org.apache.spark.SparkContext object WordCount { def main(args: Array[String]): Unit = { val conf = new SparkConf() conf.setAppName("MY frist Spark App") conf.setMaster("local") var sc = new SparkContext(conf) val lines = sc.textFile("D:\\test.txt", 1) val words = lines.flatMap { line => line.split(" ") } val pairs = words.map { word => (word,1) } val WordCount = pairs.reduceByKey(_+_) WordCount.foreach(wordNumberPair=>println(wordNumberPair._1 + ":" + wordNumberPair._2)) sc.stop() } } 右键运行: Using Spark's default log4j profile: org/apache/spark/log4j-defaults.properties 16/07/18 19:07:04 INFO SparkContext: Running Spark version 1.6.2 16/07/18 19:07:13 INFO SecurityManager: Changing view acls to: qinchaofeng 16/07/18 19:07:13 INFO SecurityManager: Changing modify acls to: qinchaofeng 16/07/18 19:07:13 INFO SecurityManager: SecurityManager: authentication disabled; ui acls disabled; users with view permissions: Set(qinchaofeng); users with modify permissions: Set(qinchaofeng) 16/07/18 19:07:14 INFO Utils: Successfully started service 'sparkDriver' on port 50513. 16/07/18 19:07:14 INFO Slf4jLogger: Slf4jLogger started 16/07/18 19:07:14 INFO Remoting: Starting remoting 16/07/18 19:07:14 INFO Remoting: Remoting started; listening on addresses :[akka.tcp://sparkDriverActorSystem@192.168.108.207:50526] 16/07/18 19:07:14 INFO Utils: Successfully started service 'sparkDriverActorSystem' on port 50526. 16/07/18 19:07:14 INFO SparkEnv: Registering MapOutputTracker 16/07/18 19:07:14 INFO SparkEnv: Registering BlockManagerMaster 16/07/18 19:07:14 INFO DiskBlockManager: Created local directory at C:\Users\qinchaofeng\AppData\Local\Temp\blockmgr-25c047c4-505d-4cfb-addd-d777e5a430d7 16/07/18 19:07:14 INFO MemoryStore: MemoryStore started with capacity 797.6 MB 16/07/18 19:07:14 INFO SparkEnv: Registering OutputCommitCoordinator 16/07/18 19:07:15 INFO Utils: Successfully started service 'SparkUI' on port 4040. 16/07/18 19:07:15 INFO SparkUI: Started SparkUI at http://192.168.108.207:4040 16/07/18 19:07:15 INFO Executor: Starting executor ID driver on host localhost 16/07/18 19:07:15 INFO Utils: Successfully started service 'org.apache.spark.network.netty.NettyBlockTransferService' on port 50533. 16/07/18 19:07:15 INFO NettyBlockTransferService: Server created on 50533 16/07/18 19:07:15 INFO BlockManagerMaster: Trying to register BlockManager 16/07/18 19:07:15 INFO BlockManagerMasterEndpoint: Registering block manager localhost:50533 with 797.6 MB RAM, BlockManagerId(driver, localhost, 50533) 16/07/18 19:07:15 INFO BlockManagerMaster: Registered BlockManager 16/07/18 19:07:15 INFO MemoryStore: Block broadcast_0 stored as values in memory (estimated size 153.6 KB, free 153.6 KB) 16/07/18 19:07:15 INFO MemoryStore: Block broadcast_0_piece0 stored as bytes in memory (estimated size 13.9 KB, free 167.5 KB) 16/07/18 19:07:15 INFO BlockManagerInfo: Added broadcast_0_piece0 in memory on localhost:50533 (size: 13.9 KB, free: 797.6 MB) 16/07/18 19:07:15 INFO SparkContext: Created broadcast 0 from textFile at WordCount.scala:15 16/07/18 19:07:16 WARN : Your hostname, qinchaofeng1 resolves to a loopback/non-reachable address: fe80:0:0:0:0:5efe:c0a8:6ccf%12, but we couldn't find any external IP address! 16/07/18 19:07:24 INFO FileInputFormat: Total input paths to process : 1 16/07/18 19:07:24 INFO SparkContext: Starting job: foreach at WordCount.scala:23 16/07/18 19:07:24 INFO DAGScheduler: Registering RDD 3 (map at WordCount.scala:19) 16/07/18 19:07:24 INFO DAGScheduler: Got job 0 (foreach at WordCount.scala:23) with 1 output partitions 16/07/18 19:07:24 INFO DAGScheduler: Final stage: ResultStage 1 (foreach at WordCount.scala:23) 16/07/18 19:07:24 INFO DAGScheduler: Parents of final stage: List(ShuffleMapStage 0) 16/07/18 19:07:24 INFO DAGScheduler: Missing parents: List(ShuffleMapStage 0) 16/07/18 19:07:24 INFO DAGScheduler: Submitting ShuffleMapStage 0 (MapPartitionsRDD[3] at map at WordCount.scala:19), which has no missing parents 16/07/18 19:07:24 INFO MemoryStore: Block broadcast_1 stored as values in memory (estimated size 4.1 KB, free 171.6 KB) 16/07/18 19:07:25 INFO MemoryStore: Block broadcast_1_piece0 stored as bytes in memory (estimated size 2.3 KB, free 173.8 KB) 16/07/18 19:07:25 INFO BlockManagerInfo: Added broadcast_1_piece0 in memory on localhost:50533 (size: 2.3 KB, free: 797.6 MB) 16/07/18 19:07:25 INFO SparkContext: Created broadcast 1 from broadcast at DAGScheduler.scala:1006 16/07/18 19:07:25 INFO DAGScheduler: Submitting 1 missing tasks from ShuffleMapStage 0 (MapPartitionsRDD[3] at map at WordCount.scala:19) 16/07/18 19:07:25 INFO TaskSchedulerImpl: Adding task set 0.0 with 1 tasks 16/07/18 19:07:25 INFO TaskSetManager: Starting task 0.0 in stage 0.0 (TID 0, localhost, partition 0,PROCESS_LOCAL, 2108 bytes) 16/07/18 19:07:25 INFO Executor: Running task 0.0 in stage 0.0 (TID 0) 16/07/18 19:07:25 INFO HadoopRDD: Input split: file:/D:/test.txt:0+214 16/07/18 19:07:25 INFO deprecation: mapred.tip.id is deprecated. Instead, use mapreduce.task.id 16/07/18 19:07:25 INFO deprecation: mapred.task.id is deprecated. Instead, use mapreduce.task.attempt.id 16/07/18 19:07:25 INFO deprecation: mapred.task.is.map is deprecated. Instead, use mapreduce.task.ismap 16/07/18 19:07:25 INFO deprecation: mapred.task.partition is deprecated. Instead, use mapreduce.task.partition 16/07/18 19:07:25 INFO deprecation: mapred.job.id is deprecated. Instead, use mapreduce.job.id 16/07/18 19:07:25 INFO Executor: Finished task 0.0 in stage 0.0 (TID 0). 2253 bytes result sent to driver 16/07/18 19:07:25 INFO TaskSetManager: Finished task 0.0 in stage 0.0 (TID 0) in 156 ms on localhost (1/1) 16/07/18 19:07:25 INFO TaskSchedulerImpl: Removed TaskSet 0.0, whose tasks have all completed, from pool 16/07/18 19:07:25 INFO DAGScheduler: ShuffleMapStage 0 (map at WordCount.scala:19) finished in 0.156 s 16/07/18 19:07:25 INFO DAGScheduler: looking for newly runnable stages 16/07/18 19:07:25 INFO DAGScheduler: running: Set() 16/07/18 19:07:25 INFO DAGScheduler: waiting: Set(ResultStage 1) 16/07/18 19:07:25 INFO DAGScheduler: failed: Set() 16/07/18 19:07:25 INFO DAGScheduler: Submitting ResultStage 1 (ShuffledRDD[4] at reduceByKey at WordCount.scala:21), which has no missing parents 16/07/18 19:07:25 INFO MemoryStore: Block broadcast_2 stored as values in memory (estimated size 2.5 KB, free 176.3 KB) 16/07/18 19:07:25 INFO MemoryStore: Block broadcast_2_piece0 stored as bytes in memory (estimated size 1583.0 B, free 177.9 KB) 16/07/18 19:07:25 INFO BlockManagerInfo: Added broadcast_2_piece0 in memory on localhost:50533 (size: 1583.0 B, free: 797.6 MB) 16/07/18 19:07:25 INFO SparkContext: Created broadcast 2 from broadcast at DAGScheduler.scala:1006 16/07/18 19:07:25 INFO DAGScheduler: Submitting 1 missing tasks from ResultStage 1 (ShuffledRDD[4] at reduceByKey at WordCount.scala:21) 16/07/18 19:07:25 INFO TaskSchedulerImpl: Adding task set 1.0 with 1 tasks 16/07/18 19:07:25 INFO TaskSetManager: Starting task 0.0 in stage 1.0 (TID 1, localhost, partition 0,NODE_LOCAL, 1894 bytes) 16/07/18 19:07:25 INFO Executor: Running task 0.0 in stage 1.0 (TID 1) 16/07/18 19:07:25 INFO ShuffleBlockFetcherIterator: Getting 1 non-empty blocks out of 1 blocks 16/07/18 19:07:25 INFO ShuffleBlockFetcherIterator: Started 0 remote fetches in 16 ms avg_flow:1 avg_flow_fee:1 local_city_nm:1 cust_num:1 birthday:1 Gender:1 cust_nm:1 join_net_time:1 avg_call_fee:1 net_age:1 prov_code:1 cmpgn:1 buy_time:1 using_meal:1 chnl:1 arpu:1 term_brand:1 term_model:1 avg_call_times:1 flow_pkg:1 16/07/18 19:07:25 INFO Executor: Finished task 0.0 in stage 1.0 (TID 1). 1165 bytes result sent to driver 16/07/18 19:07:25 INFO DAGScheduler: ResultStage 1 (foreach at WordCount.scala:23) finished in 0.062 s 16/07/18 19:07:25 INFO TaskSetManager: Finished task 0.0 in stage 1.0 (TID 1) in 62 ms on localhost (1/1) 16/07/18 19:07:25 INFO TaskSchedulerImpl: Removed TaskSet 1.0, whose tasks have all completed, from pool 16/07/18 19:07:25 INFO DAGScheduler: Job 0 finished: foreach at WordCount.scala:23, took 0.356812 s 16/07/18 19:07:25 INFO SparkUI: Stopped Spark web UI at http://192.168.108.207:4040 16/07/18 19:07:25 INFO MapOutputTrackerMasterEndpoint: MapOutputTrackerMasterEndpoint stopped! 16/07/18 19:07:25 INFO MemoryStore: MemoryStore cleared 16/07/18 19:07:25 INFO BlockManager: BlockManager stopped 16/07/18 19:07:25 INFO BlockManagerMaster: BlockManagerMaster stopped 16/07/18 19:07:25 INFO OutputCommitCoordinator$OutputCommitCoordinatorEndpoint: OutputCommitCoordinator stopped! 16/07/18 19:07:25 INFO SparkContext: Successfully stopped SparkContext 16/07/18 19:07:25 INFO ShutdownHookManager: Shutdown hook called 16/07/18 19:07:25 INFO RemoteActorRefProvider$RemotingTerminator: Shutting down remote daemon. 16/07/18 19:07:25 INFO ShutdownHookManager: Deleting directory C:\Users\qinchaofeng\AppData\Local\Temp\spark-c54f4cb1-7351-47d1-9fd4-fbdac0fae228 可以看到已经执行成功了! 源码 Spark的这些事系列文章: Spark的这些事<一>——Windows下spark开发环境搭建 Spark的这些事<二>——几个概念 Spark的这些事<三>——spark常用的Transformations 和Actions

优秀的个人博客,低调大师

K8S的名称空间创建&amp;&amp;版本的升级、回滚操作

1、创建私有仓库 [root@docker-k8s01 ~]# docker run -itd --name registry -p 5000:5000 --restart always registry:latest #在所有需要使用私有仓库的docker主机上添加如下配置 [root@docker-k8s01 ~]# vim /usr/lib/systemd/system/docker.service ExecStart=/usr/bin/dockerd -H unix:// --insecure-registry 192.168.171.151:5000 #修改完文件后重启使之生效 [root@docker-k8s01 ~]# systemctl daemon-reload [root@docker-k8s01 ~]# systemctl restart docker 2、在master节点,自定义一个镜像,基于nginx镜像,默认界面内容改为:Version:v1,版本2内容为:Version:v2.版本3内容为:Version:v3 #编写dockerfile文件 [root@docker-k8s01 ~]# vim Dockerfile FROM nginx ADD index.html /usr/share/nginx/html #编辑v1版本的首页内容 [root@docker-k8s01 ~]# echo "Version:v1" > index.html #制作v1版本的镜像 [root@docker-k8s01 ~]# docker build -t 192.168.171.151:5000/zyz:v1 . #制作v2版本的镜像 [root@docker-k8s01 ~]# echo "Version:v2" > index.html [root@docker-k8s01 ~]# docker build -t 192.168.171.151:5000/zyz:v2 . #制作v3版本的镜像 [root@docker-k8s01 ~]# echo "Version:v3" > index.html [root@docker-k8s01 ~]# docker build -t 192.168.171.151:5000/zyz:v3 . #将制作好的三个版本的镜像上传至私有仓库 [root@docker-k8s01 ~]# docker push 192.168.171.151:5000/zyz:v1 [root@docker-k8s01 ~]# docker push 192.168.171.151:5000/zyz:v2 [root@docker-k8s01 ~]# docker push 192.168.171.151:5000/zyz:v3 创建一个Namespace.接下来的所有操作都在此名称空间之下 [root@docker-k8s01 ~]# mkdir test [root@docker-k8s01 ~]# cd test/ [root@docker-k8s01 test]# vim ns.yaml apiVersion: v1 kind: Namespace metadata: name: zyz [root@docker-k8s01 test]# kubectl apply -f ns.yaml # 运行yaml文件 namespace/zyz created [root@docker-k8s01 test]# kubectl get ns zyz # 查看创建的namespace NAME STATUS AGE zyz Active 30s 创建一个Deployment资源对象,镜像版本为v1 [root@docker-k8s01 test]# vim zyz.yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: name: zyz namespace: zyz spec: revisionHistoryLimit: 5 # 规定记录多少个版本,这个字段通过 kubectl explain deploy.spec 命令找到revisionHistoryLimit <integer>行获得 replicas: 2 template: metadata: labels: name: zyz spec: containers: - name: zyz image: 192.168.171.151:5000/zyz:v1 # 镜像版本为1 ports: - containerPort: 80 #执行该yaml文件, --record表示记录版本历史 [root@docker-k8s01 test]# kubectl apply -f zyz.yaml --record [root@docker-k8s01 test]# kubectl get pod # 查看上面yaml文件运行的pod No resources found. #可以得出结论,如果在编写yaml文件时,指定了归属于哪个namespace, #那么执行上面的命令是查看不到运行的pod的,而不是没有运行的pod [root@docker-k8s01 test]# kubectl get pod -n zyz # 增加“-n”选项,指定名称空间,即可看对应的pod NAME READY STATUS RESTARTS AGE zyz-5d7cb64655-9pdcj 1/1 Running 0 98s zyz-5d7cb64655-zzqcv 1/1 Running 0 98s #查看zyz的namespace名称空间的名为zyz的deployment资源对象 [root@docker-k8s01 test]# kubectl rollout history deployment -n zyz zyz deployment.extensions/zyz REVISION CHANGE-CAUSE 1 kubectl apply --filename=zyz.yaml --record=true #可以看到当前只有一个版本,也可以看到filename(文件名称) 创建一个Service资源对象,关联到上面的Deployment资源对象 [root@docker-k8s01 test]# vim zyz-svc.yaml apiVersion: v1 kind: Service metadata: name: zyz-service namespace: zyz spec: type: NodePort selector: name: zyz ports: - name: zyz-port port: 8080 # service的IP接口 targetPort: 80 # pod的端口 nodePort: 31111 # 映射到宿主机的端口 [root@docker-k8s01 test]# kubectl apply -f zyz-svc.yaml [root@docker-k8s01 test]# kubectl get svc # 若不指定名称空间,是查不到对应的service的 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 9d [root@docker-k8s01 test]# kubectl get svc -n zyz # 使用“-n”选项查看指定的名称空间中的service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE zyz-service NodePort 10.100.89.145 <none> 8080:31111/TCP 55s 注意:创建的service资源对象必须和创建的deployment资源对象在同一个namespace中,否则无法进行关联!!! 客户端访问k8s群集中的任意一个节点的31111端口访问其pod提供的服务如果需要在线修改pod提供的网页文件,可以先查看到pod的name,然后直接在主节点上登录到此pod即可,命令如下 [root@docker-k8s01 httpd-web]# kubectl get pod -o wide #查看pod的name NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES httpd-devploy1-6f987c9764-5g92w 1/1 Running 0 8m35s 10.244.1.5 node01 <none> <none> httpd-devploy1-6f987c9764-wvgft 1/1 Running 0 [root@docker-k8s01 httpd-web]# kubectl exec -it httpd-devploy1-6f987c9764-5g92w /bin/bash #通过指定pod的name,进入pod 现在由1版本,滚动更新至2版本、再到3版本,最后回滚到指定1版本内容 [root@docker-k8s01 test]# sed -i 's/zyz:v1/zyz:v2/' zyz.yaml # 更新为版本2 [root@docker-k8s01 test]# kubectl apply -f zyz.yaml # 执行 deployment.extensions/zyz configured # 配置成功 #访问验证 [root@docker-k8s01 test]# curl 192.168.171.151:31111 Version:v2 #查看历史版本 [root@docker-k8s01 test]# kubectl rollout history deployment -n zyz zyz deployment.extensions/zyz REVISION CHANGE-CAUSE 1 kubectl apply --filename=zyz.yaml --record=true 2 <none> #现在是有两个历史版本了 #接下来再次升级并验证 [root@docker-k8s01 test]# sed -i 's/zyz:v2/zyz:v3/' zyz.yaml [root@docker-k8s01 test]# kubectl apply -f zyz.yaml deployment.extensions/zyz configured #再次访问可以看到现在是版本3了 [root@docker-k8s01 test]# curl 192.168.171.151:31111 Version:v3 #查看历史版本信息 [root@docker-k8s01 test]# kubectl rollout history deployment -n zyz zyz deployment.extensions/zyz REVISION CHANGE-CAUSE 1 kubectl apply --filename=zyz.yaml --record=true 2 <none> 3 <none> #接下来执行回滚操作 #回滚到版本1,需要指定namespace,“--to-revision”是指定回滚到哪个版本 [root@docker-k8s01 test]# kubectl rollout undo deployment -n zyz zyz --to-revision=1 deployment.extensions/zyz rolled back [root@docker-k8s01 test]# curl 192.168.171.151:31111 Version:v1 [root@docker-k8s01 test]# kubectl rollout history deployment -n zyz zyz #再次查看历史记录,发现版本1变成了版本4,之前的1已经没有了 deployment.extensions/zyz REVISION CHANGE-CAUSE 2 <none> 3 <none> 4 kubectl apply --filename=zyz.yaml --record=true

优秀的个人博客,低调大师

Java并发编程笔记2-线程可见性&amp;线程封闭&amp;指令重排序

一.指令重排序 例子如下: public class Visibility1 { public static boolean ready; public static int number; } public class ReaderThread extends Thread { @Override public void run() { while (!Visibility1.ready){ Thread.yield(); System.out.println(Visibility1.number); } } } public class Test1 { public static void main(String[] args) { new ReaderThread().start(); Visibility1.number = 42; Visibility1.ready = true; } } 多次运行结果分别如下: 可以看到多次运行所得到三种结果,分别为0,42,没有输出结果。 程序一开始执行,默认将ready赋值为false,ready默认赋值为0,一开始执行时,在ReaderThread中符合循环条件,进入循环,遇到 Thread.yield();这语句是就是说当一个线程使用了这个方法之后,它就会把自己CPU执行的时间让掉,让自己或者其它的线程运行。如果自己先走出现结果为0的情况,因为,默认初始int的类型数据为0,如果让别的线程先走让这样就又回到了Main方法中的继续执行 Visibility1.number = 42;Visibility1.ready = true;这两条语句,问题来了,这两条语句会按照程序的顺序执行吗?答案是,并不一定的。这就涉及到指令重排序问题,也就是说 CPU一般采用流水线来执行指令。一个指令的执行被分成:取指、译码、访存、执行、写回、等若干个阶段。然后,多条指令可以同时存在于流水线中,同时被执行。 指令流水线并不是串行的,并不会因为一个耗时很长的指令在“执行”阶段呆很长时间,而导致后续的指令都卡在“执行”之前的阶段上。 重排序的目的是为了性能。 也就是说,有时候CPU认为 Visibility1.ready = true; 语句执行要快,所以让Visibility1.ready = true;先执行。这就导致了会出现了无输出结果。这就是涉及了指令重排序问题,虽然这里如果出现了指令重排序问题, 进入生产开发会造成严重的后果的。但是要注意一点,如下: 二.可见性 可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果。另一个线程马上就能看到。比如:用volatile修饰的变量,就会具有可见性。volatile修饰的变量不允许线程内部缓存和重排序,即直接修改内存。 所以对其他线程是可见的。但是这里需要注意一个问题,volatile只能让被他修饰内容具有可见性,但不能保证它具有原子性。比如 volatile int a = 0;之后有一个操作 a++;这个变量a具有可见性,但是a++ 依然是一个非原子操作,也就这这个操作同样存在线程安全问题。 先看如下程序: public class Visibility { private static boolean bChanged; public static void main(String[] args) throws InterruptedException { new Thread(){ @Override public void run() { for (; ; ){ if (bChanged == true){ System.out.println("!="); System.exit(0); } } } }.start(); Thread.sleep(10); new Thread(){ @Override public void run() { for (; ; ){ bChanged = true; } } }.start(); } } 运行结果为: 可以看到运行为死循环。按道理来说应该会输出!=结束的。但是为什么会出现死循环呢?原因是因为程序中bChanged如果没有手动赋值前,默认是false的,所以一开始没输出,但是第二线程命名修改了bChanged的值了,为什么还不退出呢,因为线程是CPU启动的,而CPU一开始从主存中取数据并没有立即将数据送到CPU,而是先送到了缓存中,而,另外一个线程修改bChanged的值,是就该主存中的值,而那个输出结果的线程并没有从主存中取bChanged的值,而是去缓存中取了bChanged的值,而缓存中bChanged的值是false,这就是为什么会死循环的原因。如下图: 解决办法: 1.在成员变量中加入关键字volatile,如下: private volatile static boolean bChanged; 这个关键字告诉线程一定要走内存,不要再从缓存中获取数据了。只要已修改,其他线程立马知道值已经被修改了,因为它们都是从主存中取的数据。修改后的结果如下图: 可以看到程序立马结束。并且有输出。因为这个关键字让这个成员变量都可见了。这就是线程的可见性。 其实volatile关键字还有一个功能就是:阻止指令排序。但是这只是相对的阻止。如下: 以前官方推荐是volatile,但是现在synchronized已经优化的很好了。不要刻意去用volatile。如果一个类加上volatile关键字,那么它的成员变量也会默认加上volatile关键字。 volatile只能解决可见性。一定程度上解决指令排序,但是是相对的。 但是volatile解决了可见性,就一定可以解决问题了吗?并不是的。例子如下: 如果你只希望单一变量可见性,volatile是可以的,但是如果关注的不是单一变量volatile就不管用了。 2.用synchronize加锁的办法。它能解决可见性,原子性。 三.线程封闭 实现好的并发是一件困难的事情,所以很多时候我们都想躲避并发。避免并发最简单的方法就是线程封闭。什么是线程封闭呢? 当访问共享变量时,往往需要加锁来保证数据同步。一种避免使用同步的方式就是不共享数据。如果仅在单线程中访问数据,就不需要同步了。这种技术称为线程封闭。在Java语言中,提供了一些类库和机制来维护线程的封闭性,例如局部变量和ThreadLocal类, 线程封闭其实就是不共享数据就行了。 线程封闭方法有: 1.不要共享变量,在变量中加final关键字 2.栈封闭:栈封闭是我们编程当中遇到的最多的线程封闭。什么是栈封闭呢?简单的说就是局部变量。多个线程访问一个方法,此方法中的局部变量都会被拷贝一分儿到线程栈中。所以局部变量是不被多个线程所共享的,也就不会出现并发问题。所以能用局部变量就别用全局的变量,全局变量容易引起并发问题。如果了解JVM应该懂得。 3.ThreadLocal线程绑定。ThreadLocal内部维护了一个Map,Map的key是每个线程的名称,而Map的值就是我们要封闭的对象。每个线程中的对象都对应着Map中一个值,也就是ThreadLocal利用Map实现了对象的线程封闭。 想要操作什么数据,可以先把数据放在ThreadLocal中绑定,用的时候才取出来。 例子如下: public class LocalTest { private int num; public int getNum() { return num; } public void setNum(int num) { this.num = num; } } public class ThreadLocalDemo { private static ThreadLocal<LocalTest> threadLocal = new ThreadLocal<LocalTest>(); public static void main(String[] args) throws InterruptedException{ final LocalTest local = new LocalTest(); new Thread(){ @Override public void run() { for (; ; ){ threadLocal.set(local); LocalTest l = threadLocal.get(); l.setNum(20); System.out.println(Thread.currentThread().getName() + "---" +threadLocal.get().getNum()); Thread.yield(); } } }.start(); new Thread(){ @Override public void run() { for (; ; ){ threadLocal.set(local); LocalTest l = threadLocal.get(); l.setNum(30); System.out.println(Thread.currentThread().getName() + "---" +threadLocal.get().getNum()); Thread.yield(); } } }.start(); } } 结果如下: 可以看到各个线程对应的值并没有乱。

优秀的个人博客,低调大师

Java并发编程笔记1-竞争条件&amp;初识原子类&amp;可重入锁

我们知道,在多线程访问一个共享变量的时候会发生安全问题。 首先看下面例子: public class Counter { private int count; public void add(){ try{ for (int i = 0;i<200;i++){ Thread.sleep(100); this.count++; System.out.println(this.count); } }catch (Exception e ){ e.printStackTrace(); } } } public class Test { public static void main(String[] args) { final Counter counter = new Counter(); new Thread(new Runnable() { public void run() { counter.add(); } }).start(); new Thread(new Runnable() { public void run() { counter.add(); } }).start(); new Thread(new Runnable() { public void run() { counter.add(); } }).start(); } } 运行结果如下: 如果没发生线程对数据的赃操作,3个线程执行,结果会累加到600的,看上面结果明显没有,并且出现一下重复的数据。这是因为存在3个线程同时操作同一个数字,导致输出重复数字。 解决办法: 1.在方法上加上synchronized关键字。如下图: 虽然结果不会出现重复,但是synchronized效率及其低下,运行速度明显被拖慢。原因是,for循环中的i变量是每个线程都有独自的局部变量,各不影响,但是每个线程都要排队进入这个方法,排队睡觉,这样就导致效率低下 2.在竞争条件上加synchronized,我们知道各个线程其实竞争的是count这个成员变量。因此在此地方加即可。如下图: 这样运行效率比方法一快了很多,因为省去了排队进入方法,排队睡觉。只需要排队取count值即可,这样效率比方法一快。 注意上图中,输出语句并不是竞争条件,并不一定要放在synchroized里面,这里放在里面是为了让线程取到值自增后立即输出,这样输出就不会发生混论,不发生抢占输出问题,一样能累加到600, 如果把输出放在synchronized外面会出现值有重复现象,因为累加后的值并没有立即输出,这样导致输出混乱,但仍然能加到600.知识输出不安全罢了。 3.使用原子类型,比如将上面的代码的int count类型改成AtomicInteger count类型,我们知道获取count的值然后再自加个1是可能会出现问题的,也就是结果出现重复数字。AtomicInteger类型是以同步的方法解决这个问题的。如下图: 结果如下图: 可以看到数字的输出没有严格的排队,但是数据确实给你保证的了,就是完整的加到600.这也恰恰因为不是严格的进行排队,才是的这种方法比前面两种方法的效率大大改进。 理论总结:synchronized是一种内部锁,就是所对象内部给我们提供的,因为每一个对象有一个状态变量,相当于一个锁,进入同步块,改变这个变量。别的线程进入之后就要判断这个变量有没有改变。 一个线程获取它本生已经持有的锁,这是可以成功的。我们知道多个线程同时抢占同一个锁它们是失败的。因为它们之间是互斥的。但是呢,一个线程再次获取一个自己已经拿过的锁是可以成功的,那么它是能够成功的。 看如下例子: public class Widget { public synchronized void doSth(){ } } public class ChildWidget extends Widget { @Override public synchronized void doSth() { super.doSth(); } } public class Test { public static void main(String[] args) { Widget w = new ChildWidget(); w.doSth(); } } 子类调用自己的方法的那个synchroized那个锁是w对象的这个锁,而在子类方法中的super.doSth()父类中的方法的synchronized的锁也是w对象的锁。因此不要被子类继承父类的方法中的锁所迷惑了。 因此这种方式叫做内部锁的可重入机制,也叫可重入锁。

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册