java多线程开发

来源:互联网 发布:祺嫔 知乎 编辑:程序博客网 时间:2024/05/17 23:59

多线程将一个运行程序分为多个执行体,在并行计算的处理器中交替运行,多个线程可以同时处理相同的编码和数据,也可以处理不同的编码和数据,以实现计算机的并行计算。多线程是Java语言的一大特性,运行多线程机制有效地提高了Java程序的运行能力。

在多线程技术产生之前,传统的程序运行时同一时刻只能执行单任务操作,效率非常低,如有程序在接收数据时发生阻塞,只有等程序获得数据之后才能继续运行。随着Internet的迅猛发展,这种状况不能被人们所接受,如果网络上接收数据阻塞,后台程序就处于等待状态而不继续任何操作。而这种阻塞是经常会碰到的,此时CPU资源就白白的闲置下来了。

多线程技术的出现改变了这种状况,可以同时运行多个相互独立的线程,如果创建一个线程进行数据输入输出,而创建另一个线程在后台执行其他数据处理,那么当输入输出线程在接收数据时发生阻塞,而处理数据的线程仍然在运行,多线程程序设计大大提高了程序执行效率和处理能力。

每个正在系统上运行的程序都是一个进程,每个进程包含一个或者多个线程。进程也可能是整个程序或者是部分程序的动态执行;线程是一组指令的集合或者是程序的特殊段,可以在程序中独立执行,也可以理解为代码运行的上下文。所以线程基本上是轻量级的进程,负责在单个程序中执行多个任务,通常是由操作系统负责多个线程的调度和执行。

线程是进程的实体,一个进程可以拥有多个线程,一个线程必须有一个父进程,线程不拥有系统资源,只拥有一些必须的数据结构,与父进程的其他子线程共享所拥有的全部资源,线程可以创建和撤销线程,从而实现程序的并发执行。一般来讲,线程具有初始态,就绪,阻塞和运行4中基本状态。

一个程序或者进程包含多个线程,线程可以根据程序和代码执行相应的指令。多线程看上去似乎是在并行执行各自的任务,如同在一台计算机上运行多个处理机一样。在多处理机计算机上实现多线程时,可以并行工作。和进程不同的是,线程可以共享地址空间,多个线程可以读取相同的变量或数据结构。

   多个线程的执行是并发的,即在逻辑上“同时”,而忽略物理上的“同时”。多线程和传统的单线程在程序设计上的最大区别在于各个线程的控制流彼此相互独立,所以使得各个线程之间的代码是乱序执行的。

   在Java程序中使用多线程的优势在于:

  (1)把占据很长时间的程序中的任务放到后台处理

  (2)加快程序的运行速度

  (3)在一些等待的任务实现上(如用户输入,文件读写或者网收发数据等),可以在这个处理器的空闲时间运行其他线程。

   如下语句可以创建一个线程并且启动:

   //声明一个对象实例,即创建一个线程

   Thread   t = new  Thread();

   //Thread类的start方法启动线程

   t.start();

   以上代码通过Thread() 构造方法创建了一个线程,并启动该线程,通过调用start()方法启动线程的run()方法,而Thread类的该方法没有任何操作语句,所以这个线程没有任何操作,要使用线程实现预定功能,必须定义自己的run()方法。

   在Java程序设计当中,创建线程有两种方法:

   (1)将线程类定义成Thread的子类,该线程子类中重写Thread类的run()方法。在run()方法当中实现程序的处理代码,接下来可以实例化并且启动该子类线程。

   (2)实现Runnable接口的类,该类必须实现Runnable接口的run()方法,然后分配该类的实例,在创建Thread 的时候作为一个参数来传递并且启动。

  

public class Thread

extends Object

implements Runnable

线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。

每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。每个线程都可以或不可以标记为一个守护程序。当某个线程中运行的代码创建一个新 Thread 对象时,该新线程的初始优先级被设定为创建线程的优先级,并且当且仅当创建线程是守护线程时,新线程才是守护程序。

当 Java 虚拟机启动时,通常都会有单个非守护线程(它通常会调用某个指定类的 main 方法)。Java 虚拟机会继续执行线程,直到下列任一情况出现时为止:

· 调用了 Runtime 类的 exit 方法,并且安全管理器允许退出操作发生。

· 非守护线程的所有线程都已停止运行,无论是通过从对 run 方法的调用中返回,还是通过抛出一个传播到 run 方法之外的异常。

创建新执行线程有两种方法。一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例。例如,计算大于某一规定值的质数的线程可以写成:

  class PrimeThread extends Thread {

       long minPrime;

PrimeThread(long minPrime) {

            this.minPrime = minPrime;

         }

         public void run() {

            // compute primes larger than minPrime

          . . .    

       }

  }

