`
flyPig
  • 浏览: 139591 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

jdk1.5的多线程总结一

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

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

Callable规定的方法是call(),而Runnable规定的方法是run().
Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
call()方法可抛出异常,而run()方法是不能抛出异常的。
运行Callable任务可拿到一个Future对象,通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。 
下面是一个简单的Callable例子。它实现了Callable<String>,String将是call方法的返回值类型,并且可以抛出异常。
/**
 * @author Administrator
 * 
 */
public class CallSample implements Callable<String> {

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.concurrent.Callable#call()
	 */
	private int aInt;

	public CallSample(int aInt) {
		this.aInt = aInt;
	}

	public String call() throws Exception {
		boolean resultOk = false;
		if (aInt == 0) {
			resultOk = true;
		} else if (aInt == 1) {
			while (true) { 
				System.out.println("looping....");
				Thread.sleep(3000);
			}
		} else {
			throw new Exception("Callable terminated with Exception!");
		}
		return resultOk?"Task done":"Task failed";
	}

}

定义一个测试程序来运行下。建立一个拥有固定3个线程的线程池,分别执行正常结束、无限循环、抛出异常这3种情况的Callable
public class Executor {
	public static void main(String[] args) {

		// 分别给定3种情况的Callable
		CallSample call1 = new CallSample(0);
		CallSample call2 = new CallSample(1);
		CallSample call3 = new CallSample(2);

		ExecutorService es = Executors.newFixedThreadPool(3);

		Future<String> future1 = es.submit(call1);
		Future<String> future2 = es.submit(call2);
		Future<String> future3 = es.submit(call3);
		try {
			System.out.println(future1.get());
			Thread.sleep(3000);
			System.out.println("Thread 2 terminated? :" + future2.cancel(true));
			System.out.println(future3.get());
		} catch (ExecutionException ex) {
			ex.printStackTrace();
		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}
	}
}

2.新的线程执行方式
在Java 5.0之前启动一个任务是通过调用Thread类的start()方法来实现的,任务的提交和执行是同时进行的,如果你想对任务的执行进行调度或是控制同时执行的线程数量就需要额外编写代码来完成。5.0里提供了一个新的任务执行架构使你可以轻松地调度和控制任务的执行,并且可以建立一个类似数据库连接池的线程池来执行任务。这个架构主要有三个接口和其相应的具体类组成。这三个接口是Executor, ExecutorService和ScheduledExecutorService。
Executor接口:
是用来执行Runnable任务的,它只定义一个方法:execute(Runnable command):执行Ruannable类型的任务
ExecutorService接口:
ExecutorService继承了Executor的方法,并提供了执行Callable任务和中止任务执行的服务,其定义的方法主要有:
submit(task):可用来提交Callable或Runnable任务,并返回代表此任务的Future对象
invokeAll(collection of tasks):批处理任务集合,并返回一个代表这些任务的Future对象集合
shutdown():在完成已提交的任务后关闭服务,不再接受新任务
shutdownNow():停止所有正在执行的任务并关闭服务。
isTerminated():测试是否所有任务都执行完毕了。
isShutdown():测试是否该ExecutorService已被关闭.
ScheduledExecutorService接口
在ExecutorService的基础上,ScheduledExecutorService提供了按时间安排执行任务的功能,它提供的方法主要有:
schedule(task, initDelay): 安排所提交的Callable或Runnable任务在initDelay指定的时间后执行。
scheduleAtFixedRate():安排所提交的Runnable任务按指定的间隔重复执行.
scheduleWithFixedDelay():安排所提交的Runnable任务在每次执行完后,等待delay所指定的时间后重复执行.
public class ScheduledExecutorServiceTest {
	public static void main(String[] args) throws InterruptedException,
			ExecutionException {
		// 初始化一个ScheduledExecutorService对象,这个对象的线程池大小为2
		ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
		// 定义了一个Runnable任务。
		Runnable task1 = new Runnable() {
			public void run() {
				System.out.println("Task repeating.");
			}
		};
		/*
		 * 调用所定义的ScheduledExecutorService对象来执行任务,任务每秒执行一次。能重复执行的任务一定是Runnable类型。我们可以用TimeUnit来制定时间单位。
		 */
		final ScheduledFuture future1 = service.scheduleAtFixedRate(task1, 0,
				1, TimeUnit.SECONDS);
		//调用ScheduledExecutorService对象来执行第二个任务,第二个任务所作的就是在5秒钟后取消第一个任务
		ScheduledFuture<String> future2 = service.schedule(
				new Callable<String>() {

					public String call() {
						future1.cancel(true);
						return "task cancelled!";
					}

				}, 5, TimeUnit.SECONDS);

		System.out.println(future2.get());
		service.shutdown();
	}
}


3.Executors类
虽然以上提到的接口有其实现的具体类,但为了方便Java 5.0建议使用Executors的工具类来得到Executor接口的具体对象,需要注意的是Executors是一个类,不是Executor的复数形式。
Executors提供了以下一些static的方法:
callable(Runnable task): 将Runnable的任务转化成Callable的任务。
newSingleThreadExecutor: 产生一个ExecutorService对象,这个对象只有一个线程可用来执行任务,若任务多于一个,任务将按先后顺序执行。
newCachedThreadPool(): 产生一个ExecutorService对象,这个对象带有一个线程池,线程池的大小会根据需要调整,线程执行完任务后返回线程池,供执行下一次任务使用。
newFixedThreadPool(int poolSize):产生一个ExecutorService对象,这个对象带有一个大小为poolSize的线程池,若任务数量大于poolSize,任务会被放在一个queue里顺序执行。
newSingleThreadScheduledExecutor:产生一个ScheduledExecutorService对象,这个对象的线程池大小为1,若任务多于一个,任务将按先后顺序执行。
newScheduledThreadPool(int poolSize): 产生一个ScheduledExecutorService对象,这个对象的线程池大小为poolSize,若任务数量大于poolSize,任务会在一个queue里等待执行。
//Single Threaded ExecutorService

     ExecutorService singleThreadeService = Executors.newSingleThreadExecutor();

//Cached ExecutorService

     ExecutorService cachedService = Executors.newCachedThreadPool();

//Fixed number of ExecutorService

     ExecutorService fixedService = Executors.newFixedThreadPool(3);

//Single ScheduledExecutorService

     ScheduledExecutorService singleScheduledService = 

          Executors.newSingleThreadScheduledExecutor();

//Fixed number of ScheduledExecutorService

ScheduledExecutorService fixedScheduledService = 

     Executors.newScheduledThreadPool(3);

4.Lockers和Condition
在多线程编程里面一个重要的概念是锁定,如果一个资源是多个线程共享的,为了保证数据的完整性,在进行事务性操作时需要将共享资源锁定,这样可以保证在做事务性操作时只有一个线程能对资源进行操作,从而保证数据的完整性。在5.0以前,锁定的功能是由Synchronized关键字来实现的,这样做存在2个问题:
1.每次只能对一个对象进行锁定。若需要锁定多个对象,编程就比较麻烦,一不小心就会出现死锁现象。
2.如果线程因拿不到锁定而进入等待状况,是没有办法将其打断的。
Lock接口
ReentrantLock是Lock的具体类,Lock提供了以下一些方法:
lock(): 请求锁定,如果锁已被别的线程锁定,调用此方法的线程被阻断进入等待状态。
tryLock():如果锁没被别的线程锁定,进入锁定状态,并返回true。若锁已被锁定,返回false,不进入等待状态。此方法还可带时间参数,如果锁在方法执行时已被锁定,线程将继续等待规定的时间,若还不行才返回false。
unlock():取消锁定,需要注意的是Lock不会自动取消,编程时必须手动解锁。
//生成一个锁
Lock lock = new ReentrantLock();
public void accessProtectedResource() {
  lock.lock(); //取得锁定
  try {
    //对共享资源进行操作
  } finally {
    //一定记着把锁取消掉,锁本身是不会自动解锁的
    lock.unlock();
  }
}

ReadWriteLock接口
为了提高效率有些共享资源允许同时进行多个读的操作,但只允许一个写的操作,比如一个文件,只要其内容不变可以让多个线程同时读,不必做排他的锁定,排他的锁定只有在写的时候需要,以保证别的线程不会看到数据不完整的文件。ReadWriteLock可满足这种需要。ReadWriteLock内置两个Lock,一个是读的Lock,一个是写的Lock。多个线程可同时得到读的Lock,但只有一个线程能得到写的Lock,而且写的Lock被锁定后,任何线程都不能得到Lock。ReadWriteLock提供的方法有:
readLock(): 返回一个读的lock
writeLock(): 返回一个写的lock, 此lock是排他的。
public class FileOperator{
      //初始化一个ReadWriteLock
      ReadWriteLock lock = new ReentrantReadWriteLock();
public String read() { 
      //得到readLock并锁定
            Lock readLock = lock.readLock();
            readLock.lock();
            try {
                  //做读的工作
                  return "Read something";
            } finally {
                 readLock.unlock(); 
            }
      } 

public void write(String content) {
      //得到writeLock并锁定
            Lock writeLock = lock.writeLock();
            writeLock.lock();
            try {
                  //做读的工作
            } finally {
                 writeLock.unlock(); 
            }
      } 
}

虽然ReadWriteLock提供了一个高效的锁定机理,但最终程序的运行效率是和程序的设计息息相关的,比如说如果读的线程和写的线程同时在等待,要考虑是先释放读的lock还是先释放写的lock。如果写发生的频率不高,而且快,可以考虑先给写的lock。还要考虑的问题是如果一个写正在等待读完成,此时一个新的读进来,是否要给这个新的读发锁,如果释放了,可能导致写的线程等很久。

Condition接口:
有时候线程取得lock后需要在一定条件下才能做某些工作,比如说经典的Producer和Consumer问题,Consumer必须在队列里面有数据的时候才去消费数据,否则它必须暂时放弃对队列的锁定,等到Producer往队列里放了数据后再去拿来。而Producer必须等到队列有空闲了才能往里放数据,否则它也需要暂时解锁等Consumer把数据消费一些才能往队列里放数据。在Java 5.0以前,这种功能是由Object类的wait(), notify()和notifyAll()等方法实现的,在5.0里面,这些功能集中到了Condition这个接口来实现,Condition提供以下方法:
await():使调用此方法的线程放弃锁定,进入睡眠直到被打断或被唤醒。
signal(): 唤醒一个等待的线程
signalAll():唤醒所有等待的线程
用一个简单的吃苹果例子来看看。
public class Basket {      
Lock lock = new ReentrantLock();
//产生Condition对象
     Condition produced = lock.newCondition();
     Condition consumed = lock.newCondition();
     boolean available = false;   

     public void produce() throws InterruptedException {
           lock.lock();
           try {
                 if(available){
                    consumed.await(); //放弃lock进入睡眠  
                 }
                 /*生产苹果*/
                 System.out.println("Apple produced.");
                 available = true;
                 produced.signal(); //发信号唤醒等待这个Condition的线程
           } finally {
                lock.unlock();
           }
     }

     public void consume() throws InterruptedException {
           lock.lock();
           try {
                 if(!available){
                  produced.await();//放弃lock进入睡眠  
                 }
                 /*吃苹果*/
                 System.out.println("Apple consumed.");
                 available = false;
                 consumed.signal();//发信号唤醒等待这个Condition的线程
           } finally {
                 lock.unlock();
           }
     }      
}

public class ConditionTester {

      public static void main(String[] args) throws InterruptedException{
        final Basket basket = new Basket();
          //定义一个producer
            Runnable producer = new Runnable() {
                  public void run() {
                        try {
                              basket.produce();
                        } catch (InterruptedException ex) {
                             ex.printStackTrace();
                        }
                  }
};

           //定义一个consumer
            Runnable consumer = new Runnable() {
                  public void run() {
                        try {
                              basket.consume();
                        } catch (InterruptedException ex) {
                              ex.printStackTrace();
                        }
                  }
};

          //各产生10个consumer和producer
            ExecutorService service = Executors.newCachedThreadPool();
            for(int i=0; i < 10; i++) {
                  service.submit(consumer);
            }
            Thread.sleep(2000);
            for(int i=0; i<10; i++) {
                  service.submit(producer);
            }
            service.shutdown();
      }      
}

分享到:
评论
1 楼 elephant_xiang 2015-07-15  
condition例子,搞错了吧
public void produce(){
lock.lock();
try {
if(available){
productCond.await();
}
System.out.println("产生苹果");
available = true;
consumeCond.signal();
} catch (Exception e) {
e.printStackTrace();
}finally{
lock.unlock();
}
}

相关推荐

    jxl.jar与jdk1.5和jxl.jar与jdk1.6匹配

    总结来说,jxl.jar在JDK1.5和JDK1.6环境下都可以使用,但每个新版本的JDK都可能带来性能差异和潜在的兼容性问题。因此,进行详尽的测试和评估是确保项目稳定运行的关键。同时,开发者也需要关注和考虑更新的库和技术...

    jdk1.5 线程并发与线程池的使用

    在Java编程语言中,线程并发和线程池是多任务执行的核心概念,尤其是在...熟练掌握这些工具,能帮助开发者编写出更加稳定、高效的多线程应用程序。通过深入学习和实践,可以更好地理解和利用这些特性,优化程序性能。

    官方JDK1.5版本

    1. 并发库(Concurrent Utilities):引入了丰富的并发工具类,如`java.util.concurrent`包,包括线程池、并发集合等,极大地改善了多线程编程的效率和可靠性。 2. NIO.2(New I/O):增强了Java的I/O性能,增加了...

    JDK1.5-1.6-1.7之新特性总结-1

    JDK 1.5增加了大量并发编程的工具类,如`java.util.concurrent`包下的`ExecutorService`、`Future`、`Semaphore`等,它们提供了更高效和安全的多线程处理方式。 **10. 遍历Map的键值对(For-Each Loop over Maps)*...

    jdk1.5 1.6 1.7的新特征总结

    1. 并发工具库的改进:包括`java.util.concurrent`包中更多线程安全的数据结构和并发工具,如`ConcurrentHashMap`,`Phaser`等。 2. 轻量级注解处理:允许在编译时处理注解,无需生成额外的源代码或字节码。 3. 动态...

    jdk-1_5_0_01-windows-i586-p.rar

    枚举在多线程、权限控制和设计模式等方面都有着广泛的应用,提升了代码的规范性和可维护性。 此外,JDK 1.5还推出了变量类型推断(varargs)功能,这使得方法可以接受任意数量的同一类型的参数。通过在方法声明中...

    jdk-1_5_0_22-windows-i586-p

    9. **并发编程改进**:引入了`java.util.concurrent`包,包含线程池、并发集合等工具,提高了多线程程序的编写效率和性能。 **环境变量配置** 安装JDK 1.5 Update 22后,为了能正确运行Java程序,需要设置一些系统...

    张孝祥Java多线程与并发库高级应用笔记

    张孝祥的Java多线程与并发库高级应用笔记涵盖了从传统线程技术到JDK1.5并发库的全面内容,不仅加深了对线程基本原理的认识,还介绍了现代Java并发编程的最佳实践。对于希望提升多线程编程技能的Java开发者来说,这是...

    jdk-1_5_0_22-linux-amd64.bin

    描述中提到"linux版的jdk1.5,官网下载",这确认了JDK的版本是1.5,也被称为Java 5,它是在Linux平台上官方提供的版本,并且是从官方网站下载的。Java 5是一个重要的Java版本,引入了许多新特性,对Java语言的演进...

    jcifs:JDK 1.5 的修补 jcifs - http

    这只是一个基础的示例,实际使用中,jcifs库可以处理更复杂的情况,如多线程访问、错误处理和高级权限控制。 总结来说,jcifs库为Java开发者提供了一个强大且灵活的工具,使他们能够在Java应用程序中无缝地集成CIFS...

    经典Java多线程与并发库高级应用

    总结来说,Java多线程与并发库的高级应用,既包括了传统的多线程编程技术,也包含了JDK 1.5之后引入的高级并发工具和特性。掌握这些知识点对于任何想要在Java编程领域深入发展的开发者来说,都是非常必要的。

    JDK1.6百度云下载

    JDK1.6是Java发展史上的一个重要版本,它在JDK1.5的基础上进行了大量的改进和优化,提供了更多的特性和功能。 ### 二、JDK1.6的主要特性 1. **性能优化**:JDK1.6对垃圾回收机制进行了改进,提升了程序运行时的...

    jdk_api_1_6中文版

    4. 多线程改进:JDK 1.6增强了线程管理,包括线程池的改进,使得开发者可以更有效地控制线程的生命周期和资源分配。 5. 新的Swing组件:JDK 1.6对Swing库进行了更新,增加了新的组件和布局管理器,提升了图形用户...

    jdk1.8下载附安装教程.zip

    JDK 1.8引入了一些重要的新特性,如Lambda表达式、Stream API、方法引用来简化代码,以及接口的默认方法和静态方法,增强了多线程处理的并发工具类等。这些特性极大地提高了Java开发的效率和代码的可读性。 总结来...

    JDK1.6英文帮助文档

    JDK1.6是Oracle公司发布的一个重要版本,它在JDK1.5的基础上进行了一系列的改进和增强,包括性能优化、新特性的添加以及对已有特性的完善。 ### 二、CHM格式 CHM(Compiled Help Manual)是一种常见的Windows帮助...

    jdk_1.6.0_45

    总结来说,JDK 1.6.0_45是Java SE的一个重要版本,为Windows用户提供了一套完整的开发和运行环境。尽管现在Java已经发展到更高的版本,但对于一些旧项目或者特定的环境需求,JDK 1.6.0_45仍然是一个可靠的选项。

    JAVA-基础多线程

    #### 五、JDK1.5之后的高级同步工具 1. **`ReentrantLock`**: - `ReentrantLock`是Java 1.5之后引入的一个可中断、可轮询、可定时的锁。 - 示例代码: ```java private ReentrantLock lock = new ...

    ConditionVariablesJAVA多线程[定义].pdf

    在Java中,从JDK 1.5版本开始,引入了java.util.concurrent.locks.Condition接口,它与java.util.concurrent.locks.Lock接口一起使用,提供了一种灵活的条件变量实现。 首先,我们来理解Lock接口。Lock接口提供了一...

Global site tag (gtag.js) - Google Analytics