SanfenR的博客


  • 首页

  • 关于

  • 归档

  • 搜索
close
SanfenR的博客

和为n的连续正数序列(day9)

发表于 2017-01-08 |

题目

  • 题目:输入一个正数n,输出所有和为n 连续正数序列。
    例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,
    所以输出3 个连续序列1-5、4-6 和7-8。

思路

  1. 对于固定的left.当前sum值小于目标Sum,则right一直后移
  2. sum==Sum。则输出序列,且将right后移
  3. 对于固定的right.,sum>Sum时,始终将left左移动

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void print(const int &left, const int &right) {
static int flag = 0;
cout << "NO:" << ++flag << "--";
for (int i = left; i <= right; ++i) {
cout << i << " ";
}
cout << endl;
}
void findSequence(const int &Sum) {
int left = 1, right = 1, sum = 1;
while (left <= Sum / 2) {
if (sum == Sum) {
print(left, right);
right++;
sum += right;
} else if (sum > Sum) {
sum -= left;
left++;
} else {
right++;
sum += right;
}
}
}

简略写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void findSequence(const int& Sum) {
int left =1, right = 1, sum = 1;
while(left <= Sum/2) {
while (sum > Sum) {
sum -= left;
left ++;
}
if (sum == Sum) {
print(left, right);
}
right ++;
sum += right;
}
}

拓展

  • 题目:输入两个整数n和m,从数列1,2,3…n中随意取几个数,使其和等于m,要求列出所有的组合。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
list<int> list1;
void find_factor(int sum, int n) {
//递归出口
if (n <= 0 || sum < 0)
return;
//输出找到的数
if (sum == n) {
list1.reverse();
for (list<int>::iterator iter = list1.begin(); iter != list1.end(); iter++)
cout << *iter << " + ";
cout << n << endl;
list1.reverse();
}
list1.push_front(n);
find_factor(sum - n, n - 1);//n参与查找
list1.pop_front();
}

源码传送门

SanfenR的博客

Android动态修改icon

发表于 2017-01-07 |

activity-alias使用

  1. 以前装应用的时候有些应用会在桌面上生成两个图标,这两个图标有些是同一个Activity的入口,有些是另外一个Activity的入口,这样的效果是怎么实现的呢?在看Android原生DeskClock程序的时候看到了这个功能的实现.使用的是activity-alias:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <activity-alias android:enabled=["true" | "false"]
    android:exported=["true" | "false"]
    android:icon="drawable resource"
    android:label="string resource"
    android:name="string"
    android:permission="string"
    android:targetActivity="string" >
    . . .
    </activity-alias>
  2. activity-alias中标记了一个名为android.intent.category.DESK_DOCK的category, 这个是在android设备插上桌面Dock底座的时候才会触发alias入口。设置:

    1
    <category android:name="android.intent.category.DESK_DOCK" />

    实现app多入口登入。

manifest代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<activity
android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity-alias
android:name=".icon_2"
android:enabled="true"
android:icon="@mipmap/steam_icon_72px"
android:label="@string/app_name"
android:targetActivity=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DESK_DOCK" />
</intent-filter>
</activity-alias>

效果如下:app显示了2个应用图标

icon

app实现动态修改icon

了解完 的基本知识之后,就知道动态修改桌面图标和应用名称是怎么做到的了。其实就是给整个应用的入口 Activity 添加一个
标签,并设置预先设计好的替代桌面图标和应用名称,并配置相同的
属性,动态启动即可。

manifest代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<!--<category android:name="android.intent.category.LAUNCHER" />-->
</intent-filter>
</activity>
<activity-alias
android:name=".icon_1"
android:enabled="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:targetActivity=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".icon_2"
android:enabled="false"
android:icon="@mipmap/steam_icon_72px"
android:label="@string/app_name"
android:targetActivity=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
</application>

Java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private void setIcon(int useCode){
try {
//要跟manifest的activity-alias 的name保持一致
String icon_1 = "com.mz.sanfen.appicon.icon_1";
String icon_2 = "com.mz.sanfen.appicon.icon_2";
if (useCode != 3) {
PackageManager pm = getPackageManager();
ComponentName normalComponentName = new ComponentName(getBaseContext(), icon_1);
//正常图标新状态
int normalNewState = useCode == 1 ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
if (pm.getComponentEnabledSetting(normalComponentName) != normalNewState) {//新状态跟当前状态不一样才执行
pm.setComponentEnabledSetting(
normalComponentName,
normalNewState,
PackageManager.DONT_KILL_APP);
}
ComponentName actComponentName = new ComponentName(getBaseContext(), icon_2);
//正常图标新状态
int actNewState = useCode == 2 ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
if (pm.getComponentEnabledSetting(actComponentName) != actNewState) {//新状态跟当前状态不一样才执行
pm.setComponentEnabledSetting(
actComponentName,
actNewState,
PackageManager.DONT_KILL_APP);
}
}
} catch (Exception e) {
}
}

