Quantcast
Channel: CSDN博客推荐文章
Viewing all 35570 articles
Browse latest View live

python数据分析系列教程——xls文件的读写

$
0
0

全栈工程师开发手册 (作者:栾鹏)

python教程全解

python将数据存储到excel文件。本文不通过与操作excel办公软件而是偏向于excel文件的处理。如果你需要通过python控制excel软件可以参考http://blog.csdn.net/luanpeng825485697/article/details/78361633

使用xlwt库,点击下载xlwt库

使用xlrd库,点击下载xlrd库

安装python库的方法,可以参考 Python库的安装与卸载

然后就可以处理excel文件了。

python3.6下xls文件的读写

#coding:utf-8
#xls文件的读写
import xlwt
import xlrd


#将数据写入xls
workbook=xlwt.Workbook(encoding='utf-8')   #文件编码
booksheet=workbook.add_sheet('Sheet 1', cell_overwrite_ok=True)   #表名,是否覆盖
DATA=(('学号','姓名','年龄','性别','成绩'),
      ('1001','A','11','男','12'),
      ('1002','B','12','女','22'),
      ('1003','C','13','女','32'),
      ('1004','D','14','男','52'),
      )
for i,row in enumerate(DATA):   #迭代
    for j,col in enumerate(row):  #迭代
        booksheet.write(i,j,col)   #写入单元格
workbook.save('test.xls')   #保存成文件



#从xls中读取数据
fname = "test.xls"
data = xlrd.open_workbook(fname)
shxrange = range(data.nsheets)
try:
    sh = data.sheet_by_name("Sheet 1")
    nrows = sh.nrows
    ncols = sh.ncols
    print("hang %d, ncols %d" % (nrows, ncols))

    for row_index in range(sh.nrows):
        for col_index in range(sh.ncols):
            print(sh.cell(row_index, col_index).value,end='')
        print('')
except:
    print("no sheet in %s named Sheet1" % fname)
作者:luanpeng825485697 发表于2017/11/21 18:18:13 原文链接
阅读:0 评论:0 查看评论

[运营专题]零预算引爆个人和企业品牌

$
0
0

说在前面:达人课是GitChat的一款轻阅读产品,由特约讲师独家发布。每一个课程你都可获得6-12篇的深度文章,同时可在读者圈与讲师互动交流。GitChat达人课,让技术分享更简单。进入我的GitChat

本篇文章非常推荐运营的朋友看下

这里写图片描述

作者简介

Easy,曾任新浪 SAE、微盘首任产品经理,看着微博成长,写过数十个微博开放平台应用,有多年的社会化传播经验。曾创立人才拍卖网站 JobDeer.com,获创新工场投资,后被国内知名猎头公司锐仕方达收购。微博非著名段子手,《从入门到放弃》系列周边作者,正以成为「技术圈中产品最好、产品圈中技术最好」的人而奋斗。查看原文

课程介绍

如何从一名普通网民进阶为草根权威? 老板不给钱却给了太多推广需求怎么办? 创业公司的网络营销与常规玩法有什么不同? ……

如何在零预算、经验少的情况下做到“有效运营”,可能是很多刚入门的运营人士或自媒体最大的困扰。本课程针对这样的问题进行了全面解答。课程内容以《引爆点》和《疯传》为基础,整合并强化了其理论体系,结合社交网络,给出了大量的实操案例。这里既有一以贯之的方法论,也有拿来即用的执行细节;既有打造爆点的运营思维,也有大量的案例对比,让你深入理解运营技巧的意义,最终从根本上“知其然,更知其所以然”。

导读:没有太多预算,却有太多需求

我能学到什么?

很多人都认为营销和自己没有什么关系,我以前也是如此,觉得自己只需要踏踏实实把代码写好就行,不需要到处王婆卖瓜。但后来我做开源软件时,售卖自己业余开发的的建站系统时,甚至是找工作时,我都深深地感到营销的巨大作用。

后来我在资金很不充裕的情况下运营一家创业公司。最初的想法是,应该去找一个市场大牛,让专业的人做专业的事,然而成就大牛们那些疯狂业绩的往往是同样疯狂的市场预算。「巧妇难为无米之炊」,他们说。难道真的就没有一个四两拔千斤的办法了?我读了很多书,上了很多课,问了很多人,发现这种事还是能够做到的,而且不少人正在做。

这就是课程里边我要和大家分享的理论和技巧。它是时间和金钱所验证过、确定可用的:

  • 通过对「从入门到放弃」这个热点内容的包装加工,我在三天时间里就卖出了5000多件T恤和手机壳。
  • 通过其中的「实物锚定」方法,我让6000多家互联网公司的近万名员工,在长达几年的时间里天天上班都能看到我们人才产品的 LOGO 。
    就连当时为了练习对附着力的敏感度,持续在微博上编写段子的努力,也得到了回报——今天我的微博粉丝已经八万多,每月浏览量超千万。

什么人适合本课程?

这是一个专为没有太多推广预算,却有太多推广需求的同学们量身定制的课程。如果你是下边三种人之一,那么它一定会让你物超所值:

  • 老板不愿意给预算的新媒体运营
  • 没投资或者投资要省着花的创业者
  • 没钱或者花自己钱的自媒体和个人品牌

本课程的与众不同之处

请让我自夸几句。大部分的营销课程都只是教你技巧,但并不会告诉你背后的支撑逻辑。一旦对应的环境发生了变化,哪怕是最轻微的变化,这些技巧就会失效。技巧就像一片叶子,你把它收集到一个大袋子里,它只会慢慢枯萎。

我们讲的是一个完整的、系统化的方法论,它是一棵知识树。叶子终会老去,但当你明白这棵树为什么要朝这个方向长、这树干为什么是这样一个形状以后,只要一个春暖花开的灵感,新的叶子就会自然萌芽。

我们提供的是理论和实践并重的实战课程。一方面课程中的案例,你融会贯通以后,可以直接用到自己的业务当中,另一方面,你又可以在课程中的知识树上找到这个案例背后的支撑逻辑,从而更深入更根本的理解这些技巧的意义,根据市场和受众的变化,做出对应的调整和优化,最终知其然,更知其所以然。

现在就让我们开始吧。

第01课:鲜为人知却又至关重要的链式传播

制造流行

制造出大规模的流行,是每一家商业公司梦寐以求的事情,所以关于如何制造流行的研究,从「古代」就开始了。

这方面的经典著作是马尔科姆·格拉德威尔(Malcolm GlaDwell)的《引爆点》一书。该书从流行病切入,通过大量的研究案例,提炼出了「流行三法则」:

  • 个别人物法则(Law of the Few):在流行传播中,有一些人更为重要。
  • 附着力因素法则(Stickiness Factor):总能找到一种简单的包装方式,让信息变得不可抗拒。
  • 环境威力法则(Power of context):传播对于环境极度敏感,一些极小的变动,也可能产生巨大的影响。
    《引爆点》一书原版出版于2000年,距离现在已经十七个年头了。虽然法则依然有效,但在社会化网络成为大多数人生活的一部分后,我们迫切需要更为具体的理论来指导工作,从而避免「道理我都懂、但依然做不好营销」的怪圈。

链式传播和社交媒体

传播有很多种,但要制造大规模流行,链式传播(也叫传播链)最好用。大家知道原子弹中子弹这类核武器,只需要非常少量的原料,就可以引发非常大的爆炸,这是因为其中有链式反应。与核武器类似,链式传播也是一对多的高速多级传播,从而可以达到几何级的增长。内容通过链式传播,可以在短短几个小时内覆盖全国,甚至全世界。

链式传播的结构

互联网产品中,微博是非常标准的链式结构。

这里写图片描述

通过关注,一个人可以有很多的粉丝;通过转发,一个人发布的微博可以被粉丝的粉丝看到。只要触发足够多的转发,内容就会被你的粉丝、你粉丝的粉丝、你粉丝的粉丝的粉丝看到。如果你的粉丝平均拥有 100 个粉丝,理论上经过三次传播,你的微博就可以覆盖 100*100*100 等于100万人。

当然这只是理论值,并不是每个人都会转发、也不是转发后他的每个粉丝都会看见。

微信上的传播比较类似,虽然微信本身是双向强关系,但内容主要是通过公众号和朋友圈转发来完成的。

这里写图片描述

你可以向关注公众号的订阅者推送文章,他们读完以后觉得不错就会分享到朋友圈,推荐给他的朋友们阅读;而他们的朋友读完文章以后,就会再分享到他的朋友圈,这也是一个典型的链式结构。

但微信和微博在传播细节上有所不同。

微博是被动式阅读,即使他关注了你,也只有在他去刷微博的时候,才能看见你发布的内容;微信是主动式阅读,内容直接下行推送。即使对方没打开微信,只要他订阅了你的公众号,内容也会被分发到手机上并提醒他阅读,所以对于非重度用户来说,微信比微博有更高的到达率。

微信后来对订阅号推送的内容进行了折叠,这让什么时候推送内容才能被用户注意到成了一种技巧。服务号推送的内容虽然没有被折叠,但微信特意去掉了提示音。据最新的小道消息,微信开始要对订阅号内容做信息流展现了,这会让订阅号更类似于微博。
然而在二次传播上,微博有标准的转发按钮,现在你点赞过的微博也会进入粉丝的信息流。而微信则需要读者在阅读完文章之后,主动点击右上角的分享按钮,才可以分享到朋友圈。官方还各种强调不允许引导分享。相比之下,微信获得分享的门槛更高,转化率也更低一些。

所以微博和微信都各有千秋,但只要我们学好了链式传播,在这两个大型的社交化平台上都能获得很好的回报。

链式传播的构成

要构成链式传播,有两个必要条件:

首先我们需要一个一对多的信息传递结构,这样才能把你的内容传递给足够多的受众。
在内容传递的过程中,我们要能够新増关系,这样下次传播时受众才会更多。
为了便于描述,我们在后文中将使用「社交媒体」或「社会化媒体」来特指这类包含了链式传播功能的产品。

在社交媒体上重验和扩展流行三法则

不同于线下的面对面社交,互联网上的互动不再严格的受到时间和空间的限制,在关系维护上成本更低、在内容传播上速度更快,但相应的,受众更多的通过内容而不是表情和语气来感受传播者的情绪。

这些特点使得流行三法则在社交媒体上的表现可能有所不同。马尔科姆在《引爆点》中写道,三个法则分别对应了流行病爆发需要的三个条件:

  • 能传播病原体的人
  • 病原体本身
  • 发生作用的环境

我们将重回这些基点,在社交媒体的背景下,去研究、验证和扩展可用于这个领域的专属方法论。这也构成了本课程最主要的章节。下面,就让我们一一展开。

