Java实现多线程的方法

 
n  在java的历史版本中,有两种创建多线程程序的方法
1.       通过创建Thread类的子类来实现
2.       通过实现Runable接口的类来实现(推荐)
一、通过Thread类实现多线程
设计Thread的子类
根据工作需要重新设计线程的run方法
n  线程类Thread中提供的run是一个空方法。为此,我们可以继承Thread,然后覆盖(override)其中的run,使得该线程能够完成特定的工作。
使用start方法启动线程,将执行权转交到run。
实例CODE:
import java.util.Random;
 
class MyThread extends Thread{
    private int sleepTime;
    private static Random generator=new Random();
    public MyThread(String n){
       super(n);
       } //n:线程名称
    public void run(){
        for(int i=0;i<5;i++){
            // 睡眠一段随机时间
            try{
             sleepTime=generator.nextInt(1000);
             Thread.sleep(sleepTime);
            }catch (InterruptedException e)
                     {e.printStackTrace();  }       
           //显示本线程名称
           System.out.print(getName()+"    ");
        } 
    }
}
 
public class MultiThreadExample{
    public static void main(String []args){
       new MyThread("A").start(); //启动线程A
       new MyThread("B").start(); //启动线程B 
    }
}
二、通过实现Runnable接口创建线程
n  创建线程最简单的方法就是创建一个实现Runnable 接口的类,这个类仅需实现其中一个run()方法。
n  主要步骤:
1.       创建某个类实现Runnable接口,实现run()方法。
2.       创建Thread对象,用实现Runnable接口的对象作为参数实例化该Thread对象。
3.       调用Thread的start方法。
示例CODE:
// Fig. 18.4: PrintTask.java
// PrintTask class sleeps for a random time from 0 to 5 seconds
import java.util.Random;
 
public class PrintTask implements Runnable
{
   private final int sleepTime; // random sleep time for thread
   private final String taskName; // name of task
   private final static Random generator = new Random();
   
   public PrintTask( String name )
   {
      taskName = name; // set task name
       
      // pick random sleep time between 0 and 5 seconds
      sleepTime = generator.nextInt( 5000 ); // milliseconds
   } // end PrintTask constructor
 
   // method run contains the code that a thread will execute
   public void run()
   {
      try // put thread to sleep for sleepTime amount of time
      {
         System.out.printf( "%s going to sleep for %d milliseconds.\n",
            taskName, sleepTime );
         Thread.sleep( sleepTime ); // put thread to sleep
      } // end try       
      catch ( InterruptedException exception )
      {
         System.out.printf( "%s %s\n", taskName,
            "terminated prematurely due to interruption" );
      } // end catch
       
      // print task name
      System.out.printf( "%s done sleeping\n", taskName );
   } // end method run
} // end class PrintTask
 
// Fig. 18.5: ThreadCreator.java
// Creating and starting three threads to execute Runnables.
import java.lang.Thread;
 
public class ThreadCreator
{
   public static void main( String[] args )
   {
      System.out.println( "Creating threads" );
 
      // create each thread with a new targeted runnable
      Thread thread1 = new Thread( new PrintTask( "task1" ) );
      Thread thread2 = new Thread( new PrintTask( "task2" ) );
      Thread thread3 = new Thread( new PrintTask( "task3" ) );
 
      System.out.println( "Threads created, starting tasks." );
 
      // start threads and place in runnable state
      thread1.start(); // invokes task1's run method
      thread2.start(); // invokes task2's run method
      thread3.start(); // invokes task3's run method
 
      System.out.println( "Tasks started, main ends.\n" );
   } // end main
} // end class RunnableTester  
 
 
n  Java有两种创建线程的方法,为什么推荐实现Runnable接口的方式创建线程?
n  Thread类定义了多种方法可以被派生类重载。对于所有的方法,唯一的必须被重载的是run()方法。这当然是实现Runnable接口所需的同样的方法。
n  很多Java程序员认为类仅在它们需要被加强或修改时被扩展。因此,如果你不重载Thread的其他方法,最好通过实现Runnable 接口,来创建线程。
二、使用Executor框架管理线程
n  虽然可以使用Thread类来显示的创建线程,但推荐的做法是使用Executor接口,让它来管理Runnable对象的执行。
n  通常Executor对象会创建并管理一组执行Runnable对象的线程,这组线程被称为线程池。
采用Executor的优势
n  Executor对象能够复用已有的线程,从而消除了为每个任务创建新线程的开销,
n  它能通过优化线程的数量,提高程序性能,保证处理器一直处于忙碌状,而不必创建过多的线程使程序资源耗尽。
 