在执行setIcon()之后,桌面会过一段时间显示新的图标,如果需要立即刷新图标,在执行setIcon()方法之后执行如下可以立即刷新。

1
2
3
4
5
6
7
8
9
10
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.addCategory(Intent.CATEGORY_DEFAULT);
List<ResolveInfo> resolves = getPackageManager().queryIntentActivities(intent, 0);
for (ResolveInfo res : resolves) {
if (res.activityInfo != null) {
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
am.killBackgroundProcesses(res.activityInfo.packageName);
}
}

使用这个方法需要添加权限:

1
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>

效果如下:

image

源码传送门

SanfenR的博客

Java中的阻塞队列

发表于 2017-01-01 |

image

JAVA中的几种主要的阻塞队列

  1. ArrayBlockingQueue: 基于数组实现的一个阻塞队列,在创建ArrayBlockingQueue对象必须指定容量大小。并且可以指定公平性和非公平性,默认情况下为非公平性,即不保存等待时间最长的队列有限能够访问队列。
  2. LinkedBlockingQueue: 基于链表实现的一个阻塞队列,在创建LinkedBlockingQueue对象如果不指定容量大小,则为Integer.MAX_VALUE.
  3. PriorityBlockingQueue: 以上2种队列都是先进先出队列,而PriorityBlockQueue却不是,它会按照元素的优先级对元素进行排序,按照优先级顺序出队,每次出队的元素都是优先级最高的元素。注意,此阻塞队列为无界阻塞队列,即容量没有上限。
  4. DelayQueue: 基于PriorityBlockingQueue: 基于PriortyQueue, 一种延时阻塞队列,DelayQueue中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue也是一种无界队列,因此往队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞。

阻塞队列的几个方法

  1. put(E e): 向队尾存入元素,如果队列满了,则等待。
  2. tack(): 从队首取元素,如果队伍为空,则等待。
  3. offer(E e, long timeout, TimeUnil unit): 如果队列满了则等待一定时间,没有插入成功则返回false,成功则返回true.
  4. poll(long timeout, TimeUnit unit): 如果队列为空,则等待一定时间,当时间期限达到时,如果未取到,则返回null.