下一篇

(购买VIP会员可阅读全部GitChat文章、参与全部Chat交流互动)

全部课程内容

导读:没有太多预算,却有太多需求

第01课:鲜为人知却又至关重要的链式传播

第02课:传播节点研究:联系员、专家和推销员

第03课:附着力六大原则 · 综述

第04课:附着力六大原则 · 社交货币

第05课:附着力六大原则 · 诱因

第06课:附着力六大原则 · 情绪

第07课:附着力六大原则 · 公共性

第08课:附着力六大原则 · 实用价值

第09课:附着力六大原则 · 故事

第10课:链式传播的综合应用

文章推荐

1、Cordova App 打包全揭秘

2、来这样做,才能向架构师靠近

3、Selenium 自动化测试从零实战

作者:GitChat 发表于2017/11/21 16:01:53 原文链接
阅读:96 评论:0 查看评论

nsq源码阅读 nsqd源码三 tcp.go

$
0
0

NSQ的TCP逻辑都是这样的,调用internal/protocol/tcp_server.go中的TCPServer:

func TCPServer(listener net.Listener, handler TCPHandler, l app.Logger)
接受客户端连接,在这里客户端为consumer,调用TCPHandler.Handle处理业务逻辑,TCPHandler.Handle是个接口,让不同的业务逻辑自己实现这个接口。

这里在tcp.go文件中实现:

func (p *tcpServer) Handle(clientConn net.Conn) {
	p.ctx.nsqd.logf("TCP: new client(%s)", clientConn.RemoteAddr())

	// The client should initialize itself by sending a 4 byte sequence indicating
	// the version of the protocol that it intends to communicate, this will allow us
	// to gracefully upgrade the protocol away from text/line oriented to whatever...
	buf := make([]byte, 4)
	//从流中读取4个字节的数据到buf,被读取的数据,会从流中截取掉
	_, err := io.ReadFull(clientConn, buf)
	if err != nil {
		p.ctx.nsqd.logf("ERROR: failed to read protocol version - %s", err)
		return
	}
	protocolMagic := string(buf)

	p.ctx.nsqd.logf("CLIENT(%s): desired protocol magic '%s'",
		clientConn.RemoteAddr(), protocolMagic)

	var prot protocol.Protocol
	switch protocolMagic {
	case "  V2":
		prot = &protocolV2{ctx: p.ctx}
	default:
		//如果不是"  V2"协议,报错,该goroutine停止
		protocol.SendFramedResponse(clientConn, frameTypeError, []byte("E_BAD_PROTOCOL"))
		clientConn.Close()
		p.ctx.nsqd.logf("ERROR: client(%s) bad protocol magic '%s'",
			clientConn.RemoteAddr(), protocolMagic)
		return
	}

	//接口,实际调用的是protocolV2.IOLoop
	err = prot.IOLoop(clientConn)
	if err != nil {
		p.ctx.nsqd.logf("ERROR: client(%s) - %s", clientConn.RemoteAddr(), err)
		return
	}
}
先判断协议版本,不是“  V2”(前面有两个空格),报错,结束该goroutine。如果是,则调用该协议对应的IOLoop方法,读取协议内容,处理协议的具体行为。这里的prot.IOLoop也是一个接口,供每个协议自己实现,V2的实现方式在protocol_v2.go文件中。

V2协议大致为“  V2 command params\n”或“  V2 command params\r\n”

协议开头必须是“  V2”,command为要执行的命令,命令有IDENTIFY、SUB、PUB等等,params为命令的参数,具体参考官网这里

Anchor 





作者:aslackers 发表于2017/11/21 18:28:09 原文链接
阅读:61 评论:0 查看评论

安卓使用Audio Record自定义录音

$
0
0

全栈工程师开发手册 (作者:栾鹏)

安卓教程全解

安卓使用Audio Record自定义录音。

创建异步任务后台录音

创建异步任务

recordbutton.setOnClickListener(new OnClickListener() {
      public void onClick(View v) {
          //创建异步任务,在后台录制声音
        AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
          @Override
          protected Void doInBackground(Void... params) {
            isRecording = true;
            record();
            return null;
          }

        };
        task.execute();
      }
    });

录音功能函数

//使用Audio Record录制原始音频
  private boolean isRecording = false;

  private void record() {
    int frequency = 11025;   //录音频率
    int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;   //频道配置
    int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;   //录音编码

    File file = new File(Environment.getExternalStorageDirectory(), "raw.pcm");

    //创建新文件
    try {
      file.createNewFile();
    } catch (IOException e) {
      Log.d("自定义录音", "IO Exception", e);
    }

    try {
      OutputStream os = new FileOutputStream(file);
      BufferedOutputStream bos = new BufferedOutputStream(os);
      DataOutputStream dos = new DataOutputStream(bos);

      int bufferSize = AudioRecord.getMinBufferSize(frequency,channelConfiguration,audioEncoding);
      short[] buffer = new short[bufferSize];

      //创建一个新的AudioRecord对象来录制音频
      AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,frequency,channelConfiguration,audioEncoding, bufferSize); 
      audioRecord.startRecording();
      Log.v("自定义录音", "开始录音");

      while (isRecording) {
        int bufferReadResult = audioRecord.read(buffer, 0, bufferSize);
        for (int i = 0; i < bufferReadResult; i++)
          dos.writeShort(buffer[i]);
      }

      audioRecord.stop();
      dos.close();
    } catch (Throwable t) {
      Log.d("自定义录音", "An error occurred during recording", t);
    }
  }

停止录音

只需要将全局变量isRecording 设置为false即可跳出后台程序,停止录音。

播放录制的音频

//使用Audio Track播放原始音频
  private void playback() {
    int frequency = 11025/2;
    int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;
    int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;

    File file = new File(Environment.getExternalStorageDirectory(), "raw.pcm");

    //用于存储音轨的short数组,(每个short占用16位,即2个字节)
    int audioLength = (int)(file.length()/2);
    short[] audio = new short[audioLength];

    try {
      InputStream is = new FileInputStream(file);
      BufferedInputStream bis = new BufferedInputStream(is);
      DataInputStream dis = new DataInputStream(bis);

      int i = 0;
      while (dis.available() > 0) {
        audio[i] = dis.readShort();
        i++;
      }

      //关闭输入流
      dis.close();
      Log.v("自定义录音", "播放录音");
      //创建和播放新的AudioTrack对象
      AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
                                             frequency,
                                             channelConfiguration,
                                             audioEncoding,
                                             audioLength,
                                             AudioTrack.MODE_STREAM);
      audioTrack.play(); 
      audioTrack.write(audio, 0, audioLength);
    } catch (Throwable t) {
      Log.d("自定义录音", "An error occurred during playback", t);
    }
  }
作者:luanpeng825485697 发表于2017/11/21 18:45:42 原文链接
阅读:58 评论:0 查看评论

数据结构与算法分析(Java语言描述)(32)—— 使用 Kruskal 算法求有权图的最小生成树

$
0
0

这里写图片描述

将图中的所有边存到最小堆中

当最小堆非空
    取出权重最小的边
    如果此边的两个端点是连接的
        跳出本次循环
    将此边加入 mst 中
    在并查集中 union 此边的两端点
package com.dataStructure.weight_graph;

import com.dataStructure.heap.MinHeap;
import com.dataStructure.union_find.UnionFind5;

import java.util.ArrayList;
import java.util.List;

// Kruskal算法求最小生成树

public class KruskalMST {
    private List<Edge> mst; // 存放最小生成树的边
    private Number mstWeight;   // 最小生成树的权重

    // 构造函数
    public KruskalMST(WeightedGraph graph) {
        // 初始化私有字段
        mst = new ArrayList<>();
        mstWeight = 0;

        // 最小堆存放图中所有的边
        MinHeap<Edge> edgeMinHeap = new MinHeap<>(graph.E());
        for (int i = 0; i < graph.V(); i++)
            for (Edge edge : graph.adjacentNode(i))   // 遍历 i 的邻接节点
                if (edge.getV() <= edge.getW()) // 过滤掉 7-1 这种重复的边
                    edgeMinHeap.insert(edge);   // 将非重复的边插入最小堆中

        // 初始化一个图中节点数量大小的并查集
        UnionFind5 unionFind = new UnionFind5(graph.V());

        // 当最小堆非空 且 最小生成树尚未连接图中的所有节点
        while (!edgeMinHeap.isEmpty() && mst.size() < graph.V() - 1) {
            Edge edge = edgeMinHeap.extractMin();   // 取出权重最小的边

            // 如果向 mst 中加入 edge 前,v 和 w 已经连接
            // edge 加入 mst 后,将形成环,为防止形成环,跳出此次循环
            if (unionFind.isConnected(edge.getV(), edge.getW()))
                continue;

            mst.add(edge);  // 将 edge 加入 mst 中
            unionFind.unionElements(edge.getV(), edge.getW());  // 并查集中 union v 和 w

        }

        for (Edge edge : mst)   // 计算最小生成树的权重
            mstWeight = mstWeight.doubleValue() + edge.getWeight().doubleValue();
    }

    public List<Edge> getMst() {
        return mst;
    }

    public Number getMstWeight() {
        return mstWeight;
    }

    // 测试 Kruskal
    public static void main(String[] args) {

        String filename = "/testG1.txt";
        int V = 8;

        SparseGraph g = new SparseGraph(V, false);
        ReadWeightedGraph readGraph = new ReadWeightedGraph(g, filename);

        // Test Kruskal
        System.out.println("Test Kruskal: ");
        KruskalMST kruskalMST = new KruskalMST(g);
        List<Edge> mst = kruskalMST.getMst();
        for (int i = 0; i < mst.size(); i++)
            System.out.println(mst.get(i));
        System.out.println("The MST weight is: " + kruskalMST.getMstWeight());

        System.out.println();
    }
}


