[线程的并发与并行]

在单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);

 }

}

  好了,我们终于看到,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的实例只能产生一个线程](不管是extends Thread, 还是implements Runnable)



  那么如果要在一个实例上产生多个线程(也就是我们常说的线程池),我们应该如何做呢?这就是Runnable接口给我们带来的伟大的功能。

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++)   // 10个Thread实例,产生了10个线程,一个Thread实例对应一个线程,一个线程独享MyTread中的变量,线程独立不相关

{

    Thread t = new MyThread();

    t.start();           //每个线程只能start一次,否则会报.IllegalThreadStateException

  }

  Thread.sleep(10000);//让上面的线程运行完成

  R r = new R();

  for(int i=0;i<10;i++)       //10个Thread实例,产生了10个线程,一个Thread实例对应一个线程,10个线程共享R中变量

{

    Thread t = new Thread(r);

    t.start();                            //每个线程只能start一次,否则会报.IllegalThreadStateException

  }

 }  

}

上面10个线程对象产生的10个线程运行时打印了10次1。

下面10个线程对象产生的10个线程运行时打印了1到10。我们把下面的10个线程称为同一实例(Runnable实例)的多个线程。

 

Thread Runnable 区别的更多相关文章

  1. Android笔记——Handler Runnable与Thread的区别

    在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继承了Thread类同时覆写了本类中的run() ...

  2. Java中Runnable和Thread的区别

    在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继承了Thread类同时覆写了本类中的run() ...

  3. Runnable和Thread的区别 (转)

    在java中可有两种方式实现多线程,一种是继承 Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继承了Thread类同时覆写了本类中的 run ...

  4. 【转】java线程系列---Runnable和Thread的区别

    在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继承了Thread类同时覆写了本类中的run() ...

  5. java多线程机制中的Thread和Runnable()区别

    1.java语言使用Thread类及其子类对象来表示线程,新建的一个线程声明周期中经历 新建.(声明一个线程,此时他已经有了相应的内存空间和其他资源),运行(线程创建之久就据用了运行的条件,一旦轮到使 ...

  6. Android开发笔记之:Handler Runnable与Thread的区别详解

    在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一 个类只要继承了Thread类同时覆写了本类中的run( ...

  7. java线程系列---Runnable和Thread的区别 (转载)

    转自:http://blog.csdn.net/wwww1988600/article/details/7309070 在java中可有两种方式实现多线程,一种是继承 Thread类,一种是实现Run ...

  8. java Thread和Runnable区别

    ①Thread类实现了Runnable接口,主要构造方法为Thread(Runnable target).Thread(Runnable target,String name).Thread(Stri ...

  9. runnable和thread的区别

    一是写一个类继承自Thread类,然后重写里面的run方法,用start方法启动线程二是写一个类实现Runnable接口,实现里面的run方法,用new Thread(Runnable target) ...

随机推荐

  1. 02-offsetLeft和offsetTop

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

  2. 洛谷P2498 [SDOI2012]拯救小云公主 【二分 + 并查集】

    题目 英雄又即将踏上拯救公主的道路-- 这次的拯救目标是--爱和正义的小云公主. 英雄来到boss的洞穴门口,他一下子就懵了,因为面前不只是一只boss,而是上千只boss.当英雄意识到自己还是等级1 ...

  3. bzoj2120 数颜色 莫队 带修改

    [bzoj2120]数颜色 Description 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会像你发布如下指令: 1. Q L R代表询问你从第L支画笔 ...

  4. mongodb的安装及环境配置

    一 下载 官网:https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-ssl-3.6.3-signed.msi 官网可能打不开, ...

  5. poj 1486 Sorting Slides

    Sorting Slides Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 4469   Accepted: 1766 De ...

  6. 转自CSDN,关于状态机

    有限状态机FSM思想广泛应用于硬件控制电路设计,也是软件上常用的一种处理方法(软件上称为FMM--有限消息机).它把 复杂的控制逻辑分解成有限个稳定状态,在每个状态上判断事件,变连续处理为离散数字处理 ...

  7. Yii 之视图布局

    控制器代码: //设置的布局文件 public $layout = 'common'; public function actionAbout(){ $data = array('page_name' ...

  8. 第一行代码 Android 思维导图

    第一行代码 Android  思维导图

  9. 非旋转Treap:用运行时间换调试时间的有效手段

    非旋转Treap:用运行时间换调试时间的有效手段   Hello大家好,我们今天来聊一聊非旋转Treap. 相信各位或多或少都做过些序列上的问题.如果水题我们考虑暴力:不强制在线我们可能用过莫队和待修 ...

  10. codevs——1294 全排列

    1294 全排列  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解  查看运行结果     题目描述 Description 给出一个n, 请输出n的所有全 ...