fpm与swoole区别

PHP-FPM

早期版本的 PHP 并没有内置的 WEB 服务器,而是提供了 SAPI(Server API)给第三方做对接。现在非常流行的 php-fpm 就是通过 FastCGI 协议来处理 PHP 与第三方 WEB 服务器之间的通信。 (推荐学习: swoole视频教程)

比如 Nginx + php-fpm 的组合,这种方式运行的 fpm 是 Master/Worker 模式,启动一个 Master 进程监听来自 Nginx 的请求,再 fork 多个 Worker 进程处理请求。每个 Worker 进程只能处理一个请求,单一进程的生命周期大体如下:

初始化模块。

初始化请求。此处请求是请求 PHP 执行代码的意思,并非 HTTP 的请求。

执行 PHP 脚本。

结束请求。

关闭模块。

Swoole 采用的也是 Master/Worker 模式,不同的是 Master 进程有多个 Reactor 线程,Master 只是一个事件发生器,负责监听 Socket 句柄的事件变化。Worker 以多进程的方式运行,接收来自 Reactor 线程的请求,并执行回调函数(PHP 编写的)。启动 Master 进程的流程大致是:

初始化模块。

初始化请求。因为 swoole 需要通过 cli 的方式运行,所以初始化请求时,不会初始化 PHP 的全局变量,如 $_SERVER, $_POST, $_GET 等。

执行 PHP 脚本。包括词法、语法分析,变量、函数、类的初始化等,Master 进入监听状态,并不会结束进程。

Swoole 加速的原理

由 Reactor(epoll 的 IO 复用方式)负责监听 Socket 句柄的事件变化,解决高并发问题。

通过内存常驻的方式节省 PHP 代码初始化的时间,在使用笨重的框架时,用 swoole 加速效果是非常明显的。

对比不同

PHP-FPM

Master 主进程 / Worker 多进程模式。

启动 Master,通过 FastCGI 协议监听来自 Nginx 传输的请求。

每个 Worker 进程只对应一个连接,用于执行完整的 PHP 代码。

PHP 代码执行完毕,占用的内存会全部销毁,下一次请求需要重新再进行初始化等各种繁琐的操作。

只用于 HTTP Server。

Swoole

Master 主进程(由多个 Reactor 线程组成)/ Worker 多进程(或多线程)模式

启动 Master,初始化 PHP 代码,由 Reactor 监听 Socket 句柄的事件变化。

Reactor 主线程负责子多线程的均衡问题,Manager 进程管理 Worker 多进程,包括 TaskWorker 的进程。

每个 Worker 接受来自 Reactor 的请求,只需要执行回调函数部分的 PHP 代码。

只在 Master 启动时执行一遍 PHP 初始化代码,Master 进入监听状态,并不会结束进程。

不仅可以用于 HTTP Server,还可以建立 TCP 连接、WebSocket 连接。

以上就是fpm与swoole区别的详细内容,更多请关注php中文网其它相关文章!

Cocos Creator加载网络图片

//设置显示图片
function setImg(imgNode, spriteFrame) {
    imgNode.getComponent(cc.Sprite).spriteFrame = spriteFrame;
}
//加载网络图片
function loadImgByUrl(imgNode, remoteUrl, imageType) {
    if (!imageType) {
        imageType = “png”;
    }
    cc.loader.load({
        url: remoteUrl,
        type: imageType
    }, function (err, texture) {
        if (err) {
            return;
        }
        setImg(imgNode, new cc.SpriteFrame(texture));
    });
}
//加载手机本地图片
function loadLocal(imgNode, absolutePath) {
    cc.loader.load(absolutePath, function (err, texture) {
        if (err) {
            return;
        }
        setImg(imgNode, new cc.SpriteFrame(texture));
    });
}


module.exports = {
    loadImgByUrl: loadImgByUrl,
    loadLocal: loadLocal,
    setImg: setImg,
};

Android编程简单实现九宫格示例

https://www.jb51.net/article/117464.htm

本文实例讲述了Android编程简单实现九宫格。分享给大家供大家参考,具体如下:

实现的步骤

1. 一个整体的容器部分。就是上图中包括整个图片项个各个部分,这里我们使用gridView(表格布局)来实现

2.整个界面里需要注意的是 “重复的部分”,就是 各个图片项和,图片下方显示的文字了。那么我们需要描述这个部分。在描述时,要说明图片位于上方,文字位于下方。

3.迭代,或者说重复的将各项 插入(放入)到容器内。
需要添加/修改3个文件:main.xml、meunitem.xml、activity

main.xml源代码如下,本身是个GirdView,用于装载Item:

123456789101112<?xml version="1.0" encoding="utf-8"?><GridView xmlns:Android="http://schemas.android.com/apk/res/android"android:id="@+id/GridView"android:layout_width="fill_parent"android:layout_height="fill_parent"android:numColumns="auto_fit"android:horizontalSpacing="10dp"android:verticalSpacing="10dp"android:columnWidth="90dp"android:stretchMode="columnWidth"android:gravity="center"></GridView>