//public class KruskalMST {
//
//    private List<Edge> mst;   // 最小生成树所包含的所有边
//    private Number mstWeight;           // 最小生成树的权值
//
//    // 构造函数, 使用Kruskal算法计算graph的最小生成树
//    public KruskalMST(WeightedGraph graph) {
//        mstWeight = 0;
//        mst = new ArrayList<>();
//
//        // 将图中的所有边存放到一个最小堆中
//        MinHeap<Edge> pq = new MinHeap<>(graph.E());
//        for (int i = 0; i < graph.V(); i++)
//            for (Edge edge : graph.adjacentNode(i))
//                if (edge.getV() <= edge.getW()) // 过滤掉 7-1 等情况,防止堆中的边重复
//                    pq.insert(edge);
//
//
//        // 创建一个并查集, 来查看已经访问的节点的联通情况
//        UnionFind5 unionFind = new UnionFind5(graph.V());
//        while (!pq.isEmpty() && mst.size() < graph.V() - 1) {
//
//            // 从最小堆中依次从小到大取出所有的边
//            Edge e = pq.extractMin();
//
//            // 如果该边的两个端点是联通的, 说明加入这条边将产生环, 扔掉这条边
//            if (unionFind.isConnected(e.getV(), e.getW()))
//                continue;
//
//            // 否则, 将这条边添加进最小生成树, 同时标记边的两个端点联通
//            mst.add(e);
//            unionFind.unionElements(e.getV(), e.getW());
//        }
//
//        for (Edge edge : mst) // 计算最小生成树的权重
//            mstWeight = mstWeight.doubleValue() + edge.getWeight().doubleValue();
//    }
//
//    // 返回最小生成树的所有边
//    List<Edge> mstEdges() {
//        return mst;
//    }
//
//    // 返回最小生成树的权值
//    Number result() {
//        return mstWeight;
//    }
//
//
//    // 测试 Kruskal
//    public static void main(String[] args) {
//
//        String filename = "/testG1.txt";
//        int V = 8;
//
//        SparseGraph g = new SparseGraph(V, false);
//        ReadWeightedGraph readGraph = new ReadWeightedGraph(g, filename);
//
//        // Test Kruskal
//        System.out.println("Test Kruskal:");
//        KruskalMST kruskalMST = new KruskalMST(g);
//        List<Edge> mst = kruskalMST.mstEdges();
//        for (int i = 0; i < mst.size(); i++)
//            System.out.println(mst.get(i));
//        System.out.println("The MST weight is: " + kruskalMST.result());
//
//        System.out.println();
//    }
//}

这里写图片描述

作者:HeatDeath 发表于2017/11/21 19:34:22 原文链接
阅读:32 评论:0 查看评论

尝试阅读ReentrantLock、AbstractQueuedSynchronizer源码(二)

$
0
0

尝试阅读ReentrantLock、AbstractQueuedSynchronizer源码 (一)


这篇文章希望通过对 AbstractQueuedSynchronizer 内部类 ConditionObject 的探索,加深对阻塞唤醒机制的理解!


一、await()方法流程

ReentrantLock lock = new ReentrantLock(true);
Condition con = lock.newCondition();
con.await();

这一段代码很常见,当调用await方法,阻塞当前线程。下面我们来看看具体实现:

AbstractQueuedSynchronizer.ConditionObject.await()

public final void await() throws InterruptedException {
        //手动检测当前线程是否中断,并清空中断标识
        //如果中断,则抛出中断异常
            if (Thread.interrupted())
                throw new InterruptedException();
            //创建一个节点并添加到一个队列中
            Node node = addConditionWaiter();
            //释放该节点持有的锁
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);//线程阻塞于此
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }

加入condition队列

下面详细看下addConditionWaiter

AbstractQueuedSynchronizer.ConditionObject.addConditionWaiter()

private Node addConditionWaiter() {
            Node t = lastWaiter;
            // If lastWaiter is cancelled, clean out.
            if (t != null && t.waitStatus != Node.CONDITION) {
                unlinkCancelledWaiters();
                t = lastWaiter;
            }
            Node node = new Node(Thread.currentThread(), Node.CONDITION);
            if (t == null)
                firstWaiter = node;
            else
                t.nextWaiter = node;
            lastWaiter = node;
            return node;
        }

通过这段代码,其实不难发现:
1、每个condition有一个关联的队列,并且这个队列是个单向链表;
2、每个condition队列头节点为firstWaiter、尾节点为lastWaiter。使用nextWaiter 进行彼此之间的关联

这里写图片描述

到现在为止,我们知道了两个队列,同步队列(阻塞队列)和条件队列(condition队列),那么这两个队列之间有什么关联呢? 值得我们关注

unlinkCancelledWaiters方法用于清除队列状态不为-2的节点。

private void unlinkCancelledWaiters() {
            Node t = firstWaiter;
            Node trail = null;
            while (t != null) {
                Node next = t.nextWaiter;
                if (t.waitStatus != Node.CONDITION) {
                    t.nextWaiter = null;
                    if (trail == null)
                        firstWaiter = next;
                    else
                        trail.nextWaiter = next;
                    if (next == null)
                        lastWaiter = trail;
                }
                else
                    trail = t;
                t = next;
            }
        }

释放线程持有的锁

线程被阻塞时,同时需要放弃自己所持有的锁,并唤醒后继节点。最后将节点状态改为-2。在释放锁时,如果当前线程不是锁持有者,则抛出IllegalMonitorStateException异常。

final int fullyRelease(Node node) {
        boolean failed = true;
        try {
            int savedState = getState();
            if (release(savedState)) {
                failed = false;
                return savedState;
            } else {
                throw new IllegalMonitorStateException();
            }
        } finally {
            if (failed)
            //将当前节点状态修改为-2;当添加下一个节点时,该节点将会被清除
                node.waitStatus = Node.CANCELLED;
        }
    }

等待进入同步队列

//判断当前线程是否在同步队列中
final boolean isOnSyncQueue(Node node) {
    //如果节点状态为-2,或者无前驱节点,返回false
        if (node.waitStatus == Node.CONDITION || node.prev == null)
            return false;
        //如果next属性不为null,返回true
        if (node.next != null) // If has successor, it must be on queue
            return true;
        /*
         * node.prev can be non-null, but not yet on queue because
         * the CAS to place it on queue can fail. So we have to
         * traverse from tail to make sure it actually made it.  It
         * will always be near the tail in calls to this method, and
         * unless the CAS failed (which is unlikely), it will be
         * there, so we hardly ever traverse much.
         */
         // 从队尾遍历
        return findNodeFromTail(node);
    }

线程不断自旋来确认是否还在同步队列中,如果不在同步队列中了,则阻塞当前线程;原来await方法使用的就是park

二、signal()方法流程

使用该用法用于唤醒一个阻塞的线程,该方法只有锁持有者可以调用,否则抛出异常。

public final void signal() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignal(first);
        }
private void doSignal(Node first) {
            do {
            //将firstWaiter指向第二个等待的节点
            //如果第二个等待的节点为null,让尾节点修改为null
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                //去掉与第二个等待节点的管理
                first.nextWaiter = null;
            }//如果转移失败,则循环转移下一个节点,直到成功
             while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        }
//将节点node转移到同步队列
final boolean transferForSignal(Node node) {
        /*
         * If cannot change waitStatus, the node has been cancelled.
         * 如果无法修改node节点是waitStatus,说明node节点的状态为1。
         * 进行条件队列下一个节点的转移
         */
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;

        //进入阻塞队列尾部,p表示前驱节点
        Node p = enq(node);
        int ws = p.waitStatus;
        //如果前驱节点状态为1(已经取消),或者改变前驱节点的状态为-1失败,则直接唤醒node节点
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }
作者:disiwei1012 发表于2017/11/21 20:29:58 原文链接
阅读:54 评论:0 查看评论

561. Array Partition I。

$
0
0

Given an array of 2n integers, your task is to group these integers into n pairs of integer, say (a1, b1), (a2, b2), …, (an, bn) which makes sum of min(ai, bi) for all i from 1 to n as large as possible.

Example 1:

Input: [1,4,3,2]

Output: 4
Explanation: n is 2, and the maximum sum of pairs is 4 = min(1, 2) + min(3, 4).

Note:
n is a positive integer, which is in the range of [1, 10000].
All the integers in the array will be in the range of [-10000, 10000].


题中给定了一个数组,然后让以两个数分为一组的形式分成好多组,并且最后分别对各个组求最小值,然后相加得出结果,并且这个结果值要尽可能的大。

刚看到题比较懵,后来一想十分简单。这个题主要是用来考虑怎么分组合适,怎么分组才能让各个组求出最小值之后进行相加得到最大。很显然就是最小的数字和其次小的数字一组最合适了,因为最小的数字无论和哪一个数字一组最后求最小值都是这个最小的数字,所以和其次小的数字一组才不会浪费较大的数字。就比如题中的例子:1、4、3、2。1可以和另外三个分成一组,但是无论和谁分到一组求出的最小值都是1,所以尽可能的让1和2一组,3和4一组,这样得到的数值才会最大为4。按照这样的思路就是将数组排序之后相邻的两个数分成一组,这样最后求出的和才能最大。而排序之后的每组的最小值正好又是每组的第一个数字,所以就很简单了。


C++:

class Solution {
public:
    int arrayPairSum(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int i,sum=0;
        for(i=0;i<nums.size();i=i+2) {
            sum += nums[i];
        }
        return sum;
    }
};
作者:Leafage_M 发表于2017/11/21 20:35:53 原文链接
阅读:55 评论:0 查看评论

安卓接收耳机按键控制音频播放

$
0
0

全栈工程师开发手册 (作者:栾鹏)

安卓教程全解

如今连接移动设备的耳机上基本都有按键,来控制音频的播放,暂停,下一首,上一首,或电话的拨打,视频的播放等功能。而外置媒体按键的按下,安卓系统接收到这个信号以后会向系统所有app发送一个媒体按键的广播事件。app注册接收按键事件来进行相应的操作。而且接收这一事件的广播接收器必须在manifest中注册

另外,安卓只允许当前时刻只有一个app能获取音频焦点。所以可以控制当有别的播放器打开后,自己的app就可以会失去焦点,通过节点事件调整自己app的播放功能。

由于外接媒体按键的广播接收器只能在maifest中注册,所以只能通过先用自定义接收器接收这一事件,再向本地app发送一个代表这一事件的间接事件。目标窗口接收间接事件,再进行相应的处理。

首先要在manifest中注册媒体按键的广播接收器

设备带有播放、停止、暂停的按键,按下、弹起时系统会广播一个带有ACTION_MEDIA_BUTTON动作的intent

<receiver android:name="com.lp.app.media.MediaControlReceiver">
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_BUTTON"/>
    </intent-filter>
</receiver>

定义广播接收器,接收媒体按键广播事件,并向本地app广播间接事件。

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

//设备按键广播接收器,接收到intent后,判断是否是设备按键广播,然后广播给音频播放activity。因为这个监听器只能在mainfest中注册
public class MediaControlReceiver extends BroadcastReceiver {

  public static final String ACTION_MEDIA_BUTTON = "com.lp.app.ACTION_MEDIA_BUTTON";

  @Override
  public void onReceive(Context context, Intent intent) {
    if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
      Intent internalIntent = new Intent(ACTION_MEDIA_BUTTON);  

      internalIntent.putExtras(intent.getExtras());   //将数据也传递给目标窗口,播放、暂停、声音调大、声音调低。按下弹起都会发起广播
      context.sendBroadcast(internalIntent);
      Log.v("媒体按键广播接收器", "接收到外接媒体按键"+internalIntent.getAction()+"发送内部广播给本app");
    }
  }
}