步骤:
n  声明一个execute()方法,接收一个Runnable实参,
n  Executor会将传递给它的execute方法的每个Runnable对象赋予线程池中的某个可用线程。
n  如果没有可用线程,Executor会创建一个新线程,或者等待某个线程成为可用的,再将其赋予Runnable对象。
 
ExecutorService接口
n  该接口扩展了Executor接口,并声明了许多方法用于管理Executor的声明周期。
n  通过Executor类中的静态方法,可用创建实现ExecutorService接口的对象。
n  程序示例: ExecutorServiceTest
线程的同步
n  同一进程的多个线程共享同一存储空间,带来了访问冲突这个严重的问题。
n  有时两个或多个线程可能会试图同时访问一个资源,在此情况下,数据可能会变得不一致。
n  例如,一个线程可能尝试从一个文件中读取数据,而另一个线程则尝试在同一文件中修改数据,
n  某个时刻只允许一个线程排他性的访问操作共享对象的代码,当独占对象的线程在操作对象时,其他线程会一直等待,直到独占对象的那个线程完成了对对象的操作,其他对象才被允许处理该对象,
n  这种方法可以实现多个线程的协同工作,解决冲突问题。
实现线程同步
n  一种常见办法是使用java内置的监控器。
n  每个对象都具有一个监控器和一个监控锁,
n  监控器保证在任何时刻,它的对象的监控锁是由唯一一个线程持有的。
n  这样监控器和监控锁才能用于实现“互斥”。
n  Java中可用使用关键字synchronized为共享资源加锁。
n  synchronized可修饰一个代码块或一个方法,使被修饰对象在任一时刻只能有一个线程访问,从而提供了程序的同步执行功能。
 
n  两种方式实现同步:
n  使用同步方法:通过在方法声明中加入synchronized关键字来声明synchronized方法
n  public synchronized void methodA() {  }
n  当一个线程调用一个“互斥”方法时,它试图获得该方法锁。如果方法未锁定,则获得使用权,以独占方式运行方法体,运行完释放该方法的锁。如果方法被锁定,则该线程必须等待,直到方法锁被释放时。
 
n  注意:
n  对方法run( )无法加锁,不可避免冲突;
n  对构造函数不能加锁,否则出现语法错误。
n  synchronized方法虽然可以解决同步的问题,但也存在缺陷,
n  如果一个synchronized方法需要执行很长时间,将会大大影响系统的效率。
n  Java语言提供了一种解决办法,就是synchronized块。
 
1.       使用同步块
2.       可以通过synchronized关键字将一个程序块声明为synchronized块。
              synchronized(object) {  //要互斥的语句  }
n  当一个线程执行这段代码时,它获得特定对象的所有权,即拥有该对象的锁。此时,如果有第二个线程对同一个对象也要执行这段代码时,它也试图获得该对象的所有权,但因该对象已被锁定,则第二个线程必须等待,直到锁被释放为止。
n  第一个线程执行完代码段后,自动释放锁,接下去第二个线程获得锁并可运行。
 
n  注意:
n  当维护所需要的同步特性时,要尽可能的缩小同步语句占用的时间,这样就能最小化被阻塞线程的等待时间。
n  线程之间的同步是针对线程间共享可变数据的情况,对于线程间共享不可变数据时,应将数据字段声明成final的。
 
 
分享:  分享到微米  分享到新浪Qing   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值