java-多线程新特性
Java定时器相关Timer和TimerTask类
每个Timer对象相对应的是单个后台线程,用于顺序地执行所有计时器任务TimerTask对象。
Timer有两种执行任务的模式,最常用的是schedule,它可以以两种方式执行任务:1:在某个时间(Data),2:在某个固定的时间之后(long delay),都可以指定任务执行的固定延迟(long period)。
另一种是scheduleAtFixedRate,它可以在1:在某个时间(Data),2:在某个固定的时间之后(long delay),以固定的频率(long period)执行任务。
在固定延迟执行中,根据前一次执行的实际执行时间来安排每次执行。如果由于任何原因(如垃圾回收或其他后台活动)而延迟了某次执行,则后续执行也将被延迟。
在固定速率执行中,相对于已安排的初始执行时间来安排每次执行。如果由于任何原因(如垃圾回收或其他后台活动)而延迟了某次执行,则将快速连续地出现两次或更多次执行,从而使后续执行能够赶上来。
区别在于,如果指定开始执行的时间在当前系统运行时间之前,scheduleAtFixedRate会把已经过去的时间也作为周期执行,而schedule不会把过去的时间算上。
TimerTask为抽象类,由子类覆写run方法实现计时器任务要执行的操作。
实例:
import java.util.*;
public class TimerTest { int count =0; public static void main(String[] args){ new Timer().schedule(new TimerTest().new MyTimerTask(), 2000); while(true){
//System.out.println(new Date().getSeconds());
System.out.println(new GregorianCalendar().get(Calendar.SECOND));
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class MyTimerTask extends TimerTask{ public void run(){
count =(count+1)%2;
System.out.println("Attention!bomb!");
new Timer().schedule(new MyTimerTask(), 2000+2000*count);
}
}
}
线程范围内共享变量——ThreadLocal
API描述:ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。
ThreadLocal用来隔离线程间的变量访问和修改。
Java提供的synchronized关键字使用了“同步锁”的机制来阻止线程的竞争访问,即“以时间换空间”。ThreadLocal则使用了“拷贝副本”的方式,人人有份,你用你的,我用我的,大家互不影响,是“以空间换时间”。每个线程修改变量时,实际上修改的是变量的副本,不怕影响到其它线程。
原理:将该ThreadLocal实例作为key,要保持的对象的引用作为value,通过ThreadLocal.set()设置到当前线程的ThreadLocalMap中,执行 ThreadLocal.get()时,各线程从ThreadLocalMap中取出放进去的对象,因此取出来的是各自自己线程中的对象。
import java.util.*;
public class ThreadLocalDemo implements Runnable {
//创建线程局部变量studentLocal
private final static ThreadLocal<Student> studentLocal = new ThreadLocal<Student>(); public static void main(String[] agrs) { ThreadLocalDemo td = new ThreadLocalDemo();
new Thread(td).start();
new Thread(td).start();
} public void run() { System.out.println(Thread.currentThread().getName() + " is running!");
int age = new Random().nextInt(100); System.out.println(Thread.currentThread().getName() + " set age to:" + age); Student student = getStudent();
student.setAge(age);
System.out.println(Thread.currentThread().getName() + " first read age is:" + student.getAge()); try {
Thread.sleep(2000);
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " second read age is:" + student.getAge()); } protected Student getStudent() {
//获取本地线程变量并强制转换为Student类型
Student student = (Student) studentLocal.get();
//线程首次执行此方法的时候,studentLocal.get()肯定为null
if (student == null) {
//创建一个Student对象,并保存到本地线程变量studentLocal中
student = new Student();
studentLocal.set(student);
}
return student;
}
}
//被多线程操纵的javabean
public class Student {
private int age = 0; public int getAge() {
return this.age;
} public void setAge(int age) {
this.age = age;
}
}
---------- 运行 ----------
Thread-0 is running!
Thread-1 is running!
Thread-1 set age to:57
Thread-1 first read age is:57
Thread-0 set age to:44
Thread-0 first read age is:44
Thread-1 second read age is:57
Thread-0 second read age is:44 输出完成 (耗时 2 秒) - 正常终止
线程池
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。作用就是限制系统中执行线程的数量。线程池根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果。
合理利用线程池能够带来三个好处:
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
newSingleThreadExecutor
创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
例如:ExecutorService pool = Executors.newSingleThreadExecutor();
newFixedThreadPool
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
例如:ExecutorService pool = Executors.newFixedThreadPool(2);
newCachedThreadPool
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
例如:ExecutorService pool = Executors.newCachedThreadPool();
newScheduledThreadPool
创建一个延迟连接的线程池。此线程池可安排在给定延迟后运行命令或者定期地执行。
例如:
ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
pool.scheduleAtFixedRate(new Runnable() {
public void run() {
System.out.println(System.currentTimeMillis());
}}
, 1000
, 2000
, TimeUnit.MILLISECONDS);
Callable和Future接口
Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。
Callable和Runnable有几点不同:
- Callable规定的方法是call(),而Runnable规定的方法是run()。
- Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
- call()方法可抛出异常,而run()方法是不能抛出异常的。
- 运行Callable任务可拿到一个Future对象 。
Future表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可了解任务执行情况isCancelled和isDone,可使用cancel方法取消任务的执行,还可使用get方法获取任务执行的结果。
import java.util.concurrent.*;
import java.util.*;
public class Test { public static void main(String[] args) throws Exception{ int taskSize = 5;
//创建一个线程池
ExecutorService pool = Executors.newFixedThreadPool(taskSize);
//创建多个有返回值的任务
List<Future> list = new ArrayList<Future>();
for (int i = 0; i < taskSize; i++) {
Callable<Object> c = new MyCallable(i);
//执行任务并获取Future对象
Future<Object> f = pool.submit(c);
list.add(f);
}
// 关闭线程池
pool.shutdown(); // 获取所有并发任务的运行结果
for (Future f : list) {
//从Future对象上获取任务的返回值,并输出到控制台
System.out.println(f.get());
}
}
} class MyCallable implements Callable<Object> {
private int taskNum; MyCallable(int taskNum) {
this.taskNum = taskNum;
} public Object call() throws Exception {
System.out.println(">>>" + taskNum + "任务启动");
long start =System.currentTimeMillis(); Thread.sleep(1000); long end = System.currentTimeMillis(); System.out.println(">>>>>>" + taskNum + "任务终止");
return taskNum + "任务返回运行结果,耗时【" + (end - start) + "毫秒】";
}
} ---------- 运行 ----------
>>>0任务启动
>>>2任务启动
>>>1任务启动
>>>3任务启动
>>>4任务启动
>>>>>>0任务终止
0任务返回运行结果,耗时【1001毫秒】
>>>>>>2任务终止
>>>>>>3任务终止
>>>>>>1任务终止
1任务返回运行结果,耗时【1001毫秒】
2任务返回运行结果,耗时【1001毫秒】
3任务返回运行结果,耗时【1001毫秒】
>>>>>>4任务终止
4任务返回运行结果,耗时【1001毫秒】 输出完成 (耗时 1 秒) - 正常终止
读写锁
分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥。
import java.util.Random;
import java.util.concurrent.locks.*; public class ReadWriteLockTest { public static void main(String[] args) {
final TheData myData=new TheData();
for(int i=0;i<3;i++){//分别开启3个线程操作读写
new Thread(new Runnable(){
public void run() {
while(true){
myData.get();
}
}
}).start(); new Thread(new Runnable(){
public void run() {
while(true){
myData.put(new Random().nextInt(1000));
}
}
}).start();
}
}
}
class TheData{
private Object data=null;
private ReadWriteLock rwl=new ReentrantReadWriteLock();
public void get(){
rwl.readLock().lock(); //读锁开启,读线程均可进入
try {
System.out.println(Thread.currentThread().getName()+"准备读取---------");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"已经取到---------"+data);
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
rwl.readLock().unlock();
}
} public void put(Object data){
rwl.writeLock().lock(); //写锁开启,这时只有一个写线程进入
try {
System.out.println(Thread.currentThread().getName()+"准备写入>>>>>>>>>");
Thread.sleep(1000);
this.data=data;
System.out.println(Thread.currentThread().getName()+"已经写入>>>>>>>>>"+data);
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
rwl.writeLock().unlock();
}
}
} ---------- 运行 ----------
Thread-0准备读取---------
Thread-2准备读取---------
Thread-0已经取到---------null
Thread-2已经取到---------null
Thread-1准备写入>>>>>>>>>
Thread-1已经写入>>>>>>>>>796
Thread-3准备写入>>>>>>>>>
Thread-3已经写入>>>>>>>>>237
Thread-3准备写入>>>>>>>>>
Thread-3已经写入>>>>>>>>>353
Thread-5准备写入>>>>>>>>>
Thread-5已经写入>>>>>>>>>646
Thread-5准备写入>>>>>>>>>
Thread-5已经写入>>>>>>>>>84
Thread-4准备读取---------
Thread-0准备读取---------
Thread-2准备读取---------
Thread-0已经取到---------84
Thread-2已经取到---------84
Thread-4已经取到---------84
Thread-1准备写入>>>>>>>>>
Thread-1已经写入>>>>>>>>>898
Thread-1准备写入>>>>>>>>> 输出完成 (耗时 9 秒) - 已被用户取消。
Semaphore
使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。
Semaphore实现的功能就类似厕所有5个坑,假如有十个人要上厕所,那么同时能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中的任何一个人让开后,其中在等待的另外5个人中又有一个可以占用了。另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。
import java.util.concurrent.*;
public class SemaphoreTest {
public static void main(String[] args) {
ExecutorService service= Executors.newCachedThreadPool();
final Semaphore sp = new Semaphore(5);
for(int i=0;i<10;i++){
Runnable r = new Runnable(){
public void run(){
try {
sp.acquire();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() +
"进入,当前已有" + (5-sp.availablePermits()) + "个并发");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() +
"即将离开");
sp.release();
//下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元
System.out.println("线程" + Thread.currentThread().getName() +
"已离开,当前已有" + (5-sp.availablePermits()) + "个并发");
}
};
service.execute(r);
}
service.shutdown();
}
}
---------- 运行 ----------
线程pool-1-thread-1进入,当前已有1个并发
线程pool-1-thread-4进入,当前已有3个并发
线程pool-1-thread-2进入,当前已有2个并发
线程pool-1-thread-3进入,当前已有4个并发
线程pool-1-thread-6进入,当前已有5个并发
线程pool-1-thread-4即将离开
线程pool-1-thread-8进入,当前已有5个并发
线程pool-1-thread-4已离开,当前已有5个并发
线程pool-1-thread-2即将离开
线程pool-1-thread-10进入,当前已有5个并发
线程pool-1-thread-2已离开,当前已有5个并发
线程pool-1-thread-6即将离开
线程pool-1-thread-6已离开,当前已有4个并发
线程pool-1-thread-3即将离开
线程pool-1-thread-3已离开,当前已有3个并发
线程pool-1-thread-5进入,当前已有4个并发
线程pool-1-thread-1即将离开
线程pool-1-thread-1已离开,当前已有3个并发
线程pool-1-thread-7进入,当前已有4个并发
线程pool-1-thread-9进入,当前已有5个并发
线程pool-1-thread-5即将离开
线程pool-1-thread-10即将离开
线程pool-1-thread-10已离开,当前已有4个并发
线程pool-1-thread-8即将离开
线程pool-1-thread-8已离开,当前已有3个并发
线程pool-1-thread-5已离开,当前已有2个并发
线程pool-1-thread-7即将离开
线程pool-1-thread-7已离开,当前已有1个并发
线程pool-1-thread-9即将离开
线程pool-1-thread-9已离开,当前已有0个并发 输出完成 (耗时 4 秒) - 正常终止
同步工具类
CyclicBarrier
一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时CyclicBarrier很有用。因为该barrier 在释放等待线程后可以重用,所以称它为循环的barrier。比如一个大型的任务,常常需要分配好多子任务去执行,只有当所有子任务都执行完成时候,才能执行主任务,这时候就可以选择CyclicBarrier了。
int |
await() 在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。 |
int |
await(long timeout, 在所有参与者都已经在此屏障上调用 await 方法之前将一直等待,或者超出了指定的等待时间。 |
import java.util.concurrent.*; public class CyclicBarrierTest { public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final CyclicBarrier cb = new CyclicBarrier(3);
for(int i=0;i<3;i++){
Runnable runnable = new Runnable(){
public void run(){
try {
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() +
"即将到达集合地点1,当前已有" + (cb.getNumberWaiting()+1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));
cb.await(); Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() +
"即将到达集合地点2,当前已有" + (cb.getNumberWaiting()+1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));
cb.await();
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() +
"即将到达集合地点3,当前已有" + (cb.getNumberWaiting() + 1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));
cb.await();
} catch (Exception e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
service.shutdown();
}
}
---------- 运行 ----------
线程pool-1-thread-1即将到达集合地点1,当前已有1个已经到达,正在等候
线程pool-1-thread-2即将到达集合地点1,当前已有2个已经到达,正在等候
线程pool-1-thread-3即将到达集合地点1,当前已有3个已经到达,都到齐了,继续走啊
线程pool-1-thread-1即将到达集合地点2,当前已有1个已经到达,正在等候
线程pool-1-thread-2即将到达集合地点2,当前已有2个已经到达,正在等候
线程pool-1-thread-3即将到达集合地点2,当前已有3个已经到达,都到齐了,继续走啊
线程pool-1-thread-2即将到达集合地点3,当前已有1个已经到达,正在等候
线程pool-1-thread-3即将到达集合地点3,当前已有2个已经到达,正在等候
线程pool-1-thread-1即将到达集合地点3,当前已有3个已经到达,都到齐了,继续走啊 输出完成 (耗时 17 秒) - 正常终止
CountDownLatch
一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
方法摘要 | |
---|---|
void |
await() 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。 |
boolean |
await(long timeout, 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。 |
void |
countDown() 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。 |
long |
getCount() 返回当前计数。 |
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class CountdownLatchTest { public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final CountDownLatch cdOrder = new CountDownLatch(1);
final CountDownLatch cdAnswer = new CountDownLatch(3);
for(int i=0;i<3;i++){
Runnable runnable = new Runnable(){
public void run(){
try {
System.out.println("线程" + Thread.currentThread().getName() +
"正准备接受命令");
cdOrder.await();
System.out.println("线程" + Thread.currentThread().getName() +
"已接受命令");
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() +
"回应命令处理结果");
cdAnswer.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
try {
Thread.sleep((long)(Math.random()*10000)); System.out.println("线程" + Thread.currentThread().getName() +
"即将发布命令");
cdOrder.countDown();
System.out.println("线程" + Thread.currentThread().getName() +
"已发送命令,正在等待结果");
cdAnswer.await();
System.out.println("线程" + Thread.currentThread().getName() +
"已收到所有响应结果");
} catch (Exception e) {
e.printStackTrace();
}
service.shutdown(); }
}
---------- 运行 ----------
线程pool-1-thread-1正准备接受命令
线程pool-1-thread-3正准备接受命令
线程pool-1-thread-2正准备接受命令
线程main即将发布命令
线程pool-1-thread-1已接受命令
线程pool-1-thread-3已接受命令
线程main已发送命令,正在等待结果
线程pool-1-thread-2已接受命令
线程pool-1-thread-1回应命令处理结果
线程pool-1-thread-2回应命令处理结果
线程pool-1-thread-3回应命令处理结果
线程main已收到所有响应结果 输出完成 (耗时 10 秒) - 正常终止
Exchanger
提供了一个可以在对中对元素进行配对和交换的线程的同步点。每个线程将条目上的某个方法呈现给 exchange
方法,与伙伴线程进行匹配,并且在返回时接收其伙伴的对象。
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class ExchangerTest { public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final Exchanger exchanger = new Exchanger();
service.execute(new Runnable(){
public void run() {
try { String data1 = "111";
System.out.println("线程" + Thread.currentThread().getName() +
"正在把数据" + data1 +"换出去");
Thread.sleep((long)(Math.random()*10000));
String data2 = (String)exchanger.exchange(data1);
System.out.println("线程" + Thread.currentThread().getName() +
"换回的数据为" + data2);
}catch(Exception e){ }
}
});
service.execute(new Runnable(){
public void run() {
try { String data1 = "222";
System.out.println("线程" + Thread.currentThread().getName() +
"正在把数据" + data1 +"换出去");
Thread.sleep((long)(Math.random()*10000));
String data2 = (String)exchanger.exchange(data1);
System.out.println("线程" + Thread.currentThread().getName() +
"换回的数据为" + data2);
}catch(Exception e){ }
}
});
service.shutdown();
}
}
---------- 运行 ----------
线程pool-1-thread-1正在把数据111换出去
线程pool-1-thread-2正在把数据222换出去
线程pool-1-thread-2换回的数据为111
线程pool-1-thread-1换回的数据为222 输出完成 (耗时 5 秒) - 正常终止
java-多线程新特性的更多相关文章
- Java多线程-新特性-线程池
Sun在Java5中,对Java线程的类库做了大量的扩展,其中线程池就是Java5的新特征之一,除了线程池之外,还有很多多线程相关的内容,为多线程的编程带来了极大便利.为了编写高效稳定可靠的多线程程序 ...
- Java多线程-新特性-有返回值的线程
在Java5之前,线程是没有返回值的,常常为了“有”返回值,破费周折,而且代码很不好写.或者干脆绕过这道坎,走别的路了. 现在Java终于有可返回值的任务(也可以叫做线程)了. 可返回值的任务必须实现 ...
- Java 8新特性前瞻
快端午小长假了,要上线的项目差不多完结了,终于有时间可以坐下来写篇博客了. 这是篇对我看到的java 8新特性的一些总结,也是自己学习过程的总结. 几乎可以说java 8是目前为止,自2004年jav ...
- Java 8 新特性之 Stream 流基础体验
Java 8 新特性之 Stream 流基础体验 package com.company; import java.util.ArrayList; import java.util.List; imp ...
- Java 8 新特性——检视阅读
Java 8 新特性--检视阅读 参考 Java 8 新特性--菜鸟 Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程,新的 JavaScript 引擎,新的 ...
- Java 8新特性-4 方法引用
对于引用来说我们一般都是用在对象,而对象引用的特点是:不同的引用对象可以操作同一块内容! Java 8的方法引用定义了四种格式: 引用静态方法 ClassName :: staticMetho ...
- Spring 4支持的Java 8新特性一览
有众多新特性和函数库的Java 8发布之后,Spring 4.x已经支持其中的大部分.有些Java 8的新特性对Spring无影响,可以直接使用,但另有些新特性需要Spring的支持.本文将带您浏览S ...
- java 8 新特性
最近在IDEA的️驱使下,看了点java8的东西,链接贴一下,,,,, 1.Java 8新特性概述2.Java 8中的 Stream API 详解[3.Java 8新特性终极指南] 简单的使用看完新特 ...
- Java 8 新特性终极版
声明:本文翻译自Java 8 Features Tutorial – The ULTIMATE Guide,翻译过程中发现并发编程网已经有同学翻译过了:Java 8 特性 – 终极手册,我还是坚持自己 ...
- Java 8新特性探究(八)精简的JRE详解
http://www.importnew.com/14926.html 首页 所有文章 资讯 Web 架构 基础技术 书籍 教程 Java小组 工具资源 - 导航条 - 首页 所有文章 资讯 ...
随机推荐
- Appium移动自动化测试之安装Android SDK和JDK
安装好Appium后,我们来继续安装Android SDK和JDK,JDK的安装以及环境变量配置这边就不再多说了,毕竟都是从事自动化的,这个应该是so easy.闲言少续,我们来操作Android S ...
- NGUI 多场景情况下 管理多个界面
简单的说就是在一个AllUI场景中,所有场景所需要的界面都挂在一个Empty GameObject下,然后这个Empty GameObject在代码中DontDestroyOnLoad,但是回到这个A ...
- CSS浮动布局与菜单栏设计
公司周六停电,终于可以双休了.用周五空余时间再夯实一下css基础,<CSS权威指南>概念性的内容看起来容易犯困,不如实践来得快,动手操作吧. 一.浮动布局 浮动存在问题:浮动使元素脱离文档 ...
- MD5编码的内存泄露
MD5CryptoServiceProvider 如果多次使用会产生内存溢出,如下这样调用几百万次就会出现内存 溢出. public static string MD5Encode(string so ...
- Scala中Iterator允许执行一次
背景 使用spark执行mapPartitionsWithIndex((index,iterator)=>{....}),在执行体中将iterator进行一次迭代后,再次根据iterator执行 ...
- 压缩文本、字节或者文件的压缩辅助类-GZipHelper
下面为大家介绍一.NET下辅助公共类GZipHelper,该工具类主要作用是对文本.字符.文件等进行压缩与解压.该类主要使用命名空间:System.IO.Compression下的GZipStream ...
- 十大开源的.NET用户界面框架 让GUI设计不再犯难
选择一款合适的GUI框架是.NET开发中比较重要但又很棘手的问题,因为用户界面相当于一款应用的"门面",直接面向用户.好的UI更能吸引用户,有时甚至成为决定一款应用成败的关键.下面 ...
- bootstrap表格内容跑到表格外面的处理办法
http://stackoverflow.com/questions/21587813/bootstrap-responsive-table-content-wrapping td写下这个样式即可.& ...
- Ceph剖析:消息处理
作者:吴香伟 发表于 2014/10/9 版权声明:可以任意转载,转载时务必以超链接形式标明文章原始出处和作者信息以及版权声明 总体上,Ceph的消息处理框架是发布者订阅者的设计结构.Messenge ...
- DISK 100% BUSY,谁造成的?
iostat等命令看到的是系统级的统计,如果要追查是哪个进程导致的I/O繁忙,应该怎么办? iostat等命令看到的是系统级的统计,比如下例中我们看到/dev/sdb很忙,如果要追查是哪个进程导致的I ...