[线程的并发与并行]

在单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. Percona XtraBackup 2.4新特性之恢复单个表数据

    参考文档:https://www.percona.com/doc/percona-xtrabackup/2.4/xtrabackup_bin/restoring_individual_tables.h ...

  2. tab栏切换效 简易效果

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

  3. hihoCoder #1471 拥堵的城市

    这道题目是hihoCoder Challenge 27的C题,我考虑了5天:(. 计数问题.由于树的结构的特殊性(树具有递归结构),不难想到思路是树形DP.由于这是[计数问题]而非[优化问题],我们思 ...

  4. 3931: [CQOI2015]网络吞吐量【网络流】

    网络吞吐量(network)题目描述路由是指通过计算机网络把信息从源地址传输到目的地址的活动,也是计算机网络设计中的重点和难点.网络中实现路由转发的硬件设备称为路由器.为了使数据包最快的到达目的地,路 ...

  5. Codeforces787D - Legacy

    Description \(n(n\leq10^5)\)个点构成的有向图,有\(m(m\leq10^5)\)条连通信息,信息有三种: 1 u v w,表示存在一条边权为\(w\)的有向边\((u,v) ...

  6. 纯干货,Mysql innodb的ACID特性是怎么实现的?以及高并发情况下会出现的问题

    首先说说什么是ACID: 它们分别是Atomicity(原子性),Consistency(一致性),Isolation(隔离性),Transaction(持久性) 原子性: 意为单个事务里的多个操作要 ...

  7. gridview和detailsview的完美结合运用实现增删改

    原文发布时间为:2008-07-24 -- 来源于本人的百度文章 [由搬家工具导入] 1、因Gridview中没有增加记录,所以应利用datalistview或formview来弥补。 2、因为det ...

  8. android:logo

    <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="ht ...

  9. 连接mysql报错 : The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone...

    time zone 时区错误 DBEAVER连接MySQL运行报错The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or repres ...

  10. PYTHON 源码

    http://www.wklken.me/index2.html http://blog.csdn.net/dbzhang800/article/details/6683440