目标activty先进行基本的音频播放配置。准备接收间接媒体按键事件

  private MediaPlayer mediaPlayer;

//配置音频播放
  private void configureAudio() {
    try {      
      mediaPlayer = new MediaPlayer();
      mediaPlayer.setDataSource("/sdcard/test.mp3");
      mediaPlayer.prepare();

    } catch (IllegalArgumentException e) {
      Log.d("媒体按键广播接收窗口", "Illegal Argument Exception: " + e.getMessage());
    } catch (SecurityException e) {
      Log.d("媒体按键广播接收窗口", "Security Exception: " + e.getMessage());
    } catch (IllegalStateException e) {
      Log.d("媒体按键广播接收窗口", "Illegal State Exception: " + e.getMessage());
    } catch (IOException e) {
      Log.d("媒体按键广播接收窗口", "IO Exception: " + e.getMessage());
    }
  }

目标activity中接收间接广播事件。同时可以注册耳机拔出事件的广播接收器

private ActivityMediaControlReceiver activityMediaControlReceiver;   //间接广播事件

@Override
  protected void onResume() {
    super.onResume();

    //媒体按键事件的广播接收器,在mainfest中注册,接收器接收到事件后统一在app内广播一个新的事件表征媒体按键的事件,所有接收此事件的窗口再去处理。
    AudioManager am =(AudioManager)getSystemService(Context.AUDIO_SERVICE);   //获取声量管理服务
    ComponentName component = new ComponentName(this, MediaControlReceiver.class);

    am.registerMediaButtonEventReceiver(component);  //将接收处理程序注册为媒体按键按下动作的唯一处理程序。这样屏蔽多个app的同时处理。不过不一定有权限

    // 注册一个本地intent receiver,用于接收在manifest文件中注册的receiver
    //媒体按键按下动作
    activityMediaControlReceiver = new ActivityMediaControlReceiver();
    IntentFilter filter = new IntentFilter(MediaControlReceiver.ACTION_MEDIA_BUTTON);

    registerReceiver(activityMediaControlReceiver, filter);
    Log.v("媒体按键监听", "注册了间接媒体按键监听器");
    //拔出耳机的广播接收器
    IntentFilter noiseFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
    registerReceiver(new NoisyAudioStreamReceiver(), noiseFilter);
    Log.v("媒体按键监听", "注册了耳机拔出监听器");
  }

其中间接广播接收器的定义如下,主要完成音频的播放、暂停、停止、音量调整、上一首、下一首。

//媒体按键BroadcastReceiver的实现,接收设备按键,控制音频播放
  public class ActivityMediaControlReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
      if (MediaControlReceiver.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
        KeyEvent event =(KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);   //
        if(event.getAction()!=KeyEvent.ACTION_UP)   //这里只处理弹起事件
            return;
        Log.v("简介媒体按键监听器", "接收到按键"+event);
        switch (event.getKeyCode()) {
          case (KeyEvent.KEYCODE_HEADSETHOOK) :  //接听/挂断 电话,播放/暂停音乐视频  多功能按键
              if (mediaPlayer.isPlaying())
              {
                pause();
                Log.v("音频播放", "暂停");    
              }
              else
              {
                play();
                Log.v("音频播放", "播放");    
              }
              break;
          case (KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) :    //如果是暂停或暂停键就控制音频播放或暂停
            if (mediaPlayer.isPlaying())
            {
                pause();
                Log.v("音频播放", "暂停");    
            }
            else
            {
                play();
                Log.v("音频播放", "播放");    
            }
            break;
          case (KeyEvent.KEYCODE_MEDIA_PLAY) :    //如果是播放键,就播放
            play(); break;
          case (KeyEvent.KEYCODE_MEDIA_PAUSE) :  //如果是暂停键,就暂停
            pause(); break;
          case (KeyEvent.KEYCODE_MEDIA_NEXT) :   //如果是下一首,就跳转到下一首
            skip(); break;
          case (KeyEvent.KEYCODE_MEDIA_PREVIOUS) :   //如果是上一首,就跳转到上一首
            previous(); break;
          case (KeyEvent.KEYCODE_MEDIA_STOP) :    //如果是停止键,就停止
            stop(); break;
          default: break;
        }
      }
    }
  }

耳机拔出事件的广播接收器如下

  //当拔出耳机时暂停播放
  private class NoisyAudioStreamReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
      if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
        pause();
        Log.v("媒体按键监听", "耳机拔出");
      }
    }
  }

音频的播放和停止设计的音频焦点的设计。由于在安卓系统中只允许一个窗口获取当前音频焦点。所以在开始播放音频时,应该主动获取音频焦点,以便其他播放器app,停止音频播放,在丢失音频焦点时,主动放弃音频播放。

定义一个音频焦点的变更事件

 //音频焦点变化响应。在用户设备有多个媒体播放app时,非活动状态app交出媒体按键控制权。活动状态app获取媒体按键控制权
  //分为暂时丢失、丢失、永久丢失。这是安装系统的架构
  private OnAudioFocusChangeListener focusChangeListener =  new OnAudioFocusChangeListener() {

    public void onAudioFocusChange(int focusChange) {

      AudioManager am = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
      Log.v("按键广播监听", "音频焦点变化"+focusChange);
      switch (focusChange) {
        case (AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) :   //暂时丢失,支持音量降低
          //降低音量
          mediaPlayer.setVolume(0.2f, 0.2f);
          break;
          //暂停
        case (AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) :   //丢失
          pause();
          break;
          //停止播放
        case (AudioManager.AUDIOFOCUS_LOSS) :  //永久丢失
          stop();
          ComponentName component = new ComponentName(AudioPlayerActivity.this,MediaControlReceiver.class);
          am.unregisterMediaButtonEventReceiver(component);  //注销广播响应程序
          break;

        case (AudioManager.AUDIOFOCUS_GAIN) :   //暂时丢失后,重获焦点
          //将音量恢复到正常大小,并且如果音频流已被暂停,则恢复音频流
          mediaPlayer.setVolume(1f, 1f);
          mediaPlayer.start();
          break;

        default: break;
      }
    }
  };

音频播放函数

  //请求音频焦点(音频焦点只能由一个app获取)。
  public void play() {    
    AudioManager am = (AudioManager)getSystemService(Context.AUDIO_SERVICE);

    //请求音频焦点
    int result = am.requestAudioFocus(focusChangeListener,  //(音频焦点丢失响应)
                   //使用音频流
                   AudioManager.STREAM_MUSIC,
                   //请求永久焦点
                   AudioManager.AUDIOFOCUS_GAIN);

    if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {   //如果焦点请求成功
       mediaPlayer.start();  //播放音频
    }

音频停止

//音频播放完成后,放弃音频焦点。
  public void stop() {
    mediaPlayer.stop();
    Log.v("音频播放", "放弃音频焦点");
    //放弃音频焦点
    AudioManager am = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
    am.abandonAudioFocus(focusChangeListener);
  }

音频的暂停、上一首、下一首就根据自己想要的的功能来实现

  //暂停函数,包含设置远程控制窗口
  public void pause() {
    mediaPlayer.pause();
    myRemoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_PAUSED);
  }

  //跳到下一首
  public void skip() {
    //自己实现
  }

  //跳到前一首
  public void previous() {
    //自己实现
  }
作者:luanpeng825485697 发表于2017/11/21 20:55:49 原文链接
阅读:28 评论:0 查看评论

Java并发编程札记-(一)基础-06synchronized详解

$
0
0

Java并发编程札记-(一)基础-05线程安全问题一文中已经学习了什么是线程安全以及实现线程安全的方法。今天就来学习下其中的一种方法——显示锁synchronized。

Java中每个对象都有且只有一个内置锁。通过synchronized修饰代码片段,可以在其上加锁。当任务运行到对象的被synchronized修饰的代码片段时,任务可以获取到对象的锁。当获取到对象的锁后,其他任务在锁被释放前就不能再进入该对象的synchronized代码片段。当任务执行完synchronized代码片段或者抛出异常,会自动释放锁。

synchronized是Java中的关键字,是一种同步锁。它可以修饰以下几种代码片段:

  1. 方法。作用范围是整个方法,作用的对象是调用这个方法的对象;
    3.. 代码块。作用范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
  2. 静态方法。作用范围是整个静态方法,作用的对象是这个类的所有对象;
  3. 类。作用范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象。

synchronized修饰方法

synchronized修饰方法很简单,只需要在方法声明中添加synchronized关键字。格式如下

public synchronized void method(){
    //方法体
}

稍许修改下Java并发编程札记-(一)基础-05线程安全问题中的“例1:火车票订票系统-线程不安全版”中的代码,用synchronized修饰售票的方法,就可以使这个例子变为线程安全的。
例1:火车票订票系统-线程不安全版

public class SellTickets {

    public static void main(String[] args) {
        TicketsWindow tw = new TicketsWindow();
        Thread t1 = new Thread(tw, "一号窗口");
        Thread t2 = new Thread(tw, "二号窗口");
        t1.start();
        t2.start();
    }
}

class TicketsWindow implements Runnable {
    private int tickets = 1;