实现

  • 阻塞队列实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class BlockingQueue {
private List<Object> queue = new LinkedList<>();
private int limit = 10;
public BlockingQueue(int limit){
this.limit = limit;
}
/**
* 当队列满了的时候阻塞线程
*
* @param item
* @throws InterruptedException
*/
public synchronized void enqueue(Object item) throws InterruptedException {
while (this.queue.size() == this.limit) {
wait();
}
if (this.queue.size() < this.limit) {
notifyAll();
}
this.queue.add(item);
}
/**
* 当队列为空的时候阻塞线程
* @return
* @throws InterruptedException
*/
public synchronized Object dequeue() throws InterruptedException {
while (this.queue.size() == 0) {
wait();
}
if (this.queue.size() > 1) {
notifyAll();
}
return this.queue.remove(0);
}
}
  • 使用阻塞队列实现生产者消费者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class ProducerConsumerPattern {
public static void main(String[] args) {
BlockingQueue shareQueue = new LinkedBlockingQueue<>();
Thread prodThread = new Thread(new Producer(shareQueue));
Thread consThread = new Thread(new Consumer(shareQueue));
prodThread.start();
consThread.start();
}
/**
* 生产者
*/
static class Producer implements Runnable {
private final BlockingQueue shareQueue;
public Producer(BlockingQueue shareQueue) {
this.shareQueue = shareQueue;
}
@Override
public void run() {
for (int i = 0; i < 10; i++){
System.out.println("Produced: " + i);
try {
shareQueue.put(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 消费者
*/
static class Consumer implements Runnable {
private final java.util.concurrent.BlockingQueue shareQueue;
public Consumer(BlockingQueue shareQueue) {
this.shareQueue = shareQueue;
}
@Override
public void run() {
while (true) {
try {
System.out.println("Consumer: " + shareQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
  • 使用Lock和Condition实现阻塞队列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length)
putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length)
takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}

源码传送门

SanfenR的博客

最大数对差(day8)

发表于 2016-12-25 |

题目

  • 题目:在数组中,数字减去它右边的数字得到一个数对之差。求所有数对之差的最大值。
    例如在数组{2, 4, 1, 16, 7, 5, 11, 9}中,
    数对之差的最大值是11,是16 减去5 的结果。

思路

  • Solution1: 将其转换成求最大子数组问题。引入辅助数组diff,长度为n-1。diff[i]=dif[i]-diff[i+1]。求出最大子数组以及位置low,high。则对应最大差为data[low]-data[high+1]。
  • Solution2: 使用动态规划法。假设data[i]减去某个数,其最大的数对差为currentMax。
    则data[i-1]的最大数对差为data[i-1]-data[i]和data[i-1]-data[i]+currentMax中的较大的那一个。从后往前遍历数组,记录最大的currentMax以及对应的low、high输出即可。
    时间复杂度O(n),空间复杂度O(1)。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void fun(int data[], int size, int &low, int &high) {
int max = data[size - 1] - data[size - 2];//最大的数对差
int currentMax = data[size - 1] - data[size - 2];//当前的数对差
int l = size - 2;
int h = size - 1;
low = l;//被减数位置
high = h;//减数位置
for (int i = size - 3; i >= 0; i--) {
if (currentMax > 0) {
currentMax = data[i] - data[i + 1] + currentMax;
l--;
} else {
currentMax = data[i] - data[i + 1];
l = i;
h = i + 1;
}
if (currentMax > max) {
low = l;
high = h;
max = currentMax;
}
}
}
void main() {
int data[] = {2, 4, 1, 16, 7, 5, 11, 9};
int low, high;
fun(data, sizeof(data) / sizeof(int), low, high);
cout << "the max is " << data[low] - data[high] << endl;
}

源码传送门

SanfenR的博客

Android中Canvas绘图(Shader)

发表于 2016-12-22 |

简介

Shader就是着色器的意思。我们可以这样理解,Canvas中的各种drawXXX方法定义了图形的形状,画笔中的Shader则定义了图形的着色、外观,二者结合到一起就决定了最终Canvas绘制的被色彩填充的图形的样子。类android.graphics.Shader有五个子类,分别是:BitmapShader、LinearGradient、RadialGradient、SweepGradient和ComposeShader,下面依次对这几个类的使用分别说明。

BitmapShader

  • BitmapShader,用Bitmap对绘制的图形进行渲染着色,其实就是用图片对图形进行贴图。
1
2
//构造函数
BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)
  1. CLAMP: 当所画图形的尺寸大于Bitmap的尺寸的时候,会用Bitmap四边的颜色填充剩余空间。
  2. REPEAT: 当我们绘制的图形尺寸大于Bitmap尺寸时,会用Bitmap重复平铺整个绘制的区域。
  3. MIRROR: 当绘制的图形尺寸大于Bitmap尺寸时,MIRROR也会用Bitmap重复平铺整个绘图区域,与REPEAT不同的是,两个相邻的Bitmap互为镜像。

实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private Shader.TileMode mDefaultTileMode = Shader.TileMode.REPEAT;
public void drawBitmap(){
bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.music_player);
BitmapShader bitmapShader = new BitmapShader(bitmap,
mDefaultTileMode,
mDefaultTileMode
);
mPaint.setShader(bitmapShader);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mPaint);
}

效果如下:

LinearGradient

  • LinearGradient, 是用来创建线性渐变效果的,它是沿着某条直线的方向渐变的,坐标(x0,y0)就是这条渐变直线的起点,坐标(x1,y1)就是这条渐变直线的终点。需要说明的是,坐标(x0,y0)和坐标(x1,y1)都是Canvas绘图坐标系中的坐标。color0和color1分别表示了渐变的起始颜色和终止颜色。与BitmapShader类似,LinearGradient也支持TileMode。

  • LinearGradient有以下三个取值:CLAMP 、REPEAT 和 MIRROR

1
2
LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile)
LinearGradient(float x0, float y0, float x1, float y1, int[] colors, float[] positions, Shader.TileMode tile)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private Shader.TileMode mDefaultTileMode = Shader.TileMode.REPEAT;
@Override
protected void onDraw(Canvas canvas) {
canvas.drawRect(0, 0, getWidth() , getHeight(), mPaint);
}
public void drawLinear(){
LinearGradient linearGradient = new LinearGradient(
0, 0, getWidth() / 4, getHeight() / 4,
Color.GREEN,
Color.BLUE,
mDefaultTileMode
);
mPaint.setShader(linearGradient);
}

效果如下:

RadialGradient

  • RadialGradient 径向渐变,径向渐变说的简单点就是个圆形中心向四周渐变的效果
1
2
3
RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, Shader.TileMode tileMode)
RadialGradient(float centerX, float centerY, float radius, int[] colors, float[] stops, Shader.TileMode tileMode)

RadialGradient是用来创建从中心向四周发散的辐射渐变效果的,所以我们需要在其构造函数中传入一些圆的参数,坐标(centerX,centerY)是圆心,即起始的中心颜色的位置,radius确定了圆的半径,在圆的半径处的颜色是edgeColor,这样就确定了当位置从圆心移向圆的轮廓时,颜色逐渐从centerColor渐变到edgeColor。RadialGradient也支持TileMode参数,有以下三个取值:CLAMP 、REPEAT 和 MIRROR。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
protected void onDraw(Canvas canvas) {
canvas.drawRect(0 , 0, getWidth(), getHeight(), mPaint);
}
public void drawRadial(){
RadialGradient radial = new RadialGradient(
getWidth() / 2, getHeight() / 2, getWidth() / 4,
Color.YELLOW, Color.RED,
mDefaultTileMode
);
mPaint.setShader(radial);
}

效果如下:

SweepGradient

SweepGradient可以用来创建360度颜色旋转渐变效果,具体来说颜色是围绕中心点360度顺时针旋转的,起点就是3点钟位置。

  • SweepGradient, 梯度渐变,也称之为扫描式渐变,因为其效果有点类似雷达的扫描效果.
1
2
SweepGradient(float cx, float cy, int color0, int color1)
SweepGradient(float cx, float cy, int[] colors, float[] positions)

SweepGradient不支持TileMode参数, 坐标(cx,cy)决定了中心点的位置,会绕着该中心点进行360度旋转。color0表示的是起点的颜色位置,color1表示的是终点的颜色位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
protected void onDraw(Canvas canvas) {
canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 4, mPaint);
}
public void drawSweep(){
//SweepGradient sweep = new SweepGradient(getWidth() /2, getHeight() / 2, Color.GREEN, Color.BLUE);
int[] colors = {Color.RED, Color.WHITE, Color.YELLOW};
float[] positions = {0f, 0.75f, 0f};
SweepGradient sweep = new SweepGradient(getWidth() / 2 , getHeight() / 2, colors, positions);
mPaint.setShader(sweep);
}

效果如下:

源码传送门

参考

自定义控件其实很简单1/3

Android中Canvas绘图之Shader使用图文详解

SanfenR的博客

二叉树中找出和为某一值的所有路径(day7)

发表于 2016-12-21 |

题目

  • 题目:输入一个整数和一棵二元树。从树的根结点开始往下访问一直到叶结点所经过的所有结点形成一条路径。打印出和与输入整数相等的所有路径。
1
2
3
4
5
6
7
8
9
例如输入整数22 和如下二元树
10
/ \
5 12
/ \
4 7
则打印出两条路径:10, 12 和10, 5, 7。

思路

  • 使用递归和栈结构。将当前路径保留在vector中。
  1. 对于空节点,返回false;
  2. 对于叶子节点,判断当前和是否为给定值,是则遍历输出栈中保存路径且返回true,否则返回false。
  3. 对于非叶子节点,将当前根节点入栈,先后递归左、右子树。且递归完后,要弹出栈中保存的当前路径。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
struct Node {
int value;
Node* left;
Node* right;
};
bool decision(Node * head, int sum, vector<int> &v){
if(!head){
return false;
} else if (!head->left && !head->right){
sum -= head->value;
if(sum == 0){
for (int i = 0; i < v.size(); i++) {
cout << v[i] << endl;
}
cout << head->value << "\n" << endl;
}
return false;
} else {
v.push_back(head->value);
sum -= head->value;
bool left = decision(head->left, sum, v);
bool right = decision(head->right, sum, v);
v.pop_back();
if (left || right){
return true;
}
}
return false;
}
int main(){
Node n4={4,NULL,NULL};
Node n5={7,NULL,NULL};
Node n3={12,NULL,NULL};
Node n2={5,&n4,&n5};
Node n1={10,&n2,&n3};
vector<int> v;
decision(&n1,22,v);
return 0;
}

源码github

SanfenR的博客

java线程池源码分析

发表于 2016-12-14 |

在什么情况下使用线程池

  1. 单个任务处理的时间比较短
  2. 将需处理的任务的数量大

使用线程池的好处

  1. 减少在创建和销毁线程上所花的时间以及系统资源的开销
  2. 如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存以及”过度切换”

案例1

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Main {
private static Executor executor = Executors.newFixedThreadPool(10);
public static void main(String[] args){
for(int i = 0; i < 20; i++){
executor.execute(new Task());
}
}
private static class Task implements Runnable{
public void run(){
System.out.println(Thread.currentThread().getName());
}
}
}
  1. Executor.newFixedThreadPool(10)初始化一个包含10个线程的executor;
  2. 通过executor.execute方法提交20个任务
  3. 负责执行任务的生命周期都由Executor框架进行管理

ThreadPoolExecutor

Executor是java线程池的工厂类**,通过它可以快速初始化一个符合业务需求的线程池,如Excutor.newFixedThreadPool方法可以生成一个拥有固定线程数的线程池。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters and default thread factory and rejected execution handler.
* It may be more convenient to use one of the {@link Executors} factory
* methods instead of this general purpose constructor.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}

其本质是通过不同的参数初始化一个ThreadPoolExecutor对象,具体参数描述如下:

corePoolSize

线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize;如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行;如果执行了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有核心线程。

maximumPoolSize

线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize。

keepAliveTime

线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间;默认情况下,该参数只在线程数大于corePoolSize时才有用。

unit

keepAliveTime的单位。

workQueue

用来保存等待被执行的任务的阻塞队列,且任务必须实现Runable接口,在JDK中提供了如下阻塞队列:

  1. ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
  2. LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
  3. SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;
  4. priorityBlockingQuene:具有优先级的无界阻塞队列;

threadFactory

创建线程的工厂,通过自定义的线程工厂可以给每个新建的线程设置一个具有识别度的线程名。

1
2
3
4
5
6
7
8
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}