在这里需要关注的属性是columnWidth,这里指定了列的宽度,一个列对象,对应一个 “可重复的子项”,这个子项就是我们 的图片项和图片下方文字显示的部分。如果不指定这个宽度的话,默认是每行(展示的行,界面)仅仅只显示一个 “可重复的子项”,而当指定了宽度时,本文指定为90dp,如果每行实际行尺寸大于90,他就会继续将下一个的“可重复的子项”,放置在本行。于是就呈现一种 一行显示多个子项的情况。numColumns属性,指定一个自动填充的值,指示了自动填充行。

2。指定“可重复的子项”,就是需要迭代显示的部分

Android:numColumns=”auto_fit” ,GridView的列数设置为自动
android:columnWidth=”90dp”,每列的宽度,也就是Item的宽度
android:stretchMode=”columnWidth”,缩放与列宽大小同步
android:verticalSpacing=”10dp”,两行之间的边距,如:行一(NO.0~NO.2)与行二(NO.3~NO.5)间距为10dp
android:horizontalSpacing=”10dp”,两列之间的边距。

接下来介绍 meunitem.xml,这个XML跟前面ListView的ImageItem.xml很类似:

12345678910111213141516<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="wrap_content"><ImageView android:layout_width="wrap_content"android:id="@+id/ItemImage"android:layout_height="wrap_content"android:layout_centerHorizontal="true"/><TextView  android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@+id/ItemImage"android:id="@+id/ItemText"android:layout_centerHorizontal="true"/></RelativeLayout>

最后是JAVA的源代码

12345678910111213141516171819202122232425@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.mainmenu);GridView gridview = (GridView) findViewById(R.id.GridView);ArrayList<HashMap<String, Object>> meumList = new ArrayList<HashMap<String, Object>>();for(int i = 1;i < 10;i++) {HashMap<String, Object> map = new HashMap<String, Object>();map.put("ItemImage", R.drawable.icon);map.put("ItemText", "NO."+i);meumList.add(map);}SimpleAdapter saMenuItem = new SimpleAdapter(this,meumList, //数据源R.layout.menuitem, //xml实现new String[]{"ItemImage","ItemText"}, //对应map的Keynew int[]{R.id.ItemImage,R.id.ItemText}); //对应R的Id//添加Item到网格中gridview.setAdapter(saMenuItem);gridview.setOnItemClickListener(new OnItemClickListener() {public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,long arg3) {System.out.println("click index:"+arg2);}});}

Android开发中保存数据的四种方法方法

     Android开发中我们会接触到四种数据存储方式,每种存储方式都各有不同;以下我分别列举了Android开发中的不同存储方式的特点
一,Preferences
Preferences 是一个较轻量级的存储数据的方法,具体使用方法:
在A中保存值:
———————————————————————————————
SharedPreferences.Editor sharedata = getSharedPreferences(“data”, 0).edit();  
  sharedata.putString(“name”,”shenrenkui”);  
  sharedata.commit();
———————————————————————————————
在B中取值:
———————————————————————————————
SharedPreferences sharedata = getSharedPreferences(“data”, 0);  
String data = sharedata.getString(“name”, null);  
Log.i(TAG,”data=”+data);
———————————————————————————————
注意,Context.getSharedPreferences(String name,int type)的参数更我们在创建数据的时候的数据权限属性是一样的,存储和取值的过程这有点像HashMap但是比HashMap更具人性化,getXXX(Object key,Object defualtReturnValue),第二个参数是当你所要的key对应没有时候返回的值。这就省去了很多逻辑判断。。。。

二,Files

在 Android上面没有的File就是J2se中的纯种File了,可见功能之强大,这里就算是走马观花地严重路过了。
———————————————————————————————
//创建文件
            file = new File(FILE_PATH , FILE_NAME);
            file.createNewFile();

            //打开文件file的OutputStream
            out = new FileOutputStream(file);
            String infoToWrite = “纸上得来终觉浅,绝知此事要躬行”;
            //将字符串转换成byte数组写入文件
            out.write(infoToWrite.getBytes());
            //关闭文件file的OutputStream
            out.close();

            //打开文件file的InputStream
            in = new FileInputStream(file);
            //将文件内容全部读入到byte数组
            int length = (int)file.length();
            byte[] temp = new byte[length];
            in.read(temp, 0, length);
            //将byte数组用UTF-8编码并存入display字符串中
            display =  EncodingUtils.getString(temp,TEXT_ENCODING);
            //关闭文件file的InputStream
            in.close();
        } catch (IOException e) {
            //将出错信息打印到Logcat
            Log.e(TAG, e.toString());
            this.finish();
        }
//从资源读取
InputStream is=getResources().getRawResource(R.raw.文件名)
———————————————————————————————
三,Databases

Android内嵌了功能比其他手机操作系统强大的关系型数据库 sqlite3,我们在大学时候学的SQL语句基本都可以使用,我们自己创建的数据可以用adb shell来操作。具体路径是/data/data/package_name/databases。如,这里演示一下进入 com.android.providers.media包下面的操作。