    @Override
    public void run() {
        while (true) {
            if (tickets > 0) {
                System.out.println(Thread.currentThread().getName() + "还剩余票:" + tickets + "张");
                tickets--;
                System.out.println(Thread.currentThread().getName() + "卖出一张火车票,还剩" + tickets + "张");
            } else {
                System.out.println(Thread.currentThread().getName() + "余票不足,暂停出售!");
                try {
                    Thread.sleep(1000 * 60 * 5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

运行结果为:

一号窗口还剩余票:1张
二号窗口还剩余票:1张
一号窗口卖出一张火车票,还剩0张
二号窗口卖出一张火车票,还剩-1张
一号窗口余票不足,暂停出售!
二号窗口余票不足,暂停出售!

余票只有一张,但最后卖出了两张火车票。这明显不是我们想要的结果。
当线程t1,即“一号窗口”执行run方法进行售票时,t2“二号窗口”也在执行run方法进行售票。下面是两个线程的执行顺序。

  1. 一号窗口读出某班次的火车票余票A,设A=1;
  2. 二号窗口读出同一班次的火车票余票B,当然也为1;
  3. 一号窗口判断出余票A=1>0,卖出一张火车票,修改余票A←A-1,A为0,把A写回数据库;
  4. 二号窗口判断出余票B=1>0,也卖出一张火车票,修改余票B←B-1,B为-1;

例2:火车票订票系统-synchronized修饰方法线程安全版
现在给run()方法添加synchronized修饰符,即将public void run()改为public synchronized void run(),再次运行,会发现结果变为:

一号窗口还剩余票:1张
一号窗口卖出一张火车票,还剩0张
一号窗口余票不足,暂停出售!

这是因为synchronized为run方法加了锁,当线程t1,即“一号窗口”执行run方法时,就获取到了对象tw的锁,所以线程t2“二号窗口”就无法执行run方法了,这样就不会线程t1所做的修改就不会被覆盖,结果自然是正确的了。

synchronized修饰代码块

synchronized不仅可以修饰方法,还可以修饰代码块。这样synchronized的使用变得灵活许多,因为也许一个方法中只有一部分代码需要同步,如果此时对整个方法进行同步,会影响执行效率。格式如下

synchronized(synObject) {
    //方法体
}

synObject可以是this,代表获取当前对象的锁;也可以是类中的一个属性对象,代表获取该属性对象的锁。
例3:火车票订票系统-synchronized修饰代码块线程安全版

@Override
public synchronized void run() {
    while (true) {
        if (tickets > 0) {
            synchronized (this) {
                System.out.println(Thread.currentThread().getName() + "还剩余票:" + tickets + "张");
                tickets--;
                System.out.println(Thread.currentThread().getName() + "卖出一张火车票,还剩" + tickets + "张");
            }
        } else {
            System.out.println(Thread.currentThread().getName() + "余票不足,暂停出售!");
            try {
                Thread.sleep(1000 * 60 * 5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

一个对象有一个锁,多个对象是有多个锁的,如果多个线程访问多个对象,怎么实现同步呢?这时就需要了解如何使多个对象共享一个锁。

synchronized修饰静态方法

synchronized还可以修饰静态方法。为什么要把静态方法和方法区分开呢?众所周知,静态方法属于类不属于对象,因此synchronized修饰静态方法锁定的是这个类的所有对象。格式如下

public synchronized static void method() {
    //方法体
}

例4:火车票订票系统-synchronized修饰静态方法线程安全版

public class SellTickets {

    public static void main(String[] args) {
        TicketsWindow tw1 = new TicketsWindow();
        TicketsWindow tw2 = new TicketsWindow();
        Thread t1 = new Thread(tw1, "一号窗口");
        Thread t2 = new Thread(tw2, "二号窗口");
        t1.start();
        t2.start();
    }
}

class TicketsWindow implements Runnable {
    private static int tickets = 1;

    @Override
    public synchronized void run() {
        sellTicket();
    }
    public synchronized static void sellTicket() {
        while (true) {
            if (tickets > 0) {
                System.out.println(Thread.currentThread().getName() + "还剩余票:" + tickets + "张");
                --tickets;
                System.out.println(Thread.currentThread().getName() + "卖出一张火车票,还剩" + tickets + "张");
            } else {
                System.out.println(Thread.currentThread().getName() + "余票不足,暂停出售!");
                try {
                    Thread.sleep(1000*60);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

这个例子和前面的例子的最大区别是有两个任务tw1和tw2,但在t1和t2并发执行时却保持了线程同步。这是因为run中调用了静态方法sellTicket,而静态方法是属于类TicketsWindow的,所以tw1和tw2共用了类TicketsWindow的锁。

synchronized修饰类

synchronized还可以修饰类。与synchronized修饰静态方法效果相同,锁定的是这个类的所有对象。格式如下

class ClassName {
    public void method() {
        synchronized(ClassName.class) {
        //方法体
        }
    }
}

例5:火车票订票系统-synchronized修饰类线程安全版

public class SellTickets {

    public static void main(String[] args) {
        TicketsWindow tw1 = new TicketsWindow();
        TicketsWindow tw2 = new TicketsWindow();
        Thread t1 = new Thread(tw1, "一号窗口");
        Thread t2 = new Thread(tw2, "二号窗口");
        t1.start();
        t2.start();
    }
}

class TicketsWindow implements Runnable {
    private int tickets = 1;

    @Override
    public synchronized void run() {
        synchronized (SyncThread.class) {
            while (true) {
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName() + "还剩余票:" + tickets + "张");
                    --tickets;
                    System.out.println(Thread.currentThread().getName() + "卖出一张火车票,还剩" + tickets + "张");
                } else {
                    System.out.println(Thread.currentThread().getName() + "余票不足,暂停出售!");
                    try {
                        Thread.sleep(1000 * 60);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

和例2相同,两个任务tw1和tw2能在t1和t2并发执行时保持了线程同步,是因为tw1和tw2共用了类TicketsWindow的锁。

注意事项

将域设置为private
在使用并发时,要将域设置为private,否则synchronized就不能阻止其他任务直接访问域,这样可能会产生不可预知的结果。

一个任务可以多次获得对象的锁
如果一个任务在同一个对象上调用了第二个方法,后者又调用了同一个对象上的第三个方法,这个任务就会多次获取这个对象的锁。每当任务执行所有的方法,锁才被完全释放。

class PrintClass {
    public synchronized void func1() {
        System.out.println("func1()");
        func2();
    }

    public synchronized void func2() {
        System.out.println("func2()");
        func3();
    }

    public synchronized void func3() {
        System.out.println("func3()");
    }
}

class MyThread extends Thread {
    public void run() {
        PrintClass pc = new PrintClass();
        pc.func1();
    }
}

public class Demo {

    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start();
    }
}

打印结果为:

func1()
func2()
func3()

每个访问临界资源的方法都必须被同步
如果在你的类中有超过一个方法在处理临界数据,那么必须同步所有的方法。如果只同步一个方法,其他方法可以忽略这个锁。所以,每个访问临界资源的方法都必须被同步。

异常自动释放锁
当一个线程执行的代码出现异常时,其所持有的锁会自动释放。

本文就讲到这里,想了解更多内容请参考:

END.

作者:panweiwei1994 发表于2017/11/21 21:27:16 原文链接
阅读:23 评论:0 查看评论

第六章 关系数据理论 范式

$
0
0

这一章的重点放在范式,主要内容如下:
这里写图片描述
何为范式 ?

范式是“符合某一种级别的关系模式的集合,表示一个关系内部各属性之间的联系的合理化程度”。说白了就是一张数据表的表结构所符合的某种设计标准的级别。范式级别越高,表的设计就越标准。

第一范式(1NF)

第一方式是数据库表需要符合的最基本条件:表的每一个属性不能是再可分的数据项。
如一张表是这样的:
这里写图片描述
其中属性进货还可以再分,所以这张表的设计不符合第一范式。
改为:
这里写图片描述
就符合了第一范式的要求了。

仅仅符合第一范式的表还是有很多问题的,如下数据表:

这里写图片描述

  1. 每一名学生的学号、姓名、系名、系主任这些数据重复多次。每个系与对应的系主任的数据也重复多次——数据冗余过大。

  2. 假如学校新建了一个系,但是暂时还没有招收任何学生(比如3月份就新建了,但要等到8月份才招生),那么是无法将系名与系主任的数据单独地添加到数据表中去的 ——插入异常

  3. 假如将某个系中所有学生相关的记录都删除,那么所有系与系主任的数据也就随之消失了(一个系所有学生都没有了,并不表示这个系就没有了)。——删除异常

  4. 假如李小明转系到法律系,那么为了保证数据库中数据的一致性,需要修改三条记录中系与系主任的数据。——修改异常

正因为仅符合1NF的数据库设计存在着这样那样的问题,我们需要提高设计标准,去掉导致上述四种问题的因素,使其符合更高一级的范式(2NF),这就是所谓的“规范化”。

关于第二范式的一些先修概念

函数依赖:若在一张表中,在属性(或属性组)X的值确定的情况下,必定能确定属性Y的值,那么就可以说Y函数依赖于X,写作 X → Y。

如有一张学生表Student(sno,sname , sage) , 学号sno确定的情况下,姓名sname可以唯一确定,所以可以有 学号->姓名。但是“姓名->学号“是不成立的,因为学生可能同名。
对于Student表,还可以有“学号sno->年龄sage“。

完全函数依赖:在一张表中,若 X → Y,且对于 X 的任何一个真子集(假如属性组 X 包含超过一个属性的话)X1,X1 → Y 不成立,那么我们称 Y 对于 X 完全函数依赖。

对于上面的Student表,可以说姓名sname完全依赖于学号sno:”sno->sname”。

部分函数依赖:在一张表中,若X -> Y , 存在一个X的真子集X1,使得X1->Y,那么就可以说Y部分依赖于X。

如有选课的表sc(sno,cno , grade) 。(sno , cno) 可以确定 grade , 记做(sno , cno)->grade 。但同时也存在着sno->grade,所以可以说grade部分依赖于(sno , cno)。

传递函数依赖:若Y->Z , X->Y, 那么有X->Z (Z 依赖于 X ) 。
如一个student表 ,student(sno,deptNo , deptName)。学号sno->系号deptno , 系号deptNo -> 系名称deptName , 那么有sno -> deptName 。

:若一个表中的属性集合K能够确定表中的所有其他属性 , 那么称K为关系表的码。关系表的码可能不止一个。包含于码中的任一属性叫主属性,不包含与码中的属性叫非主属性

如上面说的选修表sc(sno , cno , grade) , (sno , cno )就是sc的一个码 , grade是非主属性 , sno 、cno可以叫表的主属性。

第二范式(2NF)

终于可以回过来说第二范式了。

第二范式就是在第一范式的基础之上消除了非主属性对码的部分依赖 。

如有关系模式R(sno,dept,sloc,cno , grade)。sno学号,dept为系,sloc为住处,cno课程号,grade成绩。关系R的码为(sno,cno), 存在这样的函数依赖:

(sno,cno)->grade
sno->dept , (sno,cno)->dept
sno->sloc , (sno,cno)->sloc
dept->sloc(一个系只有一个住处)

由(sno,cno)->dept , sno->sdept可知存在部分依赖,同理sloc部分依赖于(sno,cno)。这个关系模式R不属于第二范式2NF。

第二范式仍然存在以下问题:

  1. 插入异常:若要插入一个学生,sno = 18 , dept=’计算机’ , sloc=’35栋’ ,但是由于该学生的课还没选好,这样整个的一条学生的记录都无法插入。

  2. 删除异常:若一个学生选修了一门课C3 , 现在这门课他不选了, 那么这条记录都要删除掉,把学生的其他不该删除的信息也删除了。

  3. 修改复杂:若一个学生转系了,本来只要修改dept即可,但是在这张表里还有修改sloc(住处),若学生修改了多门课程,那么多条记录都要发生修改,就造成了修改复杂。

可以看出符合2NF的表设计也还有很多问题的。

第三范式 3NF

第三范式就是在第二范式的基础上再消除了非主属性对码的传递依赖。

有关系R(sno , sdept , deptName) 。sno->sdept , sdept->deptName 。

码:sno
非主属性:sdept , deptName。
由sno->sdept , sdept->deotName得:sno->deptName 。
存在传递依赖 ,不符合3NF。
分解关系R为:R1(sno , sdept)和 R2(sdept , deptName) ,分解后的关系就符合了3NF 。

修正的第三范式 BCNF

BCNF就是在第三范式的基础上消除了主属性对码的部分依赖和传递依赖。

存在关系模式STJ(S , T , J),S是学生,T是教师 , J是课程。一个老师只带一门课 , 一门课有多个老师可以上,一个学生只有一个固定的老师。

那么有如下关系:
(S , J )->T , (S , T)->J , T->J
这里(S , J)和(S , T)都是候选码。
码:S , T ,J 。
非主属性:无。
可以看出关系STJ各项不可再分(符合1NF),没有非主属性 (符合2NF和3NF)。
由(S , T)->J , T->J知主属性对码(S,T)存在部分依赖所以关系STJ不符合BCNF 。

可以通过分解STJ为 R1(S,T) , R2(T,J),此时R1、R2都符合BCNF。

各范式的关系

再看第一张图

参考
1、 知乎高赞回答:https://www.zhihu.com/question/24696366
2、 数据库系统概论 第五版。

作者:csdn_blog_lcl 发表于2017/11/21 21:33:06 原文链接
阅读:18 评论:0 查看评论

python设计模式之门面模式

$
0
0


一、理解结构型设计模式

          1.结构型模式描述如何将对象和类组合成更大的结构

        2.结构型模式是一种能够简化设计工作的模式,因为它能够找出更简单的方法来认识或表示实体之间的关系。在面向对象世界中,实体指的是对象或类。

        3.类模式可以通过继承来描述抽象,从而提供更有用的程序接口,而对象模式则描述了如何将对象联系起来从而组成更大的对象。结构型模式是类和对象模式的综合体。


二、理解门面设计模式

        1.它为子系统中的一组接口提供一个统一的接口,并定义一个高级接口来帮助客户用过简单的方式使用子系统。

        2.门面所解决问题是,如何用单个接口对象来表示复杂的子系统。实际上,他并不是封装子系统,而是对底层子系统进行组合。


三、UML图


           1. 门面:门面的主要责任是将一组复杂的系统封装起来,从而为外面世界提供一个舒适的外观

                      1.1 它是一个接口,它知道某个请求可以交由哪个子系统进行处理。

                      1.2 它使用组合将客户端的请求委托给相应的子系统对象。

           2. 系统:这代表一组不同的子系统,使整个系统混杂在一起,难以观察或使用。

                      2.1 它实现子系统的功能,同时,系统由一个类表示。理想情况下,系统应该由一组负责不同的任务的类来表示。

                      2.2 它处理门面对象分配的工作,但并不知道门面,而且不引用它。

           3. 客户端:客户端与门面交互,这样就可以轻松地与子系统进行通信并完成工作了。不必担心系统的复杂性。

                      3.1 客户端是实例化门面的类。

                      3.2 为了让子系统完成相应的工作,客户端需要向门面提出请求。 


四、例子

# -*- coding: UTF-8 -*-

# 门面类
class EventManager(object):
    def __init__(self):
        print("Event Manager: Let me talk to the folks")

    def arrage(self):
        self.hotelier = Hotelier() # 酒店类
        self.hotelier.bookHotel() # 检查当天是不是由免费的酒店

        self.florist = Florist() # 鲜花类
        self.florist.setFlowerRequirements() # 用于指定要使用哪种画来装饰婚礼

        self.caterer = Caterer() # 宴席承办人类
        self.caterer.setCuisine() # 指定酒店的饭菜类型

        self.musician = Musician() # 婚礼音乐类
        self.musician.setMusicType() #音乐要求


# 事件
class Hotelier(object):
    def __init__(self):
        print("安排酒店结婚? --")

    def __isAvilable(self):
        print("免费就结,不免费不结了?")
        return True

    def bookHotel(self):
        if self.__isAvilable():
            print("给老子订一间房")

# 事件
class Florist(object):
    def __init__(self):
        print("鲜花事件")

    def setFlowerRequirements(self):
        print("九十九朵浪漫的玫瑰吧")

# 事件
class Caterer(object):
    def __init__(self):
        print("宴席承办人事件")

    def setCuisine(self):
        print("中国菜和印度菜将被提供")

# 事件
class Musician(object):
    def __init__(self):
        print("婚礼音乐事件")

    def setMusicType(self):
        print("播放《今天你要嫁给我》")

# 事件
class You(object):
    def __init__(self):
        print("你:婚礼安排")

    def askEventManager(self):
        print("你:让我和活动经理联系下")
        em = EventManager()
        em.arrage()

    def __del__(self):
        print("感谢你活动经历,所有东西都安排妥当了")

you = You()
you.askEventManager()









五、最少知识原则

        最少知识原则:减少对象之间的交互。

               1.在设计系统是,对于创建的每个对象,都应该考察与之狡猾的类的逻辑,以及交互的方式。

               2.遵循这个原则,就能够避免创建许多彼此紧密耦合的类的情况。

               3.如果类之间存在大量依赖关系,那么系统就会变得难以维护。如果对系统中的任务一部分进行修改,都可能导致系统的其他部分被无意改变,这意味着系统会退化,                   是应该被避免的。


六、常见问答

        1. 迪米特法则是什么,它与工厂模式有何关联?

               1.1 迪米特法则:

                         1.1.1 每个单元对系统中其他单元知道的越少越好。

                         1.1.2 单元应该只与朋友交流。

                         1.1.3 单元不应该知道它操作的对象的内部细节。

                1.2 最少知识原则和迪米特法则是一致的,都是指向送耦合理论。就像他的名称暗示的那样,最少知识原则适用于门面模式的用例,并且“原则”这个词是指导方针的意                       思,不是严格遵守的意思,并且只有在修的时候采用。

        2. 子系统可以有多个门面吗?

                一组子系统组件实现多个门面

        3. 最少知识原则的缺点是什么?

               门面提供了一个简化的接口供客户端与子系统交互。本着提供简化接口的精神应用可能会建立多个不必要的接口,这增加了系统的复杂性并且降低了运行时的性能。

        4. 客户端可以独立访问子系统吗?

               是的,实际上,由于门面模式提供了简化的接口,这使得客户端不必担心系统的复杂性。

        5. 门面是否可以添加自己的功能?

               门面可以将其“想法”添加到子系统中,例如确保子系统的改进顺序由门面来jued

作者:u013584315 发表于2017/11/21 22:33:41 原文链接
阅读:3 评论:0 查看评论

Python3与OpenCV3.3 图像处理(五)--图像运算

$
0
0

一、本节简介

图像运算也就是像素运算,简单的说就是利用算术运算或逻辑运算,对图像的每个像素进行处理(例如两个图像的合并)。虽然我们可以像第二节课那样,一个像素一个像素的遍历并修改值,但是如果图像分辨率很大的情况下,会处理的很慢,并且处理一些复杂的运算时,我们的代码效率会变得更低,代码编写出来也变得很麻烦。这节课就来讲解以下OpenCV中对图像运算的方法。

注意:我们在处理两个图像时,图像的像素大小和类型要完全一致,否则OpenCV就会报错。


二、算术运算

图像算术运算就是对两个图像的每个像素点执行加减乘除的运算,从而得到一个新的图像。代码如下

def add(image1,image2):
    """图片相加"""
    dst=cv.add(image1,image2)
    cv.imshow("add image",dst)


def subtract(image1,image2):
    """图片相减"""
    dst=cv.subtract(image1,image2)
    cv.imshow("subtract image",dst)


def divide(image1,image2):
    """图片相除"""
    dst=cv.divide(image1,image2)
    cv.imshow("divide image",dst)


def multiply(image1,image2):
    """图片相乘"""
    dst=cv.multiply(image1,image2)
    cv.imshow("multiply image",dst)

三、逻辑运算

图像的逻辑运算就是对图像的每个像素点执行与或非的运算,从而得到一个新的图片,代码如下

def logic(image1,image2):
    """逻辑运算"""
    #与操作
    dst=cv.bitwise_and(image1,image2)
    cv.imshow("logic",dst)
    # 或操作(与相加操作类似)
    dst = cv.bitwise_or(image1, image2)
    cv.imshow("logic", dst)
    # 非操作(像素取反)
    dst = cv.bitwise_not(image1)
    cv.imshow("logic", dst)

四、其他算数运算

def others(image1,image2):
    #计算每个通道的平均值
    m1= cv.mean(image1)
    m2 = cv.mean(image2)
    #计算每个通道的平均值和方差
    m1,dev1=cv.meanStdDev(image1)
    m2,dev2=cv.meanStdDev(image2)
    print(m1,dev1)
    print(m2,dev2)

五、简单的Demo

def contrast_brightness(image,c,b):
    """
    修改亮度和对比度
    c:对比度
    b:亮度
    """
   #获取图片的高、宽和通道数
    h,w,ch=image.shape
    #创建一个全黑色的图片
    blank=np.zeros([h,w,ch],image.dtype)
    #调整亮度和对比度
    dst=cv.addWeighted(image,c,blank,1-c,b)
    cv.imshow("con-bri",dst)


作者:gangzhucoll 发表于2017/11/21 23:49:43 原文链接
阅读:1 评论:0 查看评论

【玩转树莓派】使用 sinopia 搭建私有 npm 服务器

$
0
0

简介

效果展示

使用 sinopia 的好处是,node系的工程师,内部协作时,使用自有 npm 包,会非常方便;另外,sinopia,会缓存已经下载过的包,可以在相当程度上,加速 npm install 相关命令的执行。

工作中,确实有需要用到 sinopia 来作为私有 npm 服务器的场景。原来一直在自己电脑上开启 sinopia。这样做最大的问题是,sinopia 后台一直开着,会越来越耗费资源,电脑最后会变得很卡。偶尔,还会因为忘记开启或关闭 sinopia,带来各种不便利。

今天我试着直接在树莓派上搭建一个 sinopia 服务。最终实现的效果较为完整,基本满足需要了。包含用户权限管理,外网使用自定义域名访问,sinopia服务开机自启等特性。

注意:以下 shell 命令,默认在树莓派的shell中执行,而不是在本机电脑上。

安装最新长期支持版 node 环境

树莓派自带的 node 环境是 v4.8.2,有必要升级下。

安装 nvm

建议安装 nvm,以方便管理多个版本的 node 环境。

# 安装 nvm
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.6/install.sh | bash

# 重启shell

# 验证 nvm 安装
command -v nvm

使用 nvm 安装最新长期支持版 node 环境

# 安装 Node
nvm install --lts

#验证安装  --> v8.9.1
node -v

安装和配置 sinopia

安装 sinopia

# 安装
npm install -g sinopia

# 验证是否安装成功 --> 这一步会输出自动生成的配置文件路径等信息。
sinopia

解决端口 4873 占用问题

sinopia 启动时,默认使用 4873端口,可能会遇到端口冲突问题。

# 安装 lsof 命令
sudo apt-get update
sudo apt-get install lsof

# 查看端口占用进程 PID
lsof -i :4873

# 杀死占用 4873 端口的进程。4649,要换为实际的 PID。
kill -9 4649

注册一个默认账户

为了提高安全性,我们稍后会禁用 sinopia 的用户注册功能,所以先注册一个默认的 sinopia 账户。需要在当前 shell 中执行 sinopia 命令开启服务之后,再重新打开一个 shell 执行:

npm set registry http://localhost:4873/
npm adduser --registry http://localhost:4873/

用户名,密码,邮箱等,要记牢,适当设置的复杂点。

### 升级安装 vim

感觉树莓派自带的 vim 不太好使了,我也顺便升级了下。

“`
# 安装 vim
sudo apt-get update
sudo apt-get install vim

# 配置支持vim中鼠标右键复制
vim ~/.vimrc
“`

在 .vimrc 此文件中增加如下一行:

shell
set mouse=v

配置 sinopia

配置文件路径可以在执行 sinopia 命令时,从其输出中查看,一般应是 /home/pi/.config/sinopia/config.yaml

基于我的使用使用经验和文档说明,主要配置了以下内容:

  • max_users: -1 :禁用注册。
  • npmjs: url: https://registry.npm.taobao.org : 设置 npm 镜像为淘宝源,一来可以加速 npm 公共包的安装,二来借助淘宝源的只读特性,避免误操作发布私有 npm 包到外网上。
  • access: $authenticated:禁止匿名用户访问。配置后,未登录用户看不到 sinopia 上私有包的任何信息。
  • max_body_size: ‘200mb’:这样设置,会提高安装超级 npm 包的成功率,比如 react-native 。

完整配置内容如下。如果你不是在树莓派上配置,请把 /home/pi 替换为自己真实的用户路径名。

#
# This is the default config file. It allows all users to do anything,
# so don't use it on production systems.
#
# Look here for more config file examples:
# https://github.com/rlidwka/sinopia/tree/master/conf
#

# path to a directory with all packages
storage: /home/pi/.local/share/sinopia/storage

auth:
  htpasswd:
    file: ./htpasswd
    # Maximum amount of users allowed to register, defaults to "+inf".
    # You can set this to -1 to disable registration.
    max_users: -1

# a list of other known repositories we can talk to

uplinks:
  npmjs:
    url: https://registry.npm.taobao.org

packages:
  '@*/*':
    # scoped packages
    access: $authenticated
    publish: $authenticated

  '*':
    # allow all users (including non-authenticated users) to read and
    # publish all packages
    #
    # you can specify usernames/groupnames (depending on your auth plugin)
    # and three keywords: "$all", "$anonymous", "$authenticated"
    access: $authenticated

    # allow all known users to publish packages
    # (anyone can register by default, remember?)
    publish: $authenticated

    # if package is not available locally, proxy requests to 'npmjs' registry
    proxy: npmjs

# log settings
logs:
  - {type: stdout, format: pretty, level: http}
  #- {type: file, path: sinopia.log, level: info}

max_body_size: '200mb'

可以在本地编辑器中修改好配置,然后直接复制到树莓派上:

# 打开配置文件
vim /home/pi/.config/sinopia/config.yaml

使用粘贴命令。直接粘贴,格式会错乱。

:set paste
i
# 右键粘贴即可。

配置frpc 远程访问

关于 frp 的配置问题,详见:【小技巧解决大问题】使用 frp 突破阿里云主机无弹性公网 IP 不能用作 Web 服务器的限制。此处只贴出 frpc 的关键配置变更:

[web-sinopia]
type = http
local_port = 4873
subdomain = sinopia

使用 Systemd 实现 sinopia 服务开机自启

树莓派,默认是带有 Systemd 的,直接使用即可:

sudo vim /usr/lib/systemd/system/sinopia.service

sinopia.service 具体内容如下,其中/home/pi/.config/sinopia/config.yaml 要替换为自己的 config.yaml 地址:

[Unit]
DescrIPtion=sinopia
After=network.target

[Service]
TimeoutStartSec=30
ExecStart=/home/pi/.nvm/versions/node/v8.9.1/bin/sinopia /home/pi/.config/sinopia/config.yaml
ExecStop=/bin/kill $MAINPID
Restart=on-failure
RestartSec=42s

[Install]
WantedBy=multi-user.target

启动 sinopia 并设置开机启动:

systemctl enable sinopia
systemctl start sinopia
systemctl status sinopia

其他可能有用的命令

# 禁用服务
systemctl disable sinopia

# 重新启动服务
systemctl restart sinopia

在另一台电脑上使用 sinopia 私有 npm 服务器功能

假定,最终的 sinopia 服务器的外网地址是: http://sinopia.example.com

真正想使用,需要在终端中配置下:

npm set registry http://sinopia.example.com
npm adduser --registry http://sinopia.example.com
npm login

配置完毕后,你可以试着发布一个私有 npm 包:

# 在某个文件夹初始化一个新的 npm 包
npm init

# 发布到私有 sinopia 服务器:
npm publish

发布成功后,在浏览器中登录 http://sinopia.example.com,刷新页面,应该就能看到自己刚发布的那个包了。

注意,其他用户在使用私有库上的包时,也应该先登录,否则会报错:

unregistered users are not allowed to access package

参考文档

作者:sinat_30800357 发表于2017/11/22 2:17:06 原文链接
阅读:1 评论:0 查看评论

Java的线程异常处理器UncaughtExceptionHandler

$
0
0

在Thread类中有个有个内部接口UncaughtExceptionHandler,这个接口定义了线程运行时遇到未被捕获的异常的规范,一般不设置这个处理器的时候,线程如果遇到未被捕获的异常则会终止。设置这个处理器之后可以用于统一记录线程抛出的异常。

在Thread中有2个方法可以设置未捕获的异常处理:

  1. setDefaultUncaughtExceptionHandler()这个方法是静态,用于设置一个默认的全局异常处理器。
  2. setUncaughtExceptionHandler()这个方法是针对某个Thread对象的,可以用于对特定的线程进行未捕获的异常处理。

下面展示一个由于异常未被捕获而导致线程终止的例子:

/**
 * 线程由于异常未被捕获导致线程终止的例子
 * @author RJH
 * 2017年11月21日
 */
public class TerminatedByUncaughtExceptionDemo {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {

            private int  i = 0;

            @Override
            public void run() {
                while (true) {//死循环自增
                    ++i;
                    if (i > 2) {//大于2则抛出异常
                        throw new RuntimeException("i is bigger than 2");
                    }
                }
            }
        });
        thread.start();
        //线程如果依然存活,则死循环
        while (thread.isAlive()) {

        }
        //此时线程已终止,状态为TERMINATED
        System.out.println(thread.getState());
    }
}

运行结果:

Exception in thread "Thread-0" java.lang.RuntimeException: i is bigger than 2
    at com.rjh.thread.TerminatedByUncaughtExceptionDemo$1.run(TerminatedByUncaughtExceptionDemo.java:14)
    at java.lang.Thread.run(Thread.java:745)
TERMINATED

这里需要注意Thread和Runnable中只能抛出RuntimeException,这是由于Runnable中的run()方法定义所导致的。

下面就用这个异常处理器实现一个模拟全局的日志记录的例子,这个例子的注释都比较齐全,大家只要看着注释基本就能明白了:

import java.lang.Thread.UncaughtExceptionHandler;
/**
 * 线程异常处理器的例子
 * @author RJH 
 * @date 2017年11月21日 下午9:32:13
 */
public class UncaughtExceptionHandlerDemo {
    public static void main(String[] args) {
        //设置一个全局的异常处理器
        Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {

            @Override
            public void uncaughtException(Thread t, Throwable e) {
                //用系统异常输出流输出类似日志的信息(其实就是模拟日志),格式是 时间戳 线程名称 异常名称 异常信息
                //这里在实际开发的时候要记得把异常堆栈也打印出来,这样便于排查问题
                System.err.println(String.format("%d %s %s",System.currentTimeMillis(),t.getName(),e.toString()));
            }
        });
        //通过for循环启动多个线程
        for(int i=0;i<5;i++){
            Thread t=new ExceptionThread();
            t.start();
        }
    }
    /**
     * 抛出异常的线程,一般情况下最好还是不要继承Thread,建议是实现Runnable接口
     * @author RJH 
     * @date 2017年11月21日 下午9:36:46
     */
    private static class ExceptionThread extends Thread{

        private int i=10;

        @Override
        public void run(){
            while(10/i>0){//i不断自减,最后会抛出一个ArithmeticException
                --i;
            }
        }
    }
}
作者:a158123 发表于2017/11/21 22:02:26 原文链接
阅读:1 评论:0 查看评论

UIAutomator2.0详解(UIDevice篇----performActionAndWait)

$
0
0

这里写图片描述

方法含义:执行action后,判断timeout时间内,是否出现Event。若未出现,相当于等待timeout毫秒。若出现,则结束等待,执行后续语句。

方法返回值:布尔型,若有Event发生,则返回true,否则返回false。

EventCondition是一个抽象类,可用通过 android.support.test.uiautomator.Until中提供的两个静态方法,来获取实体对象。

public static EventCondition<Boolean> newWindow() 
public static EventCondition<Boolean> scrollFinished(final Direction direction) 

这里写图片描述

查看源码,可发现newWindow()是指发生window状态变化,或内容变化所引发的Event。

这里写图片描述

而scrollFinished,则是当当滑动到某一方向尽头时所引发的Event。

还是用一个示例来看一下效果。先用newWindow为EventCondition.

核心代码如下:

public class WaitConditionTest extends UIDeviceTest {

    @Test
    public void test1(){
        String packageName="com.breakloop.salaryshow";
        String activityName=".MainActivity";

        boolean result1=mDevice.performActionAndWait(new Runnable() {
            @Override
            public void run() {
                Log.i(TAG, "run: new window appeared in 5 s");
            }
        }, Until.newWindow(), 5000);

        Log.i(TAG, "test1: result = "+result1);

        Utils.startAPP(mDevice,packageName,activityName);

        mDevice.waitForWindowUpdate(packageName,5000);

        Utils.closeAPP(mDevice,packageName);
    }
}

执行效果如下:

这里写图片描述

执行结果如下:

11-21 23:50:29.221 I/TestRunner: run started: 1 tests
11-21 23:50:29.236 I/TestRunner: started: test1(com.breakloop.u2demo.uidevice.WaitConditionTest)
11-21 23:50:29.238 I/MonitoringInstrumentation: Activities that are still in CREATED to STOPPED: 0
11-21 23:50:29.239 I/com.breakloop.u2demo.uidevice.WaitConditionTest: run: new window appeared in 5 s
11-21 23:50:34.684 I/com.breakloop.u2demo.uidevice.WaitConditionTest: test1: result = false
11-21 23:50:34.705 I/TestRunner: finished: test1(com.breakloop.u2demo.uidevice.WaitConditionTest)

由执行结果可见,在5秒后,输出了“result = false”,说明5秒内未有新窗口事件产生。原因在于,打开APP这个操作,发生在5秒之后。

我们修改代码,将

Utils.startAPP(mDevice,packageName,activityName);

放置在performActionAndWait之前,查看执行结果。

11-22 00:28:43.961 I/TestRunner: started: test1(com.breakloop.u2demo.uidevice.WaitConditionTest)
11-22 00:28:43.963 I/MonitoringInstrumentation: Activities that are still in CREATED to STOPPED: 0
11-22 00:28:44.322 I/com.breakloop.u2demo.uidevice.WaitConditionTest: run: new window appeared in 5 s
11-22 00:28:44.411 I/com.breakloop.u2demo.uidevice.WaitConditionTest: test1: result = true
11-22 00:28:50.912 I/TestRunner: finished: test1(com.breakloop.u2demo.uidevice.WaitConditionTest)

可以发现,“result = true”,说明有newWindow事件发生。并且,“result = true”紧接着“new window appeared in 5 s”输出,说明并没有等待发生。

作者:daihuimaozideren 发表于2017/11/22 0:44:17 原文链接
阅读:5 评论:0 查看评论

python: center、ljust、rjust 函数

$
0
0

Syntax

ljust

str. ljust(width[, fillchar])

rjust

str. rjust(width[, fillchar])

center

str. center(width[, fillchar])

Args

  • width: 指定字符串长度。
  • fillchar: 填充字符(默认字符为空格)。

实验代码

str_ = 'Nanjing'

# fillchar = '+'
assert str_.center(10, '+') == '+Nanjing++'
assert str_.ljust(10, '+') == 'Nanjing+++'
assert str_.rjust(10, '+') == '+++Nanjing'

# fillchar: default is a space
assert str_.center(10) == ' Nanjing  '
assert str_.ljust(10) == 'Nanjing   '
assert str_.rjust(10) == '   Nanjing'


Ref:



作者:JNingWei 发表于2017/11/22 9:58:54 原文链接
阅读:1 评论:0 查看评论

leetcode: 89. Gray Code

$
0
0

Q

The gray code is a binary numeral system where two successive values differ in only one bit.

Given a non-negative integer n representing the total number of bits in the code, print the sequence of gray code. A gray code sequence must begin with 0.

For example, given n = 2, return [0,1,3,2]. Its gray code sequence is:

00 - 0
01 - 1
11 - 3
10 - 2

Note:
For a given n, a gray code sequence is not uniquely defined.

For example, [0,2,3,1] is also a valid gray code sequence according to the above definition.

For now, the judge is able to judge based on one instance of gray code sequence. Sorry about that.

AC

class Solution(object):
    def grayCode(self, n):
        """
        :type n: int
        :rtype: List[int]
        """
        ret = [0]
        num = 0
        while True:
            for i in range(n):
                m = 1<<i
                if num^m not in ret:
                    num = num^m
                    ret.append(num)
                    break
            else:
                break
        return ret


# Time:  O(2^n)
# Space: O(1)
class Solution2(object):
    def grayCode(self, n):
        """
        :type n: int
        :rtype: List[int]
        """
        result = [0]
        for i in xrange(n):
            for n in reversed(result):
                result.append(1 << i | n)
        return result


class Solution3(object):
    def grayCode(self, n):
        """
        :type n: int
        :rtype: List[int]
        """
        return [i >> 1 ^ i for i in xrange(1 << n)]


if __name__ == "__main__":
    assert Solution().grayCode(0) == [0]
    assert Solution().grayCode(2) == [0, 1, 3, 2]


作者:JNingWei 发表于2017/11/22 10:20:50 原文链接
阅读:0 评论:0 查看评论

leetcode: 90. Subsets II

$
0
0

Q

Given a collection of integers that might contain duplicates, nums, return all possible subsets (the power set).

Note: The solution set must not contain duplicate subsets.

For example,
If nums = [1,2,2], a solution is:

[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]

AC

class Solution(object):
    def subsetsWithDup(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        self.result = set()
        for i in range(len(nums)+1):
            self.getCombi(nums, i,[])
        return list(self.result)

    def getCombi(self, nums, i, temp):
        if i==0:
            self.result.add(tuple(sorted(temp)))
        elif not nums:
            return
        else:
            self.getCombi(nums[1:], i-1, temp+[nums[0]])
            self.getCombi(nums[1:], i, temp)


# Time:  O(n * 2^n)
# Space: O(1)
class Solution2(object):
    def subsetsWithDup(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        nums.sort()
        result = [[]]
        previous_size = 0
        for i in xrange(len(nums)):
            size = len(result)
            for j in xrange(size):
                # Only union non-duplicate element or new union set.
                if i == 0 or nums[i] != nums[i - 1] or j >= previous_size:
                    result.append(list(result[j]))
                    result[-1].append(nums[i])
            previous_size = size
        return result


# Time:  O(n * 2^n) ~ O((n * 2^n)^2)
# Space: O(1)
class Solution3(object):
    def subsetsWithDup(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        result = []
        i, count = 0, 1 << len(nums)
        nums.sort()

        while i < count:
            cur = []
            for j in xrange(len(nums)):
                if i & 1 << j:
                    cur.append(nums[j])
            if cur not in result:
                result.append(cur)
            i += 1

        return result


# Time:  O(n * 2^n) ~ O((n * 2^n)^2)
# Space: O(1)
class Solution4(object):
    def subsetsWithDup(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        result = []
        self.subsetsWithDupRecu(result, [], sorted(nums))
        return result

    def subsetsWithDupRecu(self, result, cur, nums):
        if not nums:
            if cur not in result:
                result.append(cur)
        else:
            self.subsetsWithDupRecu(result, cur, nums[1:])
            self.subsetsWithDupRecu(result, cur + [nums[0]], nums[1:])


if __name__ == "__main__":
    assert Solution().subsetsWithDup([1, 2, 2]) == [(1, 2), (1,), (1, 2, 2), (2,), (), (2, 2)]


作者:JNingWei 发表于2017/11/22 10:21:20 原文链接
阅读:0 评论:0 查看评论

leetcode: 91. Decode Ways

$
0
0

Q

A message containing letters from A-Z is being encoded to numbers using the following mapping:

'A' -> 1
'B' -> 2
...
'Z' -> 26

Given an encoded message containing digits, determine the total number of ways to decode it.

For example,
Given encoded message “12”, it could be decoded as “AB” (1 2) or “L” (12).

The number of ways decoding “12” is 2.

AC

class Solution:
    def numDecodings(self, s):
        """
        :type s: str
        :rtype: int
        """
        if not s:
            return 0
        self.memo = {}
        return self.getCount(s, 0)

    def getCount(self, s, i):
        if i in self.memo:
            return self.memo[i]
        if not s:
            ret = 1
        elif len(s) ==1:
            if s!='0':
                ret = 1
            else:
                ret = 0
        else:
            if s[0]=='0':
                ret = 0
            else:
                ret = self.getCount(s[1:], i+1)
                if int(s[:2])<=26:
                    ret += self.getCount(s[2:], i+2)
        self.memo[i] = ret
        return ret


# Time:  O(n)
# Space: O(1)
class Solution2(object):
    def numDecodings(self, s):
        """
        :type s: str
        :rtype: int
        """
        if len(s) == 0 or s[0] == '0':
            return 0
        prev, prev_prev = 1, 0
        for i in xrange(len(s)):
            cur = 0
            if s[i] != '0':
                cur = prev
            if i > 0 and (s[i - 1] == '1' or (s[i - 1] == '2' and s[i] <= '6')):
                cur += prev_prev
            prev, prev_prev = cur, prev
        return prev


if __name__ == "__main__":
    assert map(lambda x: Solution().numDecodings(x), ["0", "10", "10", "103", "1032", "10323"]) == [0, 1, 1, 1, 1, 2]


作者:JNingWei 发表于2017/11/22 10:21:50 原文链接
阅读:0 评论:0 查看评论

leetcode: 92. Reverse Linked List II

$
0
0

Q

Reverse a linked list from position m to n. Do it in-place and in one-pass.

For example:
Given 1->2->3->4->5->NULL, m = 2 and n = 4,

return 1->4->3->2->5->NULL.

Note:
Given m, n satisfy the following condition:
1 ≤ m ≤ n ≤ length of list.

AC

# Definition for singly-linked list.
class ListNode(object):
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution(object):
    def reverseBetween(self, head, m, n):
        """
        :type head: ListNode
        :type m: int
        :type n: int
        :rtype: ListNode
        """
        if m==n:
            return head
        idx = 1
        h = None
        p = head
        vals = []
        while True:
            if idx==m:
                h = p
            if idx>=m and idx<=n:
                vals.append(p.val)
            if idx==n:
                break
            idx = idx +1
            p = p.next
        while h!=p.next:
            h.val = vals.pop()
            h = h.next
        return head


# Time:  O(n)
# Space: O(1)
# Definition for singly-linked list.
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

    def __repr__(self):
        if self:
            return "{} -> {}".format(self.val, repr(self.next))
class Solution2(object):
    def reverseBetween(self, head, m, n):
        diff, dummy, cur = n - m + 1, ListNode(-1), head
        dummy.next = head
        last_unswapped = dummy
        while cur and m > 1:
            cur, last_unswapped, m = cur.next, cur, m - 1
        prev, first_swapped = last_unswapped,  cur
        while cur and diff > 0:
            cur.next, prev, cur, diff = prev, cur, cur.next, diff - 1
        last_unswapped.next, first_swapped.next = prev, cur
        return dummy.next


if __name__ == "__main__":
    head, head.next, head.next.next, head.next.next.next, head.next.next.next.next \
        = ListNode(1), ListNode(2), ListNode(3), ListNode(4), ListNode(5)
    print Solution().reverseBetween(head, 2, 4)


作者:JNingWei 发表于2017/11/22 10:22:19 原文链接
阅读:0 评论:0 查看评论
Viewing all 35570 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>