handler

线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:

  1. AbortPolicy:直接抛出异常,默认策略;
  2. CallerRunsPolicy:用调用者所在的线程来执行任务;
  3. DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
  4. DiscardPolicy:直接丢弃任务;

当然也可以根据应用场景实现RejectedExecutionHandler接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。

Exectors

Exectors工厂类提供了线程池的初始化接口,主要有如下几种:

newFixedThreadPool

初始化一个指定线程数的线程池,其中corePoolSize == maximumPoolSize,使用LinkedBlockingQuene作为阻塞队列,不过当线程池没有可执行任务时,也不会释放线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue. At any point, at most
* {@code nThreads} threads will be active processing tasks.
* If additional tasks are submitted when all threads are active,
* they will wait in the queue until a thread is available.
* If any thread terminates due to a failure during execution
* prior to shutdown, a new one will take its place if needed to
* execute subsequent tasks. The threads in the pool will exist
* until it is explicitly {@link ExecutorService#shutdown shutdown}.
*
* @param nThreads the number of threads in the pool
* @return the newly created thread pool
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}

newCachedThreadPool

  1. 初始化一个可以缓存线程的线程池,默认缓存60s,线程池的线程数可达到Integer.MAX_VALUE,即2147483647,内部使用SynchronousQueue作为阻塞队列;
  2. 和newFixedThreadPool创建的线程池不同,newCachedThreadPool在没有任务执行时,当线程的空闲时间超过keepAliveTime,会自动释放线程资源,当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Creates a thread pool that creates new threads as needed, but