1,  adb shell

2,  cd /data/data/com.android.providers.media/databases

3,  ls(查看 com.android.providers.media下面的数据库)

4,  sqlite3 internal.db

5,  .help— 看看如何操作

6,  .table列出internal数据中的表

7,  select * from albums;
———————————————————————————————
DatabaseHelper mOpenHelper;
private static final String DATABASE_NAME = “dbForTest.db”;
private static final int DATABASE_VERSION = 1;
private static final String TABLE_NAME = “diary”;
private static final String TITLE = “title”;
private static final String BODY = “body”;
private static class DatabaseHelper extends SQLiteOpenHelper {
  DatabaseHelper(Context context) {
  super(context, DATABASE_NAME, null, DATABASE_VERSION);
  }
  @Override
  public void onCreate(SQLiteDatabase db) {
  String sql = “CREATE TABLE ” + TABLE_NAME + ” (” + TITLE
    + ” text not null, ” + BODY + ” text not null ” + “);”;
  Log.i(“haiyang:createDB=”, sql);
  db.execSQL(sql);
  }
  @Override
  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  }
}
/*
  * 重新建立数据表
  */
private void CreateTable() {
  SQLiteDatabase db = mOpenHelper.getWritableDatabase();
  String sql = “CREATE TABLE ” + TABLE_NAME + ” (” + TITLE
    + ” text not null, ” + BODY + ” text not null ” + “);”;
  Log.i(“haiyang:createDB=”, sql);
  try {
  db.execSQL(“DROP TABLE IF EXISTS diary”);
  db.execSQL(sql);
  setTitle(” 数据表成功重建”);
  } catch (SQLException e) {
  setTitle(“数据表重建错误”);
  }
}
/*
  * 删除数据表
  */
private void dropTable() {
  SQLiteDatabase db = mOpenHelper.getWritableDatabase();
  String sql = “drop table ” + TABLE_NAME;
  try {
  db.execSQL(sql);
  setTitle(“数据表成功删除:” + sql);
  } catch (SQLException e) {
  setTitle(“数据表删除错误”);
  }
}
/*
  * 插入两条数据
  */
private void insertItem() {
  SQLiteDatabase db = mOpenHelper.getWritableDatabase();
  String sql1 = “insert into ” + TABLE_NAME + ” (” + TITLE + “, ” + BODY
    + “) values(‘haiyang’, ‘android的发展真是迅速啊’);”;
  String sql2 = “insert into ” + TABLE_NAME + ” (” + TITLE + “, ” + BODY
    + “) values(‘icesky’, ‘android的发展真是迅速啊’);”;
  try {
  Log.i(“haiyang:sql1=”, sql1);
  Log.i(“haiyang:sql2=”, sql2);
  db.execSQL(sql1);
  db.execSQL(sql2);
  setTitle(” 插入两条数据成功”);
  } catch (SQLException e) {
  setTitle(“插入两条数据失败”);
  }
}
/*
  * 删除其中的一条数据
  */