然后,下列代码会创建并启动一个线程:

     PrimeThread p = new PrimeThread(143);

     p.start();

创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。采用这种风格的同一个例子如下所示:

   class PrimeRun implements Runnable {

       long minPrime;

  PrimeRun(long minPrime) {

           this.minPrime = minPrime;

         }

     public void run() {

             // compute primes larger than minPrime

           . . .

      }

 }

然后,下列代码会创建并启动一个线程:

     PrimeRun p = new PrimeRun(143);

     new Thread(p).start();

构造方法详细信息

Thread

public Thread()

分配新的 Thread 对象。这种构造方法与 Thread(null, null, gname) 具有相同的作用,其中 gname 是一个新生成的名称。自动生成的名称的形式为 "Thread-"+n,其中的 n 为整数。

public Thread(Runnable target)

分配新的 Thread 对象。这种构造方法与 Thread(null, target,gname) 具有相同的作用,其中的 gname 是一个新生成的名称。自动生成的名称的形式为 “Thread-”+n,其中的 n 为整数。

每个线程的处理内容都放置在该线程对象的run()方法当中,该方法相当于传统程序当中的main方法,线程会持续运行直到run()方法返回为止,此时该线程便消亡。

package com.example.thread;public class ThreadTest{public static void main(String args[]){ThreadFirst first = new ThreadFirst(5);ThreadSecond second = new ThreadSecond(6);first.start();second.start();}}class ThreadFirst extends  Thread{int num;ThreadFirst(int num){this.num = num;}public void run(){int i=1;while(i<=num){System.out.println("First-----"+i);i++;}}}class ThreadSecond extends  Thread{int num;ThreadSecond(int num){this.num = num;}public void run(){int i=1;while(i<=num){System.out.println("second-----"+i);i++;}}}


上面是一个测试程序:

下面是测试结果

First-----1

second-----1

second-----2

second-----3

second-----4

First-----2

second-----5

second-----6

First-----3

First-----4

First-----5

由此可见,程序并不是执行完第一个线程之后才继续执行第二个线程,而是两个线程混合执行,这就是多线程的并行计算。当一个线程需要等待的时候,另一个在排队等候CPU执行的线程应该立即执行,从而不浪费时间,提高程序的执行效率。

以上实验就是多线程实现的基本实例。

下面也是一个实验,分别在两个界面中完成多线程的实验

