0.从一道题说起

var t = true;
setTimeout(function(){ t = false; }, 1000);
while(t){ }
alert('end');
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

问,以上代码何时alert“end”呢?
测试一下:答案是:永远都不会alert。

解析:JavaScript引擎是单线程的,事件触发排队等候。所有任务按照触发时间先后排队处理。
上例中,排队的顺序状态是:

| var t=true ; | while(t){}; | alert(‘end’); |

在1000ms之后,setTimeout函数也加入队列。
while(t){ }无限循环阻塞了单线程,不管排到后面的代码执行时间有多短,后面的代码无法执行,一直阻塞下去。

1.浏览器线程

浏览器有这么几大线程:UI渲染线程(用于页面的渲染),javascript引擎线程(用于处理js),GUI事件触发线程(用于交互)。

有时会开启的线程:http传输线程定时触发线程(定时器)

它们之间的关系是什么呢?

(1)UI渲染线程 与 javascript引擎线程 互斥

由于javascript可以操纵页面的DOM,所以如果UI渲染线程与javascript引擎线程
不互斥的话,在UI渲染线程进行页面渲染的同时,javascript引擎线程进行DOM修改,最终会造成DOM状态不一致的现象。所以,当javascript引擎线程运行的时候,UI渲染线程处于冻结状态。

(2)javascript引擎线程 与 GUI事件触发线程(用于交互) 异步

浏览器开启事件触发线程,等待用户动作,事件触发线程解析为响应事件,转移到javascript引擎线程,排队等候,等待javascript引擎的处理。

(3)javascript引擎线程 与 http传输线程 异步

网页get,post等请求,xhr异步请求都通过http传输线程,传送到javascript引擎排队,等候处理。

(4)javascript引擎线程 与 定时触发线程(定时器) 异步

setTimeout(),setInterval()由单独的线程 定时触发线程 触发,传送到javascript引擎排队等候,等待处理。

2.xhr异步是障眼法

我们来做一个试验:

客户端js代码

