Java多线程编程基本之线程工具
副标题#e#
在进入java平台的线程工具之前,基于基本篇(一)的一些问题,我先插入两个根基观念。
[线程的并发与并行]
在单CPU系统中,系统调治在某一时刻只能让一个线程运行,固然这种调试机制有多种形式(大大都是时间片轮巡为主),但无论如何,要通过不绝切换需要运行的线程让其运行的方法就叫并发(concurrent)。而在多CPU系统中,可以让两个以上的线程同时运行,这种可以同时让两个以上线程同时运行的方法叫做并行(parallel)。
在上面包罗今后的所有阐述中,请列位伴侣体谅,我无法用最精确的词语来界说储如并发和并行这类术语,但我以我的履历能通俗地汇报各人它是怎么一回事,假如您看到我说的一些"尺度"文档上说的纷歧样,只要意思一致,那您就不要挑刺了。
[JAVA线程工具]
此刻我们来开始考查JAVA中线程工具。
在JAVA中,要开始一个线程,有两种方法。一是直接挪用Thread实例的start()要领,二是
将Runable实例传给一个Thread实例然后挪用它的start()要领。
在前面已经说过,线程工具和线程是两个完全差异的观念。这里我们再次深入一下,生成一个线程的实例,并不代表启动了线程。而启动线程是说在某个线程工具上启动了该实例对应的线程,当该线程竣事后,并不会就当即消失。
对付从许多书籍上可以看到的基本常识我就不消多说了。既然是基本常识,我也着重于从普通文档上读不到的内容。所以本节我重点要说的是两种线程工具发生线程方法的区别。
class MyThread extends Thread{
public int x = 0;
public void run(){
for(int i=0;i<100;i++){
try{
Thread.sleep(10);
}catch(Exception e){}
System.out.println(x++);
}
}
}
假如我们生成MyThread的一个实例,然后挪用它的start()要领,那么就发生了这个实例对应的线程:
public class Test {
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread();
mt.start();
}
}
不消说,最终会打印出0到99,此刻我们稍微玩一点格式:
public class Test {
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread();
mt.start();
System.out.println(101);
}
}
也不消说,在基本篇(一)中我们知道由于单CPU的原因,一般会先打印101,然后打印0到99。不外我们可以节制线程让它按我们的意思来运行:
public class Test {
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread();
mt.start();
mt.join();
System.out.println(101);
}
}
#p#副标题#e#
好了,我们终于看到,mt实例对应的线程(如果我有时说mt线程请你不要怪我,不外我只管不这么说)。在运行完成后,主线程才打印101。因为我们让当前线程(这里是主线程)期待mt线程的运行竣事。"在线程工具a上挪用join()要领,就是让当前正在执行的线程期待线程工具a对应的线程运行完成后才继承运行。" 请各人必然要深刻领略并熟记这句话,而我这里引出这个常识点的目标是为了让你继承看下面的例子:
public class Test {
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread();
mt.start();
mt.join();
Thread.sleep(3000);
mt.start();
}
}
当线程工具mt运行完成后,我们让主线程休息一下,然后我们再次在这个线程工具上启动线程。功效我们看到:
Exception in thread "main" java.lang.IllegalThreadStateException
也就是这种线程工具一时运行一次完成后,它就再也不能运行第二次了。我们可以看一下它有详细实现:
public synchronized void start() {
if (started)
throw new IllegalThreadStateException();
started = true;
group.add(this);
start0();
}
一个Thread的实例一旦挪用start()要领,这个实例的started标志就标志为true,事实中不管这个线程厥后有没有执行到底,只要挪用了一次start()就再也没有时机运行了,这意味着:
[通过Thread实例的start(),一个Thread的实例只能发生一个线程]
那么假如要在一个实例上发生多个线程(也就是我们常说的线程池),我们应该如何做呢?这就是Runnable接口给我们带来的伟大的成果。
#p#分页标题#e#
class R implements Runnable{
private int x = 0;
public void run(){
for(int i=0;i<100;i++){
try{
Thread.sleep(10);
}catch(Exception e){}
System.out.println(x++);
}
}
}
正如它的名字一样,Runnable的实例是可运行的,但它本身并不能直接运行,它需要被Thread工具来包装才行运行:
public class Test {
public static void main(String[] args) throws Exception{
new Thread(new R()).start();
}
}
虽然这个功效和mt.start()没有什么区别。但假如我们把一个Runnable实例给Thread工具多次包装,我们就可以看到它们实际是在同一实例上启动线程:
public class Test {
public static void main(String[] args) throws Exception{
R r = new R();
for(int i=0;i<10;i++)
new Thread(r).start();
}
}
x是实例工具,但功效是x被加到了999,说明这10个线程是在同一个r工具上运行的。请各人留意,因为这个例子是在单CPU上运行的,所以没有对多个线程同时操纵配合的工具举办同步。这里是为了说明的利便而简化了同步,而真正的情况中你无法预知措施会在什么情况下运行,所以必然要思量同步。
到这里我们做一个完整的例子来说明线程发生的方法差异而生成的线程的区别:
package debug;
import java.io.*;
import java.lang.Thread;
class MyThread extends Thread{
public int x = 0;
public void run(){
System.out.println(++x);
}
}
class R implements Runnable{
private int x = 0;
public void run(){
System.out.println(++x);
}
}
public class Test {
public static void main(String[] args) throws Exception{
for(int i=0;i<10;i++){
Thread t = new MyThread();
t.start();
}
Thread.sleep(10000);//让上面的线程运行完成
R r = new R();
for(int i=0;i<10;i++){
Thread t = new Thread(r);
t.start();
}
}
}
上面10个线程工具发生的10个线程运行时打印了10次1。下面10个线程工具发生的10个线程运行时打印了1到10。我们把下面的10个线程称为同一实例(Runnable实例)的多个线程。
下节我们将研究线程工具要领,照旧那句话,一般文档中可以读到的内容我不会先容太多
请各人本身相识。