Java多线程编程基础之线程对象

在进入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);
 }


  好了,我们终于看到,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接口给我们带来的伟大的功能。

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实例)的多个线程。

Java多线程编程基础之线程对象,码迷,mamicode.com

时间: 2024-10-01 20:24:08

Java多线程编程基础之线程对象的相关文章

java多线程编程(二创建线程)

1.概念           因为java是完全面向对象的,所以在java中,我们说的线程,就是Thread类的一个实例对象.所以,一个线程就是一个对象,它有自己字段和方法. 2.创建线程 创建线程有2种方法:1.扩展线程父类Thread类 , 2.实现Runnable接口.2种方法是由区别的. 提示:Thread类已经实现了Runnable接口,Thread类中不但实现了run方法,也定义了更多的,全面的线程对象的操作方法,而Runnable接口中只有run这一个方法. 通过扩展Thread类

Java多线程编程7--SimpleDateFormat非线程安全处理

类SimpleDateFonnat主要负责日期的转换与格式化,但在多线程的环境中,使用此类容易造成数据转换及处理的不准确,因为SimpleDateFormat类并不是线程安全的. 1.出现异常 本示例将实现使用类SimpleDateFormat在多线程环境下处理日期但得出的结果却是错误的情况,这也是在多线程环境开发中容易遇到的间题. public class MyThread extends Thread { private SimpleDateFormat sdf; private Strin

Java多线程编程(二)对象及变量的并发访问

一.synchronized同步方法 1.方法内的变量为线程安全 2.实例变量非线程安全 3.多个对象多个锁 4.synchronized方法与锁对象 5.脏读 6.synchronized锁冲入 7.出现异常,锁自动释放 8.同步不具有继承性 二.synchronized同步语句块 1.synchronized方法的弊端 2.synchronized同步代码块的使用 3.用同步代码块解决同步的弊端 4.一半异步,一半同步 5.synchronized代码块间的同步性 6.验证同步synchro

Java多线程编程之不可变对象模式

在多线程环境中,为了保证共享数据的一致性,往往需要对共享数据的使用进行加锁,但是加锁操作本身就会带来一定的开销,这里可以使用将共享数据使用不可变对象进行封装,从而避免加锁操作. 1. 模型角色 不可变对象指的是,对象内部没有提供任何可供修改对象数据的方法,如果需要修改共享变量的任何数据,都需要先构建整个共享对象,然后对共享对象进行整体的替换,通过这种方式来达到对共享对象数据一致性的保证.如下是不可变对象设计的类图: 如下是各个角色功能的描述: ImmutableObject:不可变对象的载体.对

接(第二篇) Java 多线程 编程 基础 教程

6.使用线程池ExecuteService 线程池为线程生命周期开销问题和资源不足问题提供了解决方案.通过对多个任务重用线程,线程创建的开销被分摊到多个任务上.其好处是,因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟.这样就可以立即为请求服务,使应用程序响应更快.而且,通过适当地调整线程池中的线程数目,也就是当请求的数目超过某个阈值时,就可以强制其他任何新到的请求一直等待,直到获得一个线程来处理位置,从而防止资源不足. 线程池是管理线程的高级技术,通常它提供了如下几个功能

java多线程编程(1) 线程的基本知识

在前面研究过多线程与进程的区别. 这里在稍微总结一下: 进程:程序动态的一次执行过程. 线程:可以只是程序员的一部分的执行过程 每个进程有多个线程组成,在java程序中,至少两个线程一个是垃圾回收线程和main线程. 线程占有的资源更少,早java中就是每个线程都有自己的工作区,就是有自己独立的栈空间.多个线程共享一些内存资源,堆是共享的. 多线程的力度小,并发度高,这样系统的吞吐量就很大,只有好处吗?调度和执行线程是需要资源的,就是像是数据库中的索引和数据库中的锁一个道理,并发会带来什么问题呢

Java多线程编程模式实战指南(二):Immutable Object模式--转载

本文由本人首次发布在infoq中文站上:http://www.infoq.com/cn/articles/java-multithreaded-programming-mode-immutable-object.转载请注明作者: 黄文海 出处:http://viscent.iteye.com. 多线程共享变量的情况下,为了保证数据一致性,往往需要对这些变量的访问进行加锁.而锁本身又会带来一些问题和开销.Immutable Object模式使得我们可以在不使用锁的情况下,既保证共享变量访问的线程安

Java多线程编程(学习笔记)

一.说明 周末抽空重新学习了下多线程,为了方便以后查阅,写下学习笔记. 有效利用多线程的关键是理解程序是并发执行而不是串行执行的.例如:程序中有两个子系统需要并发执行,这时候需要利用多线程编程. 通过多线程的使用,可以编写出非常高效的程序.但如果创建了太多的线程,程序执行的效率反而会降低. 同时上下文的切换开销也很重要,如果创建太多的线程,CPU花费在上下文的切换时间将对于执行程序的时间. 二.Java多线程编程 概念 在学习多线程时,我们应该首先明白另外一个概念. 进程:是计算机中的程序关于某

Java多线程编程详解

线程的同步 由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题.Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问. 由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是 synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块. 1. synchronized 方法:通过在方法声明中加入 synch