private void deleteItem() {
  try {
  SQLiteDatabase db = mOpenHelper.getWritableDatabase();
  db.delete(TABLE_NAME, ” title = ‘haiyang'”, null);
  setTitle(“删除title为haiyang的一条记录”);
  } catch (SQLException e) {
  }
}
/*
  * 在屏幕的title区域显示当前数据表当中的数据的条数。
  */
private void showItems() {
  SQLiteDatabase db = mOpenHelper.getReadableDatabase();
  String col[] = { TITLE, BODY };
  Cursor cur = db.query(TABLE_NAME, col, null, null, null, null, null);
  Integer num = cur.getCount();
  setTitle(Integer.toString(num) + ” 条记录”);
}
———————————————————————————————
四,Network

这是借助Internet来存储我们要的数据,这是CS结构的存储方式,也是点一下名了。

如何使用 Content Provider

下边是用户经常接触到的几个典型Content Provider应用:

    * Content Provider Name : Intended Data
    * Browser : Browser bookmarks, Browser history, etc.
    * CallLog : Missed calls, Call datails, etc.
    * Contacts : Contact details
    * MediaStore : Media files such as audio, Video and Images
    * Settings : Device Settings and Preferences

调用Content Provider资源的标准URI结构:
———————————————————————————————
<standard_prefix>://<authority>/<data_path>/<id>
———————————————————————————————
例如:
1) 取得浏览器所有“书签”信息: content://browser/bookmarks
2) 取得系统通讯录中的信息: content://contacts/people (如果取得某一个特定通讯记录,在路径URI的末端指定一个ID号:content://contacts/people/5
简单的实例片段:
———————————————————————————————
Uri allCalls = Uri.parse(“content://call_log/calls”);
Cursor c = managedQuery(allCalls, null, null, null, null);———————————

android click事件中获取 Button文本 从一个按钮开关中获取文本

android获取Button文本 从一个按钮开关中获取文本

在程序里可以从一个按钮中获取文本
String buttonText = button.getText();
也可以从按钮开关中获取id
int buttinID = view.getId();
但是如何从按钮开关中获取文本呢?这俩个怎么结合呢?
在onClick()中传递的视图就是你要找的button,需要强制转换一下。
public void onClick(View view) {
// 从按钮开关中获取文本
Button b = (Button)v;
String buttonText = b.getText().toString();
}

Creator3D:太厉害了!3D模型原来可以这样显示在2DUI上

今天菜鸟分享一个小的技术点:3D模型应该怎样显示到2D的ui层上边。最近逛论坛经常看见有关于这个问题的帖子,正好菜鸟后边也要用到,那就提前研究一下。下边将实现的两种方式简单说明一下。

效果

咱们还是先来看看效果:

1.UIMeshRenderer 组件实现效果

刚接触1.2.0版本的人可能对这个比较陌生,在之前的版本他有另一个名字UIModel,在1.2.0版本改名成了UIMeshRenderer

2.RenderTexture 实现效果

正文

1.UIMeshRenderer实现方式

首先建议大家看一下UIMeshRenderer组件的官方文档,这种方式的实现基本上不涉及代码,但是有几个特别需要注意的问题,我大概说下

  • 1.菜鸟习惯将3d节点与2d的ui节点分开放,2d的ui节点默认在Canvas下。3d的节点会创建一个父节点放置,也就是统一放置到ModelContro下,类似于这样:
  • 2.添加MeshRenderer组件到3d模型节点,
    • 官方文档是这样说的
    添加方式是在层级管理器中选中带有或继承自 MeshRenderer 组件的节点, 然后点击 属性检查器 下方的 添加组件 按钮,选择 UI/MeshRenderer即可
  • 3.添加了MeshRenderer组件后大家预览可能发现并没有效果
    • 接下来就是这种方式的需要注意的重点了:
    • 1:绑定了MeshRenderer组件的3d节点必须放在Canvas下,也就是2DUI层
    • 2:3d节点的大小,3d节点添加了MeshRenderer组件,并且放置到Canvas下,那么他的大小将不会按照3d节点在摄像机下的大小显示,而是按照3d节点相对Canvas的大小来显示,通常模型的大小需要缩放到在UI层下的实际预览大小
    • 3:材质,当大小和层级调整好以后你会发现模型是显示出来了,但是是黑的,如图:
  • 你已经到了最后一步了,大家的默认effect 应该是builtin-standard吧,只需要将其改为builtin-unlit就可以了

2.RenderTexture 实现方式

这种方式主要是通过代码来实现的,把 3D 相机照射的内容绘制到 UI 的精灵帧上。 在菜鸟出demo的时候是国庆前,文档和API都还没有更新,还是之前版本的。1.2.0版本对部分参数进行了调整。现在好像API调整了,文档还是之前的,但是没啥影响。 下边咱们说一下重点:

  • 1.原理就是 把 3D 相机照射的内容绘制到 UI 的精灵帧上
  • 2.那么主要就是3d摄像机,需要创建一个摄像机,然后将你所需要显示在ui上的3d节点移动到摄像机的视觉范围内
  • 3.创建Sprite用来摄像机渲染出来的显示
  • 4.代码实现
const _colorAttachment = new GFXColorAttachment();
const _depthStencilAttachment = new GFXDepthStencilAttachment();
let renderTex = new RenderTexture();
renderTex.reset({
    width: 500,
    height: 700,
    passInfo: {
        colorAttachments: [_colorAttachment],
        depthStencilAttachment: _depthStencilAttachment,
    }
});

let spriteframe: SpriteFrame = this.camerSprite.spriteFrame;
let sp: SpriteFrame = new SpriteFrame();
sp.reset({
    originalSize: spriteframe.originalSize,
    rect: spriteframe.rect,
    offset: spriteframe.offset,
    isRotate: spriteframe.rotated,
    borderTop: spriteframe.insetTop,
    borderLeft: spriteframe.insetLeft,
    borderBottom: spriteframe.insetBottom,
    borderRight: spriteframe.insetRight,
});

this.camera.targetTexture = renderTex;
sp.texture = renderTex;
this.camerSprite.spriteFrame = sp;

//对比和之前版本的代码,两点调整:
//- 1.SpriteFrame 属性调整
//- 2.renderTex.reset 参数的调整
  • 5.在实现过程中可能会出现,本来只想显示指定的模型,但是会将摄像机的缓冲颜色也显示出来,如图:

这里菜鸟是将摄像机的ClearColor的透明度直接调为0,

cocos creator入门教程(八)—— Sprite组件的使用详解

cc.Sprite使用

cc.Sprite

1: 游戏中显示一个图片,通常我们把这个叫做”精灵” sprite

2: cocos creator如果需要显示一个图片,那么需要在节点上挂一个精灵组件,为这个组件指定要显示的图片(SpriteFrame)

3: 显示一个图片的步骤:      

(1) 创建一个节点;      

(2) 添加一个组件;    

 (3) 要显示的图片(SpriteFrame)拖动到SpriteFrame;    

 (4) 配置图片的SIZE_MODE:                

a: CUSTOM 大小和CCNode的大小一致;            

 b: RAW 原始的图片大小;              

c:  TRIMMED 大小为原始图片大小, 显示的内容是裁剪掉透明像素后的图片;      

(5) trim: 是否裁剪掉 图片的透明区域, 如果勾选,就会把完全透明的行和列裁掉, 做帧动画的时候,我们一般是用原始大小不去透明度,动画,不至于抖动;

4: 精灵更换spriteFame;

5: 快捷创建带精灵组件的节点;

图片模式

1:  simple: 精灵最普通的模式, 选择该模式后,图片将缩放到指定的大小;

2:  Tiled: 平铺模式, 图片以平铺的模式,铺地板砖的模式,铺到目标大小上;

3:  Slice: 九宫格模式,指定拉伸区域;    

4:  Filled: 设置填充的方式(圆,矩形),可以使用比例来裁剪显示图片(只显示的比例);

九宫格的使用

1:  指定拉伸区域, 让图片在拉伸的时候某些区域不会改变;        比如圆角,聊天气泡等

2:  九宫格能省图片资源, (对话框);

3:  编辑九宫格,来制定缩放区域;

4: 体会对话框背景的九宫拉伸;

Filled模式

1:  配置Filled模式

2: 配置Filled模式, 设置为Radius参数;

3: 配置Radius的参数模式,            

中心: 位置坐标(0, 1小数), (0, 0)左下脚, (1, 1) 右上角 (0.5, 0.5) 中心点                      

 Fill Start 开始的位置: 0 ~1, 右边中心点开始,逆时针走          

 Fill Range: 填充总量(0, 1];          

 FillRange为正,那么就是逆时针,如果为负,那么就是顺时针;

4: 个性化时间进度条案例;

5: 游戏中道具的时间进度显示都可以;

这里Fill Range为负数的时候,会改变旋转方向。正数是逆时针,负数是顺时针。

head.js周围边框倒计时效果

// Learn cc.Class://  - [Chinese] https://docs.cocos.com/creator/manual/zh/scripting/class.html//  - [English] http://docs.cocos2d-x.org/creator/manual/en/scripting/class.html// Learn Attribute://  - [Chinese] https://docs.cocos.com/creator/manual/zh/scripting/reference/attributes.html//  - [English] http://docs.cocos2d-x.org/creator/manual/en/scripting/reference/attributes.html// Learn life-cycle callbacks://  - [Chinese] https://docs.cocos.com/creator/manual/zh/scripting/life-cycle-callbacks.html//  - [English] https://www.cocos2d-x.org/docs/creator/manual/en/scripting/life-cycle-callbacks.html cc.Class({    extends: cc.Component,     properties: {        // foo: {        //     // ATTRIBUTES:        //     default: null,        // The default value will be used only when the component attaching        //                           // to a node for the first time        //     type: cc.SpriteFrame, // optional, default is typeof default        //     serializable: true,   // optional, default is true        // },        // bar: {        //     get () {        //         return this._bar;        //     },        //     set (value) {        //         this._bar = value;        //     }        // },         sprite: {            default: null,            type: cc.Sprite,        },         act_time: 15,    },     // LIFE-CYCLE CALLBACKS:     onLoad () {        //获取组件实例:1代码获取、2编辑器绑定        var _node = this.node.getChildByName("time_bar");        this.sp = _node.getComponent(cc.Sprite);         //1.增加        //this.now_time = 0;         //2.减少        this.now_time = this.act_time;    },     start () {     },     update (dt) {        // //1.增加        // this.now_time += dt;        // var percent = this.now_time / this.act_time; //百分比        // if(percent >= 1){        //     percent = 1;        //     this.now_time = 0;//重新开始        // }        // this.sp.fillRange = percent;          //2.减少        this.now_time -= dt;        var percent = this.now_time / this.act_time; //百分比        if(percent <= 0){            this.now_time = this.act_time;//重新开始        }        this.sp.fillRange = percent;    },});

change_frame.js

// Learn cc.Class://  - [Chinese] https://docs.cocos.com/creator/manual/zh/scripting/class.html//  - [English] http://docs.cocos2d-x.org/creator/manual/en/scripting/class.html// Learn Attribute://  - [Chinese] https://docs.cocos.com/creator/manual/zh/scripting/reference/attributes.html//  - [English] http://docs.cocos2d-x.org/creator/manual/en/scripting/reference/attributes.html// Learn life-cycle callbacks://  - [Chinese] https://docs.cocos.com/creator/manual/zh/scripting/life-cycle-callbacks.html//  - [English] https://www.cocos2d-x.org/docs/creator/manual/en/scripting/life-cycle-callbacks.htmlcc.Class({    extends: cc.Component,    properties: {        // foo: {        //     // ATTRIBUTES:        //     default: null,        // The default value will be used only when the component attaching        //                           // to a node for the first time        //     type: cc.SpriteFrame, // optional, default is typeof default        //     serializable: true,   // optional, default is true        // },        // bar: {        //     get () {        //         return this._bar;        //     },        //     set (value) {        //         this._bar = value;        //     }        // },        //1编辑器方式,2代码资源的动态加载        sprite_frame: {            default: null,            type: cc.SpriteFrame,        },    },    // LIFE-CYCLE CALLBACKS:    onLoad () {        //获取组件实例:1代码获取、2编辑器绑定        //var node = this.node.getChildByName("img_time");        this.sp = this.getComponent(cc.Sprite);        this.sp.spriteFrame = this.sprite_frame;//注意是小写spriteFrame    },    start () {    },    // update (dt) {},});

https://blog.csdn.net/ccnu027cs/article/details/102472127

JAVA 多进程VS多线程

一. 含义

  1. 进程
    当一个程序进入内存运行时,即变成一个进程。进程是处于运行过程中的程序。 进程是操作系统进行资源分配和调度的一个独立单位。
  2. 线程
    线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。线程也被称作轻量级进程。线程在进程中是独立,并发的执行流。

二. 特点
1. 进程
1. 独立性 独立存在的实体,每个进程都有自己独立私有的一块内存空间。
2. 动态性 程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。
3. 并发性 多个进程可在单处理器上并发执行。
说明:
并发性和并行性
并发是指在同一时间点只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。
并行指在同一时间点,有多条指令在多个处理器上同时执行。

三. 区别

 1.  线程是进程的组成部分,一个进程可以有很多线程,每条线程并行执行不同的任务。
	 2. 不同的进程使用不同的内存空间,而线程与父进程的其他线程共享父进程的所拥有的全部资源。这样编程方便了,但是要更加小心。
	 3. 别把内存空间和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。线程拥有自己的堆栈、自己的程序计数器和自己的局部变量,但不拥有系统资源。
	 4. 线程的调度和管理由进程本身负责完成。操作系统对进程进行调度,管理和资源分配。
4. 优势
	1. 多线程
		1. 进程之间不能共享内存,但线程之间共享内存。
		2. 系统创建进程时需要为该进程重新分配系统资源,但创建线程则代价小很多,效率高。
		3. 资源利用率更好
		4. 程序设计更简单
		5. 程序响应更快 

三、 Java中创建线程方法

  1. 继承Thread类创建线程类
    定义Thread类的子类,重写该类的run()方法。该方法为线程执行体。
    创建Thread子类的实例。即线程对象。
    调用线程对象的start()方法启动该线程
  2. 实现Runnable接口创建线程类
    定义Runnable接口的实现类,重写该接口的run()方法。该方法为线程执行体。
    创建Runnable实现类的实例。并以此实例作为Thread的target来创建Thread对象。该Thread对象才是真正的线程对象。
    调用线程对象(该Thread对象)的start()方法启动该线程。
  3. 使用Callable和Future创建线程
  4. //Callable
  5. public class CallableAndFuture {
  6. public static void main(String[] args) {
  7. Callable<Integer> callable = new Callable<Integer>() { public Integer call() throws Exception {
  8. return new Random().nextInt(100);
  9. } };
  10. FutureTask<Integer> future = new FutureTask<Integer>(callable); new Thread(future).start();
  11. try {
  12. Thread.sleep(5000);// 可能做一些事情 System.out.println(future.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } // **Future** public class CallableAndFuture { public static void main(String[] args) { ExecutorService threadPool = Executors.newSingleThreadExecutor(); Future<Integer> future = threadPool.submit(new Callable<Integer>() { public Integer call() throws Exception { return new Random().nextInt(100); } }); try { Thread.sleep(5000);// 可能做一些事情 System.out.println(future.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }

四、 用Runnable还是ThreadJava以及创建线程两种方法对比?
在java多线程中,一般推荐采用实现Runnable接口来创建多线程,因为实现Runnable接口相比继承Thread类有如下优劣势:
实现Runnable接口,线程类只是实现了接口,还可以继承其他类;继承Thread类的话,不能再继承其他父类。
实现Runnable接口,多个线程可以共享同一个target对象,所以适合多个相同程序代码的线程区处理同一资源的情况。分离数据和代码,体现面向对象的思想。
实现Runnable接口,访问当前线程,必须使用Thread.currentThread()方法;继承Thread类的话,使用this获得当前线程。

五、 Thread 类中的start() 和 run() 方法有什么区别?
start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。
当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。 需要特别注意的是:不能对同一线程对象两次调用start()方法。

六、 线程的生命周期
Java线程五种状态:
1. 新建状态(New):当线程对象创建后,即进入了新建状态。仅仅由java虚拟机分配内存,并初始化。如:Thread t = new MyThread();
2. 就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,java虚拟机创建方法调用栈和程序计数器,只是说明此线程已经做好了准备,随时等待CPU调度执行,此线程并 没有执行。
3. 运行状态(Running):当CPU开始调度处于就绪状态的线程时,执行run()方法,此时线程才得以真正执行,即进入到运行状态。注:绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
4. 阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
等待阻塞 – 运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态,JVM会把该线程放入等待池中;
同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5. 死亡状态(Dead):线程run()方法执行完了或者因异常退出了run()方法,该线程结束生命周期。 当主线程结束时,其他线程不受任何影响。
七、 java控制线程方法
1. join线程 join方法用线程对象调用,如果在一个线程A中调用另一个线程B的join方法,线程A将会等待线程B执行完毕后再执行。

2. 守护线程(Daemon Thread) Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程) 。 用户线程即运行在前台的线程,而守护线程是运行在后台的线程。 守护线程作用是为其他前台线程的运行提供便利服务,而且仅在普通、非守护线程仍然运行时才需要,比如垃圾回收线程就是一个守护线程。当VM检测仅剩一个守护线程,而用户线程都已经退出运行时,VM就会退出,因为没有如果没有了被守护这,也就没有继续运行程序的必要了。如果有非守护线程仍然存活,VM就不会退出。 守护线程的特征:如果所有前台线程都死亡,后台线程会自动死亡。 守护线程并非只有虚拟机内部提供,用户在编写程序时也可以自己设置守护线程。用户可以用Thread的setDaemon(true)方法设置当前线程为守护线程。 虽然守护线程可能非常有用,但必须小心确保其他所有非守护线程消亡时,不会由于它的终止而产生任何危害。因为你不可能知道在所有的用户线程退出运行前,守护线程是否已经完成了预期的服务任务。一旦所有的用户线程退出了,虚拟机也就退出运行了。 因此,不要在守护线程中执行业务逻辑操作(比如对数据的读写等)。 另外有几点需要注意:

1、setDaemon(true)必须在调用线程的start()方法之前设置,否则会跑出IllegalThreadStateException异常。
2、在守护线程中产生的新线程也是守护线程。  
3、 不要认为所有的应用都可以分配给守护线程来进行服务,比如读写操作或者计算逻辑。 
  1. 线程让步(yield ) yield可以直接用Thread类调用,可以让当前正在执行的线程暂停,不会阻塞该线程,只是将该线程转入就绪状态。yield让出CPU执行权给同等级的线程,如果没有相同级别的线程在等待CPU的执行权,则该线程继续执行。

八、 sleep()方法和yield()方法的区别
sleep()方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程的优先级;但yield()方法只会给优先级相同,或优先级更高的线程执行机会。
sleep()方法会将线程转入阻塞状态(block状态),直到经过阻塞时间才会转入就绪状态;而yield()方法不会将线程转入阻塞状态,它只是强制当前线程进入就绪状态。 因此完全有可能某个线程调用yield()方法暂停之后,立即再次获得处理器资源被执行。
sleep()方法声明抛出了InterruptedException异常,所以调用sleep()方法时要么捕捉该异常,要么显式声明抛出该异常;而yield()方法则没有声明抛出任何异常。
sleep()方法比yield()方法有更好的可移植性,通常不建议使用yield()方法来控制并发线程的执行。
九、 为什么Thread类的sleep()和yield()方法是静态的?
Thread类的sleep()和yield()方法将在当前正在执行的线程上运行。所以在其他处于等待状态的线程上调用这些方法是没有意义的。它们可以在当前正在执行的线程中工作,并避免程序员错误的认为可以在其他非运行线程调用这些方法。 现在的实现, 是只能sleep当前的线程.当前线程是自愿的.让sleep()成为实例方法, 当前线程可以直接sleep别的线程, 会引入很多 多线程问题,例如死锁。 destroy(), suspend(), stop(),resume()这些实例方法都已经被deprecated(弃用)。活下来的是哪些? 只有static方法(只对当前线程操作和一些比较温和的实例方法, 如getXXX(), isXXX(), join(), yield()等.

十、 sleep方法与wait方法的区别?
sleep方法是静态方法,wait方法是非静态方法。
sleep方法在时间到后会自己“醒来”,但wait不能,必须由其它线程通过notify(All)方法让它“醒来”。
sleep方法通常用在不需要等待资源情况下的阻塞,像等待线程、数据库连接的情况一般用wait。
十一、 线程安全问题
线程安全问题,其实是指多线程环境下对共享资源的访问可能会引起此共享资源的不一致性。因此,为避免线程安全问题,应该避免多线程环境下对此共享资源的并发访问。

十二、 同步代码块
同步代码块的格式为:

 synchronized (obj) {             
    //...
 }

其中,obj为锁对象,因此,选择哪一个对象作为锁是至关重要的。一般情况下,都是选择此共享资源对象作为锁对象。 任何时刻只能有一个线程可以获得对锁对象的锁定,其他线程无法获得锁,也无法修改它。当同步代码块执行完成后,该线程会释放对锁对象的锁定。 通过这种方式可以保证并发线程在任一时刻只有一个线程可以进入修改共享资源的代码区(临界区),从而保证线程的安全性。

十三、 同步方法
对共享资源进行访问的方法定义中加上synchronized关键字修饰,使得此方法称为同步方法。可以简单理解成对此方法进行了加锁,其锁对象为当前方法所在的对象自身。多线程环境下,当执行此方法时,首先都要获得此同步锁(且同时最多只有一个线程能够获得),只有当线程执行完此同步方法后,才会释放锁对象,其他的线程才有可能获取此同步锁,以此类推…

 public synchronized void a() {        
     // ....
 }

可变类的线程安全是以降低程序的运行效率为代价的,为了减少程序安全所带来的负面影响,程序可以采用如下策略: – 不要对线程安全类的所有方法都进行同步,只对那些会改变竞争资源的方法进行同步。 – 如果可变类有两种运行环境:单线程和多线程环境,则应该为该可变类提供两种版本:线程不安全版本和线程安全版本。在单线程环境中使用线程不安全版本以保证性能,在多线程环境中使用线程安全版本。

十四、 何时会释放对同步监视器锁定?
程序无法显式的释放对同步监视器的锁定,线程可以通过以下方式释放锁定: A、当线程的同步方法、同步代码库执行结束,就可以释放同步监视器 B、当线程在同步代码库、方法中遇到break、return终止代码的运行,也可释放 C、当线程在同步代码库、同步方法中遇到未处理的Error、Exception,导致该代码结束也可释放同步监视器 D、当线程在同步代码库、同步方法中,程序执行了同步监视器对象的wait方法,导致方法暂停,释放同步监视器

下面情况不会释放同步监视器:
A、当线程在执行同步代码库、同步方法时,程序调用了Thread.sleep()/Thread.yield()方法来暂停当前程序,当前程序不会释放同步监视器
B、当线程在执行同步代码库、同步方法时,其他线程调用了该线程的suspend方法将该线程挂起,该线程不会释放同步监视器。注意尽量避免使用suspend、resume
十五、同步锁(Lock)
通常认为:Lock提供了比synchronized方法和synchronized代码块更广泛的锁定操作,Lock更灵活的结构,有很大的差别,并且可以支持多个Condition对象 Lock是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁, 线程开始访问共享资源之前应先获得Lock对象。不过某些锁支持共享资源的并发访问,如:ReadWriteLock(读写锁),在线程安全控制中, 通常使用ReentrantLock(可重入锁)。使用该Lock对象可以显示加锁、释放锁。

class C {
        //锁对象
        private final ReentrantLock lock = new ReentrantLock();
        ......
        //保证线程安全方法
        public void method() {
            //上锁
            lock.lock();
            try {
                //保证线程安全操作代码
            } catch() {
 
            } finally {
                lock.unlock();//释放锁
            }
        }
    }

使用Lock对象进行同步时,锁定和释放锁时注意把释放锁放在finally中保证一定能够执行。使用锁和使用同步很类似,只是使用Lock时显示的调用lock方法来同步。而使用同步方法synchronized时系统会隐式使用当前对象作为同步监视器,同样都是“加锁->访问->释放锁”的操作模式,都可以保证只能有一个线程操作资源。 同步方法和同步代码块使用与竞争资源相关的、隐式的同步监视器,并且强制要求加锁和释放锁要出现在一个块结构中,而且获得多个锁时,它们必须以相反的顺序释放,且必须在与所有锁被获取时相同的范围内释放所有资源。 Lock提供了同步方法和同步代码库没有的其他功能,包括用于非块结构的tryLock方法,已经试图获取可中断锁lockInterruptibly()方法, 还有获取超时失效锁的tryLock(long, timeUnit)方法。 ReentrantLock具有重入性,也就是说线程可以对它已经加锁的ReentrantLock再次加锁,ReentrantLock对象会维持一个计数器来追踪lock方法的嵌套调用,线程在每次调用lock()加锁后,必须显示的调用unlock()来释放锁,所以一段被保护的代码可以调用另一个被相同锁保护的方法。

十六、死锁
当2个线程相互等待对方是否同步监视器时就会发生死锁,JVM没有采取处理死锁的措施,这需要我们自己处理或避免死锁。 一旦死锁,整个程序既不会出现异常,也不会出现错误和提示,只是线程将处于阻塞状态,无法继续。 由于Thread类的suspend也很容易导致死锁,所以Java不推荐使用此方法暂停线程。 参考http://ifeve.com/deadlock/了解更多死锁情况。 大部分代码并不容易产生死锁,死锁可能在代码中隐藏相当长的时间,等待不常见的条件地发生,但即使是很小的概率,一旦发生,便可能造成毁灭性的破坏。避免死锁是一件困难的事,遵循以下原则有助于规避死锁:

 1、只在必要的最短时间内持有锁,考虑使用同步语句块代替整个同步方法;
 
 2、尽量编写不在同一时刻需要持有多个锁的代码,如果不可避免,则确保线程持有第二个锁的时间尽量短暂;
 
 3、创建和使用一个大锁来代替若干小锁,并把这个锁用于互斥,而不是用作单个对象的对象级别锁;

https://blog.csdn.net/qq_32662595/article/details/84850304