科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件Java 5.0多线程编程(1)

Java 5.0多线程编程(1)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

Java自1995年面世以来得到了广泛得一个运用,但是对多线程编程的支持Java很长时间一直停留在初级阶段。

作者:中国IT实验室 来源:中国IT实验室 2007年8月28日

关键字: 多线程 java

  • 评论
  • 分享微博
  • 分享邮件
    Java1995年面世以来得到了广泛得一个运用,但是对多线程编程的支持Java很长时间一直停留在初级阶段。在Java 5.0之前Java里的多线程编程主要是通过Thread类,Runnable接口,Object对象中的wait() notify() notifyAll()等方法和synchronized关键词来实现的。这些工具虽然能在大多数情况下解决对共享资源的管理和线程间的调度,但存在以下几个问题

1.      过于原始,拿来就能用的功能有限,即使是要实现简单的多线程功能也需要编写大量的代码。这些工具就像汇编语言一样难以学习和使用,比这更糟糕的是稍有不慎它们还可能被错误地使用,而且这样的错误很难被发现。

2.      如果使用不当,会使程序的运行效率大大降低。

3.      为了提高开发效率,简化编程,开发人员在做项目的时候往往需要写一些共享的工具来实现一些普遍适用的功能。但因为没有规范,相同的工具会被重复地开发,造成资源浪费。

4.      因为锁定的功能是通过Synchronized来实现的,这是一种块结构,只能对代码中的一段代码进行锁定,而且锁定是单一的。如以下代码所示:

synchronizedlock{

    //执行对共享资源的操作

    ……

}

     一些复杂的功能就很难被实现。比如说如果程序需要取得lock Alock B来进行操作1,然后需要取得lock C并且释放lock A来进行操作2Java 5.0之前的多线程框架就显得无能为力了。

   因为这些问题,程序员对旧的框架一直颇有微词。这种情况一直到Java 5.0才有较大的改观,一系列的多线程工具包被纳入了标准库文件。这些工具包括了一个新的多线程程序的执行框架,使编程人员可方便地协调和调度线程的运行,并且新加入了一些高性能的常用的工具,使程序更容易编写,运行效率更高。本文将分类并结合例子来介绍这些新加的多线程工具。

   在我们开始介绍Java 5.0里的新Concurrent工具前让我们先来看一下一个用旧的多线程工具编写的程序,这个程序里有一个Server线程,它需要启动两个ComponentServer线程需等到Component线程完毕后再继续。相同的功能在Synchronizer一章里用新加的工具CountDownLatch有相同的实现。两个程序,孰优孰劣,哪个程序更容易编写,哪个程序更容易理解,相信大家看过之后不难得出结论。

public class ServerThread {

      Object concLock = new Object();

      int count = 2;

public void runTwoThreads() {

      //启动两个线程去初始化组件

            new Thread(new ComponentThread1(this)).start();

            new Thread(new ComponentThread1(this)).start();

            // Wait for other thread

while(count != 0) {

                  synchronized(concLock) {

                        try {

                              concLock.wait();

                              System.out.println("Wake up.");

                        } catch (InterruptedException ie) { //处理异常}

                  }

            }

            System.out.println("Server is up.");

      }

      public void callBack() {

synchronized(concLock) {

                  count--;

                  concLock.notifyAll();

            }

      }

      public static void main(String[] args){

            ServerThread server = new ServerThread();

            server.runTwoThreads();

      }

}

 

public class ComponentThread1 implements Runnable {

      private ServerThread server;

      public ComponentThread1(ServerThread server) {

            this.server = server;

      }

public void run() {

      //做组件初始化的工作

            System.out.println("Do component initialization.");

            server.callBack();

      }

}

1:三个新加的多线程包

   Java 5.0里新加入了三个多线程包:java.util.concurrent, java.util.concurrent.atomic, java.util.concurrent.locks.

  • java.util.concurrent包含了常用的多线程工具,是新的多线程工具的主体。
  • java.util.concurrent.atomic包含了不用加锁情况下就能改变值的原子变量,比如说AtomicInteger提供了addAndGet()方法。AddGet是两个不同的操作,为了保证别的线程不干扰,以往的做法是先锁定共享的变量,然后在锁定的范围内进行两步操作。但用AtomicInteger.addAndGet()就不用担心锁定的事了,其内部实现保证了这两步操作是在原子量级发生的,不会被别的线程干扰。
  • java.util.concurrent.locks包包含锁定的工具。

2Callable Future接口

   Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。CallableRunnable有几点不同:

  • Callable规定的方法是call(),而Runnable规定的方法是run().
  • Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
  • call()方法可抛出异常,而run()方法是不能抛出异常的。
  • 运行Callable任务可拿到一个Future对象,通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。

以下是Callable的一个例子:

public class DoCallStuff implements Callable{ // *1

        private int aInt;

        public DoCallStuff(int aInt) {

                this.aInt = aInt;

        }

        public String call() throws Exception { //*2

                boolean resultOk = false;

                if(aInt == 0){

                        resultOk = true;

                }  else if(aInt == 1){

                        while(true){ //infinite loop

                                System.out.println("looping....");

                                Thread.sleep(3000);

                        }

                } else {

                        throw new Exception("Callable terminated with Exception!"); //*3

                }

                if(resultOk){

                        return "Task done.";

                } else {

                        return "Task failed";

                }

        }

}

*1: 名为DoCallStuff类实现了CallableString将是call方法的返回值类型。例子中用了String,但可以是任何Java类。

*2: call方法的返回值类型为String,这是和类的定义相对应的。并且可以抛出异常。

*3: call方法可以抛出异常,如加重的斜体字所示。

以下是调用DoCallStuff的主程序。

import java.util.concurrent.ExecutionException;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;

public class Executor {

        public static void main(String[] args){

                //*1

                DoCallStuff call1 = new DoCallStuff(0);

                DoCallStuff call2 = new DoCallStuff(1);

                DoCallStuff call3 = new DoCallStuff(2);

                //*2

                ExecutorService es = Executors.newFixedThreadPool(3);

                //*3

                Future future1 = es.submit(call1);

                Future future2 = es.submit(call2);

                Future future3 = es.submit(call3);

                try {

                        //*4

                        System.out.println(future1.get());

                         //*5

                        Thread.sleep(3000);

                        System.out.println("Thread 2 terminated? :" + future2.cancel(true));

                        //*6

                        System.out.println(future3.get());

                } catch (ExecutionException ex) {

                        ex.printStackTrace();

                } catch (InterruptedException ex) {

                        ex.printStackTrace();

                }

        }

}

 

查看本文来源
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章