//jquery封装的ajax请求,请求http://localhost:3000/login页面
$.ajax({
type: "post",
url: "http://localhost:3000/login",
dataType: "json",
data:{ username: username, password: password },
success: function(data){
if(data){
if(data.message=="i202"){
alert('密码错误,请重新输入');
window.location.href="login";
}else if(data.message=="i200"){
alert('登陆成功');
window.location.href="index";
}
else{
alert('没有这个用户名');
window.location.href="login";
}
} else{ }
}
});
//这里有一个无限循环
while(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
  • 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

后端Node.js代码:

//后台对post的响应
router.post('/login', function (req, res, next) {
sleep(1000);
res.send({status:"success", message:"i200"});
}); /**
* 模拟sleep
* @param sleepTime
*/
function sleep(sleepTime) {
for(var start = +new Date; +new Date - start <= sleepTime; ) { }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

前台将永远不会alert(“登陆成功”)。浏览器通过http线程收到xhr响应,但是转到javascript线程等待执行。javascript单线程,一次只能处理一个任务。第一个任务无限循环,后面的任务全部阻塞。

xhr异步编程实际上是一种障眼法。

3.定时器时间不准

(1)时间不准1

 setTimeout(function () { while (true) { } }, 1000);
setTimeout(function () { alert('end 2'); }, 2000);
setTimeout(function () { alert('end 1'); }, 100);
alert('end');
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

运行这段代码。运行结果是alert(‘end’) alert(‘end 1’)。

前两个定时器并不能如约在规定的时间点执行哦。

(2)时间不准2

setTimeout(function(){
/* 代码块... */
setTimeout(arguments.callee, 10);
}, 10); setInterval(function(){
/*代码块... */
}, 10);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

两个定时器,本想实现相同的功能:每十秒触发一次定时器。

但是实际上,setTimeout在10ms后才加入js执行队列,排队等待。所以每两次定时器触发的时间间隔可能 > 10ms。

setInterval每10s就向js执行队列添加一个setInterval事件等待执行。前面的setInterval事件可能被它之前的事件阻塞,导致执行晚了几拍。那么没两次定时时间触发的时间间隔可能 <10ms 。

3.web worker 才是真正多线程

来吧,试验一下:

index.html

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script src="js/Fthread.js"></script>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Fthread.js

//这里创建一个webworker就是开一个新的线程
var worker=new Worker('js/Sthread.js');//创建子线程
//这里接收新的线程传来的data
worker.onmessage = function(event) {
console.log(event.data);
};
//这个将会触发向子进程的请求
worker.postMessage("begin"); //构造一个无限循环
setTimeout(function () { while (true) { } }, 1000);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Sthread.js

//这里占有一个新线程,向主线程发送消息
postMessage('hello'); //实现之前的一个实例,看是否阻塞
setTimeout(function () { console.log('end 2'); }, 2000);
setTimeout(function () { console.log('end 1'); }, 100);
console.log('end');
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

运行结果:

end
hello //这是两个线程数据的传送,可以不看
end1
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

哈哈,end1没有被阻塞,因为人家是在子线程运行滴。这才是多线程嘛。

看到某大大在博客里说可以使用webworker开线程给后台发送实时心跳。告诉后台前台没崩。不过开一个线程据说开销很大。这就。。。

HTML5 Web Worker的使用

2012/11/20 · 开发 · 1 评论 · HTML, Web Worker

分享到: 32

来源:feng_013 的博客

Web Workers 是 HTML5 提供的一个javascript多线程解决方案,我们可以将一些大计算量的代码交由web Worker运行而不冻结用户界面。

一:如何使用Worker

Web Worker的基本原理就是在当前javascript的主线程中,使用Worker类加载一个javascript文件来开辟一个新的线程,起到互不阻塞执行的效果,并且提供主线程和新线程之间数据交换的接口:postMessage,onmessage。

那么如何使用呢,我们看一个例子:

JavaScript
//worker.js
onmessage =function (evt){
var d = evt.data;//通过evt.data获得发送来的数据
postMessage( d );//将获取到的数据发送会主线程
}
1
2
3
4
5
//worker.js
onmessage =function (evt){
  var d = evt.data;//通过evt.data获得发送来的数据
  postMessage( d );//将获取到的数据发送会主线程
}

HTML页面:test.html

JavaScript
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<script type="text/javascript">
//WEB页主线程
var worker =new Worker("worker.js"); //创建一个Worker对象并向它传递将在新线程中执行的脚本的URL
worker.postMessage("hello world"); //向worker发送数据
worker.onmessage =function(evt){ //接收worker传过来的数据函数
console.log(evt.data); //输出worker发送来的数据
}
</script>
</head>
<body></body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<script type="text/javascript">
//WEB页主线程
var worker =new Worker("worker.js"); //创建一个Worker对象并向它传递将在新线程中执行的脚本的URL
worker.postMessage("hello world");     //向worker发送数据
worker.onmessage =function(evt){     //接收worker传过来的数据函数
   console.log(evt.data);              //输出worker发送来的数据
}
</script>
</head>
<body></body>
</html>

用Chrome浏览器打开test.html后,控制台输出  “hello world” 表示程序执行成功。

通过这个例子我们可以看出使用web worker主要分为以下几部分

WEB主线程:

1.通过 worker = new Worker( url ) 加载一个JS文件来创建一个worker,同时返回一个worker实例。

2.通过worker.postMessage( data ) 方法来向worker发送数据。

3.绑定worker.onmessage方法来接收worker发送过来的数据。

4.可以使用 worker.terminate() 来终止一个worker的执行。

worker新线程:

1.通过postMessage( data ) 方法来向主线程发送数据。

2.绑定onmessage方法来接收主线程发送过来的数据。

二:Worker能做什么

知道了如何使用web worker ,那么它到底有什么用,可以帮我们解决那些问题呢。我们来看一个fibonacci数列的例子。

大家知道在数学上,fibonacci数列被以递归的方法定义:F0=0,F1=1,Fn=F(n-1)+F(n-2)(n>=2,n∈N*),而javascript的常用实现为:

JavaScript
var fibonacci =function(n) {
return n <2? n : arguments.callee(n -1) + arguments.callee(n -2);
};
//fibonacci(36)
1
2
3
4
var fibonacci =function(n) {
    return n <2? n : arguments.callee(n -1) + arguments.callee(n -2);
};
//fibonacci(36)

在chrome中用该方法进行39的fibonacci数列执行时间为19097毫秒 ,而要计算40的时候浏览器直接提示脚本忙了。

由于javascript是单线程执行的,在求数列的过程中浏览器不能执行其它javascript脚本,UI渲染线程也会被挂起,从而导致浏览器进入僵死状态。使用web worker将数列的计算过程放入一个新线程里去执行将避免这种情况的出现。具体看例子:

JavaScript
//fibonacci.js
var fibonacci =function(n) {
return n <2? n : arguments.callee(n -1) + arguments.callee(n -2);
};
onmessage =function(event) {
var n = parseInt(event.data, 10);
postMessage(fibonacci(n));
};
1
2
3
4
5
6
7
8
//fibonacci.js
var fibonacci =function(n) {
    return n <2? n : arguments.callee(n -1) + arguments.callee(n -2);
};
onmessage =function(event) {
    var n = parseInt(event.data, 10);
    postMessage(fibonacci(n));
};

HTML页面:fibonacci.html

XHTML
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>web worker fibonacci</title>
<script type="text/javascript">
onload =function(){
var worker =new Worker('fibonacci.js');
worker.addEventListener('message', function(event) {
var timer2 = (new Date()).valueOf();
console.log( '结果:'+event.data, '时间:'+ timer2, '用时:'+ ( timer2 - timer ) );
}, false);
var timer = (new Date()).valueOf();
console.log('开始计算:40','时间:'+ timer );
setTimeout(function(){
console.log('定时器函数在计算数列时执行了', '时间:'+ (new Date()).valueOf() );
},1000);
worker.postMessage(40);
console.log('我在计算数列的时候执行了', '时间:'+ (new Date()).valueOf() );
}
</script>
</head>
<body>
</body>
</html>
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
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>web worker fibonacci</title>
<script type="text/javascript">
  onload =function(){
      var worker =new Worker('fibonacci.js');  
      worker.addEventListener('message', function(event) {
        var timer2 = (new Date()).valueOf();
           console.log( '结果:'+event.data, '时间:'+ timer2, '用时:'+ ( timer2  - timer ) );
      }, false);
      var timer = (new Date()).valueOf();
      console.log('开始计算:40','时间:'+ timer );
      setTimeout(function(){
          console.log('定时器函数在计算数列时执行了', '时间:'+ (new Date()).valueOf() );
      },1000);
      worker.postMessage(40);
      console.log('我在计算数列的时候执行了', '时间:'+ (new Date()).valueOf() );
  }  
  </script>
</head>
<body>
</body>
</html>

在Chrome中打开fibonacci.html,控制台得到如下输出:

JavaScript
开始计算:40 时间:1316508212705
我在计算数列的时候执行了 时间:1316508212734
定时器函数在计算数列时执行了 时间:1316508213735
结果:102334155 时间:1316508262820 用时:50115
1
2
3
4
开始计算:40 时间:1316508212705
我在计算数列的时候执行了 时间:1316508212734
定时器函数在计算数列时执行了 时间:1316508213735
结果:102334155 时间:1316508262820 用时:50115

这个例子说明在worker中执行的fibonacci数列的计算并不会影响到主线程的代码执行,完全在自己独立的线程中计算,只是在计算完成之后将结果发回主线程。

利用web worker我们可以在前端执行一些复杂的大量运算而不会影响页面的展示,并且不会弹出恶心的脚本正忙提示。

下面这个例子使用了web worker来计算场景中的像素,场景打开时是一片一片进行绘制的,一个worker只计算一块像素值。

http://nerget.com/rayjs-mt/rayjs.html

三:Worker的其他尝试

我们已经知道Worker通过接收一个URL来创建一个worker,那么我们是否可以利用web worker来做一些类似jsonp的请求呢,大家知道jsonp是通过插入script标签来加载json数据的,而script元素在加载和执行过程中都是阻塞式的,如果能利用web worker实现异步加载将会非常不错。

下面这个例子将通过 web worker、jsonp、ajax三种不同的方式来加载一个169.42KB大小的JSON数据

JavaScript
// /aj/webWorker/core.js
function $E(id) {
return document.getElementById(id);
}
onload =function() {
//通过web worker加载
$E('workerLoad').onclick =function() {
var url ='http://js.wcdn.cn/aj/mblog/face2';
var d = (new Date()).valueOf();
var worker =new Worker(url);
worker.onmessage =function(obj) {
console.log('web worker: '+ ((new Date()).valueOf() - d));
};
};
//通过jsonp加载
$E('jsonpLoad').onclick =function() {
var url ='http://js.wcdn.cn/aj/mblog/face1';
var d = (new Date()).valueOf();
STK.core.io.scriptLoader({
method:'post',
url : url,
onComplete : function() {
console.log('jsonp: '+ ((new Date()).valueOf() - d));
}
});
};
//通过ajax加载
$E('ajaxLoad').onclick =function() {
var url ='http://js.wcdn.cn/aj/mblog/face';
var d = (new Date()).valueOf();
STK.core.io.ajax({
url : url,
onComplete : function(json) {
console.log('ajax: '+ ((new Date()).valueOf() - d));
}
});
};
};
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
// /aj/webWorker/core.js
function $E(id) {
    return document.getElementById(id);
}
onload =function() {
    //通过web worker加载
    $E('workerLoad').onclick =function() {
        var url ='http://js.wcdn.cn/aj/mblog/face2';
        var d = (new Date()).valueOf();
        var worker =new Worker(url);
        worker.onmessage =function(obj) {
            console.log('web worker: '+ ((new Date()).valueOf() - d));
        };
    };
    //通过jsonp加载
    $E('jsonpLoad').onclick =function() {
        var url ='http://js.wcdn.cn/aj/mblog/face1';
        var d = (new Date()).valueOf();
        STK.core.io.scriptLoader({
            method:'post',
            url : url,
            onComplete : function() {
                console.log('jsonp: '+ ((new Date()).valueOf() - d));
            }
        });
    };
    //通过ajax加载
    $E('ajaxLoad').onclick =function() {
        var url ='http://js.wcdn.cn/aj/mblog/face';
        var d = (new Date()).valueOf();
        STK.core.io.ajax({
            url : url,
            onComplete : function(json) {
                console.log('ajax: '+ ((new Date()).valueOf() - d));
            }
        });
    };
};

HTML页面:/aj/webWorker/worker.html

XHTML
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Worker example: load data</title>
<script src="http://js.t.sinajs.cn/STK/js/gaea.1.14.js" type="text/javascript"></script>
<script type="text/javascript" src="http://js.wcdn.cn/aj/webWorker/core.js"></script>
</head>
<body>
<input type="button" id="workerLoad" value="web worker加载"></input>
<input type="button" id="jsonpLoad" value="jsonp加载"></input>
<input type="button" id="ajaxLoad" value="ajax加载"></input>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Worker example: load data</title>
<script src="http://js.t.sinajs.cn/STK/js/gaea.1.14.js" type="text/javascript"></script>
<script type="text/javascript" src="http://js.wcdn.cn/aj/webWorker/core.js"></script>
</head>
<body>
    <input type="button" id="workerLoad" value="web worker加载"></input>
    <input type="button" id="jsonpLoad" value="jsonp加载"></input>
    <input type="button" id="ajaxLoad" value="ajax加载"></input>
</body>
</html>

设置HOST

JavaScript
127.0.0.1 js.wcdn.cn
1
127.0.0.1 js.wcdn.cn

通过 http://js.wcdn.cn/aj/webWorker/worker.html 访问页面然后分别通过三种方式加载数据,得到控制台输出:

JavaScript
web worker: 174
jsonp: 25
ajax: 38
1
2
3
web worker: 174
jsonp: 25
ajax: 38

多试几次发现通过jsonp和ajax加载数据的时间相差不大,而web worker的加载时间一直处于高位,所以用web worker来加载数据还是比较慢的,即便是大数据量情况下也没任何优势,可能是Worker初始化新起线程比较耗时间。除了在加载过程中是无阻塞的之外没有任何优势。

那么web worker是否能支持跨域js加载呢,这次我们通过http://127.0.0.1/aj/webWorker/worker.html 来访问页面,当点击 “web worker加载” 加载按钮时Chrome下无任何反映,FF6下提示错误。由此我们可以知道web worker是不支持跨域加载JS的,这对于将静态文件部署到单独的静态服务器的网站来说是个坏消息。

所以web worker只能用来加载同域下的json数据,而这方面ajax已经可以做到了,而且效率更高更通用。还是让Worker做它自己擅长的事吧。

四:总结

web worker看起来很美好,但处处是魔鬼。

我们可以做什么:

1.可以加载一个JS进行大量的复杂计算而不挂起主进程,并通过postMessage,onmessage进行通信

2.可以在worker中通过importScripts(url)加载另外的脚本文件

3.可以使用 setTimeout(), clearTimeout(), setInterval(), and clearInterval()

4.可以使用XMLHttpRequest来发送请求

5.可以访问navigator的部分属性

有那些局限性:

1.不能跨域加载JS

2.worker内代码不能访问DOM

3.各个浏览器对Worker的实现不大一致,例如FF里允许worker中创建新的worker,而Chrome中就不行

4.不是每个浏览器都支持这个新特性

深入理解javascript异步编程障眼法&&h5 web worker实现多线程的更多相关文章

  1. Javascript异步编程之一异步原理

    本系列的例子主要针对node.js环境,但浏览器端的原理应该也是类似的. 本人也是Javascript新手,把自己这段时间学习积累的要点总结下来,希望可以对同样在学习Javascript/node.j ...

  2. 深入理解 JavaScript 异步——转载

    本文章转载于深入理解 JavaScript 异步 前言 2014年秋季写完了<深入理解javascript原型和闭包系列>,已经帮助过很多人走出了 js 原型.作用域.闭包的困惑,至今仍能 ...

  3. JavaScript异步编程的主要解决方案—对不起,我和你不在同一个频率上

    众所周知(这也忒夸张了吧?),Javascript通过事件驱动机制,在单线程模型下,以异步的形式来实现非阻塞的IO操作.这种模式使得JavaScript在处理事务时非常高效,但这带来了很多问题,比如异 ...

  4. JavaScript异步编程原理

    众所周知,JavaScript 的执行环境是单线程的,所谓的单线程就是一次只能完成一个任务,其任务的调度方式就是排队,这就和火车站洗手间门口的等待一样,前面的那个人没有搞定,你就只能站在后面排队等着. ...

  5. Promises与Javascript异步编程

    Promises与Javascript异步编程 转载:http://www.zawaliang.com/2013/08/399.html 在如今都追求用户体验的时代,Ajax应用真的是无所不在.加上这 ...

  6. 转: Promises与Javascript异步编程

    在如今都追求用户体验的时代,Ajax应用真的是无所不在.加上这些年浏览器技术.HTML5以及CSS3等的发展,越来越多的富Web应用出现:在给与我们良好体验的同时,Web开发人员在背后需要处理越来越多 ...

  7. JavaScript异步编程

    前言 如果你有志于成为一个优秀的前端工程师,或是想要深入学习JavaScript,异步编程是必不可少的一个知识点,这也是区分初级,中级或高级前端的依据之一.如果你对异步编程没有太清晰的概念,那么我建议 ...

  8. JavaScript 异步编程的前世今生(上)

    前言 提到 JavaScript 异步编程,很多小伙伴都很迷茫,本人花费大约一周的业余时间来对 JS 异步做一个完整的总结,和各位同学共勉共进步! 目录 part1 基础部分 什么是异步 part2 ...

  9. javascript异步编程方案汇总剖析

    code[class*="language-"] { padding: .1em; border-radius: .3em; white-space: normal; backgr ...

随机推荐

  1. 《实战java高并发程序设计》源码整理及读书笔记

    日常啰嗦 不要被标题吓到,虽然书籍是<实战java高并发程序设计>,但是这篇文章不会讲高并发.线程安全.锁啊这些比较恼人的知识点,甚至都不会谈相关的技术,只是写一写本人的一点读书感受,顺便 ...

  2. cursor 属性

    鼠标 样式 default 默认光标(通常是一个箭头) auto 默认.浏览器设置的光标. crosshair 光标呈现为十字线. pointer 光标呈现为指示链接的指针(一只手) move 此光标 ...

  3. RabbitMQ系列教程之二:工作队列(Work Queues)

        今天开始RabbitMQ教程的第二讲,废话不多说,直接进入话题.   (使用.NET 客户端 进行事例演示)          在第一个教程中,我们编写了一个从命名队列中发送和接收消息的程序. ...

  4. 用C写一个web服务器(四) CGI协议

    * { margin: 0; padding: 0 } body { font: 13.34px helvetica, arial, freesans, clean, sans-serif; colo ...

  5. asp.net MVC 网站图片防盗链的几种方法

    目录 1. 通过 URL Rewrite Module 组件 2. 通过 nginx 图片防盗链 3.自定义 HttpHandler 处理 4. 通过 MVC 自定义路由规则防盗链 5. 通过 MVC ...

  6. cpp(第十一章)

    1. std::istream & operator>>(std::istream &is,complex_c &t) { std::cout<<&qu ...

  7. 每篇半小时1天入门MongoDB——3.MongoDB可视化及shell详解

    本篇主要介绍MongoDB可视化操作以及shell使用及命令,备份恢复.数据导入导出. MongoVUE安装和简单使用 使用mongo.exe 管理数据库虽然可行,功能也挺强大,但每次都要敲命令,即繁 ...

  8. ThreadLocal来管理事务

    ThreadLocal (扩展) 1 ThreadLocal API ThreadLocal类只有三个方法: l  void set(T value):保存值: l  T get():获取值: l  ...

  9. drozer使用

    1.启用adb 端口转发 adb forward tcp:314154 tcp:31415 2.启用drozer 3.链接drozer drozer console connect     4:如果没 ...

  10. oracle删除数据后表空间仍过大问题解决方法

    -----亲测有效------- --一.备份原始数据库库--1.备份空表--在plsql里面执行一下这句话 然后把结果集 再执行一把 再导数据select 'alter table '||table ...