* will reuse previously constructed threads when they are
* available. These pools will typically improve the performance
* of programs that execute many short-lived asynchronous tasks.
* Calls to {@code execute} will reuse previously constructed
* threads if available. If no existing thread is available, a new
* thread will be created and added to the pool. Threads that have
* not been used for sixty seconds are terminated and removed from
* the cache. Thus, a pool that remains idle for long enough will
* not consume any resources. Note that pools with similar
* properties but different details (for example, timeout parameters)
* may be created using {@link ThreadPoolExecutor} constructors.
*
* @return the newly created thread pool
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}

Java线程池的原理及几类线程池的介绍

深入分析java线程池的实现原理

SanfenR的博客

MarkDowm命令

发表于 2016-12-06 |

基本用法

标题

1
2
3
4
# 一级标题
## 二级标题
### 三级标题
...

有序列表

1
2
3
4
1. 列表
2. 列表
3. 列表
...

无序列表

1
2
3
* 列表
* 列表
* 列表

引用

1
> 这是一个引用

链接与图片

1
2
3
[baidu](www.baidu.com)
![icon](http://ohqvqufyf.bkt.clouddn.com/%E4%B8%8B%E8%BD%BD.jpeg)

字体

1
2
3
*这是斜体*
**这是粗体**

表格

1
2
3
| 表格1 | 表格2 |
|--- |--- |
| 哈哈 | 呵呵 |

code

1
2
3
void helloWorld(){
print("%s", "helloWorld");
}

嵌入音乐,视频

  • MarkDowm 支持原始的html语法,可以使用html标签插入音乐和视频。

图片

HTML代码

1
2
<img src="http://ohqvqufyf.bkt.clouddn.com/girl.png" alt="girl">

效果:
girl

音乐

HTML5代码

1
2
3
4
<audio controls loop>
<source src="http://ohqvqufyf.bkt.clouddn.com/%E9%99%88%E4%B8%80%E5%8F%91%E5%84%BF%20-%20%E7%AB%A5%E8%AF%9D%E9%95%87.mp3" type="audio/mpeg"/>
Your browser does not support HTML5 audio.
</audio>

效果:



Your browser does not support HTML5 audio.

视频

HTML5代码

1
2
3
4
<video width="320" controls>
<source src="http://ohqvqufyf.bkt.clouddn.com/fyq.mp4" type="video/mp4" />
Your browser does not support HTML5 video.
</video>

效果:



Your browser does not support HTML5 video.
SanfenR的博客

二叉树转换成双向链表(day6)

发表于 2016-11-20 |

题目

  • 题目:输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。
    要求不能创建任何新的结点,只调整指针的指向。比如将二元查找树
1
2
3
4
5
6
7
8
将
10
/ \
6 14
/ \ / \
4 8 12 16
转换成双向链表4=6=8=10=12=14=16

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
struct Node {
int value;
Node* left;
Node* right;
};
bool createList(Node* head, Node* &left, Node* &right){
left = head;
right = head;
if(!head){
return false;
} else {
Node *l1, *r1, *l2, *r2;
l1 = NULL;
r1 = NULL;
l2 = NULL;
r2 = NULL;
if(createList(head->left, l1, r1)){
head->left = r1;
r1->right = head;
left = l1;
}
if(createList(head->right, l2, r2)){
head->right=l2;
l2->left=head;
right = r2;
}
return true;
}
}
int main(){
Node n4={4,NULL,NULL};
Node n5={8,NULL,NULL};
Node n6={12,NULL,NULL};
Node n7={16, NULL, NULL};
Node n2={6,&n4,&n5};
Node n3={14,&n6,&n7};
Node n1={10,&n2,&n3};
Node*Left=NULL;
Node*Right=NULL;
createList(&n1,Left,Right);
while(Left){
cout<<Left->value<<endl;
Left=Left->right;
}
return 0;
}

源码github

SanfenR的博客

Android Annotation使用

发表于 2016-11-10 |

要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的注解之前,我们就必须要了解Java为我们提供的元注解和相关定义注解的语法。

元注解

元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:

  1. @Target
  2. @Retention
  3. @Documented
  4. @Inherited

这些类型和它们所支持的类在java.lang.annotation包中可以找到。下面我们看一下每个元注解的作用和相应分参数的使用说明。

Target

@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。

作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE

取值(ElementType)有:

  1. CONSTRUCTOR:用于描述构造器
  2. FIELD:用于描述域
  3. LOCAL_VARIABLE:用于描述局部变量
  4. METHOD:用于描述方法
  5. PACKAGE:用于描述包
  6. PARAMETER:用于描述参数
  7. TYPE:用于描述类、接口(包括注解类型) 或enum声明

使用实例:

1
2
3
4
5
6
7
8
@Target(ElementType.TYPE)
public @interface Table {
/**
* 数据表名称注解,默认值为类名称
* @return
*/
public String tableName() default "className";
}

注解Table 可以用于注解类、接口(包括注解类型) 或enum声明,而注解NoDBColumn仅可用于注解类的成员变量。


Retention:

@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。
M

123
SanfenR

SanfenR

大成若缺, 大直若屈, 大巧若拙, 大智若愚。

29 日志
8 标签
GitHub
© 2017 SanfenR
由 Hexo 强力驱动
主题 - NexT.Muse

本站总访问量 次, 访客数 人次, 本文总阅读量 次