package com.example.thread;import java.awt.BorderLayout;import javax.swing.JFrame;import javax.swing.JTextArea;public static void main(String args[]){         hh a = new hh();      a.start();      new ThreadTest().start();}}public class ThreadTest extends Thread{JFrame jf = new JFrame();JTextArea jtf = new JTextArea();public ThreadTest(){jf.setLayout(new BorderLayout());jf.add(jtf);jf.setTitle("多线程");jf.setSize(400,400);jf.setVisible(true);}public void run(){int i=1;while(i<300){  String ii =""+i;  if(i%20==0){ii=i+"\r\n";}jtf.setText(jtf.getText()+ii+",");i++;  }}class hh extends Thread{public void run(){int i = 1;while(i<=10000){ System.out.println("---"+i);    i++;}     }}



   我们故意将在控制台中的数据输出扩大到10000从而盐城CPU处理时间,这样CPU就会分割时间片(即CPU为每个线程分配一定的时间),调度不同的线程实现并行计算

   运行程序后发现控制台输出到4000多的时候,停下来了,产生了GUI界面中的300个数。

  然后控制台继续输出剩下的数直到10000.

下面给出一个实现Runnable接口的多线程线程实例

package com.example.thread;import java.util.Calendar;import java.text.SimpleDateFormat;import javax.swing.JFrame;import javax.swing.JTextArea;public class ThreadTest2{   public static void main(String args[]){ThreadFirst first = new ThreadFirst();Thread t1 = new Thread(first);t1.start();ThreadSecond second = new ThreadSecond();new Thread(second).start();new Thread(new ThreadThird()).start(); }}class ThreadFirst extends JFrame implements Runnable {    //JFrame jf = new JFrame("First");    JTextArea jtf = new JTextArea();    public ThreadFirst(){    this.setSize(100,100);    this.add(jtf);    this.setVisible(true);    }  public void run(){String date;while(true){date = new SimpleDateFormat("HH:mm:ss").format(Calendar.getInstance().getTime());     //System.out.println(date);      jtf.setText(date);     try{      Thread.sleep(1000);     }catch(Exception e){      e.printStackTrace();     }     }}}class ThreadSecond implements Runnable {  JFrame jf = new JFrame("First");   JTextArea jtf = new JTextArea();    public ThreadSecond(){    jf.setSize(100,100);    jf.setLocation(200,200);    jf.add(jtf);    jf.setVisible(true);    }public void run(){String date;while(true){date = new SimpleDateFormat("MM-dd HH:mm:ss").format(Calendar.getInstance().getTime());     //System.out.println(date);     jtf.setText(date);     try{      Thread.sleep(1000);     }catch(Exception e){      e.printStackTrace();     }     }}}class ThreadThird implements Runnable{    JFrame jf = new JFrame("Third");    JTextArea jtf = new JTextArea();    public ThreadThird(){    jf.setSize(100,100);    jf.setLocation(300,300);    jf.add(jtf);    jf.setVisible(true);    }    public void run(){String date;while(true){date = new SimpleDateFormat("yyyy-MM-dd  HH:mm:ss").format(Calendar.getInstance().getTime());    //System.out.println(date);    jtf.setText(date);        try{        Thread.sleep(1000);         }catch(Exception e){         e.printStackTrace();        }}}}


三个时钟并行执行,同时计时。

实现Runnable接口构造线程对象。该接口当中只有一个无参方法run()

实现这个接口把要操作的代码写到这个方法当中去,然后把实现整个接口类的实例传给Thread类的构造方法来产生线程对象。

还有sleep方法是要用Thread类直接调用才行的

还有SimpleDateFormat

SimpleDateFormat 是一个以与语言环境有关的方式来格式化和解析日期的具体类。它允许进行格式化(日期 -> 文本)、解析(文本 -> 日期)和规范化。 

以下示例显示了如何在美国语言环境中解释日期和时间模式。给定的日期和时间为美国太平洋时区的本地时间 2001-07-04 12:08:56。 

日期和时间模式

结果

"yyyy.MM.dd G 'at' HH:mm:ss z"

2001.07.04 AD at 12:08:56 PDT

"EEE, MMM d, ''yy"

Wed, Jul 4, '01

"h:mm a"

12:08 PM

"hh 'o''clock' a, zzzz"

12 o'clock PM, Pacific Daylight Time

"K:mm a, z"

0:08 PM, PDT

"yyyyy.MMMMM.dd GGG hh:mm aaa"

02001.July.04 AD 12:08 PM

"EEE, d MMM yyyy HH:mm:ss Z"

Wed, 4 Jul 2001 12:08:56 -0700

"yyMMddHHmmssZ"

010704120856-0700

"yyyy-MM-dd'T'HH:mm:ss.SSSZ"

2001-07-04T12:08:56.235-0700

同步

日期格式是不同步的。建议为每个线程创建独立的格式实例。如果多个线程同时访问一个格式,则它必须是外部同步的。

SimpleDateFormat

public SimpleDateFormat(String pattern)

用给定的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。注:此构造方法可能不支持所有语言环境。要覆盖所有语言环境,请使用 DateFormat 类中的工厂方法。 

参数:

pattern- 描述日期和时间格式的模式

看得出来是个构造方法不返回String类型 于是

使用SimpleDataFormat类的方法

public StringBuffer format(Date date,

                           StringBuffer toAppendTo,

                           FieldPosition pos)

将给定的 Date 格式化为日期/时间字符串,并将结果添加到给定的 StringBuffer。 

指定者:

类 DateFormat 中的 format

参数:

date- 要被格式化为日期-时间字符串的日期-时间值。

toAppendTo- 新的日期-时间文本被添加的位置。

pos- 格式化的位置。输入时:如果需要,是一个对齐字段。输出时:为对齐字段的偏移量。

返回:

格式化的日期-时间字符串。

Calendar类

getInstance

public static Calendar getInstance(TimeZone zone)

使用指定时区和默认语言环境获得一个日历。返回的 Calendar 基于当前时间,使用了给定时区和默认语言环境。 

参数:

zone- 要使用的时区

返回:

一个 Calendar。

getTime

public final Date getTime()

返回一个表示此 Calendar 时间值(从历元至现在的毫秒偏移量)的 Date 对象。 

返回:

表示时间值的 Date。

另请参见:

setTime(Date)getTimeInMillis()

format

public final String format(Date date)

将一个 Date 格式化为日期/时间字符串。 

参数:

date- 要格式化为时间字符串的时间值。

返回:

已格式化的时间字符串。

java.text
类 DateFormat

java.lang.Object

  java.text.Format

      java.text.DateFormat

所有已实现的接口:

SerializableCloneable

直接已知子类:

SimpleDateFormat

原创粉丝点击