您的位置:首页 > 编程语言 > Java开发

Java线程创建和线程状态的转换(三)

2018-01-22 11:16 302 查看

1.线程的创建

1.继承Thread(不推荐)。
不推荐原因:
应该将并行运行的任务与运行机制解耦,如果很多任务,要为每个任务创建一个独立的线程所付出的代价太大。

start():让系统安排一个时间来调用Thread中的run()方法(随机性),也就是使线程得到运行,启动线程,具有异步执行的效果。线程的启动顺序与执行顺序无关。

2.实现Runnable。
Runnable
接口只有一个方法 run(),我们声明自己的类实现 Runnable 接口并提供这一方法,将我们的线程代码写入其中,就完成了这一部分的任务。但是 Runnable 接口并没有任何对线程的支持,我们还必须创建 。Thread 类的实例,这一点通过 Thread 类的构造函数public Thread(Runnable target);来实现。

其实用的就是一种包装器模式。

3.使用callable和Future创建线程
java异步计算-Future使用
异步计算的发起线程(控制线程):负责异步计算任务的分解和发起,把分解好的任务交给异步计算的work线程去执行,发起异步计算后,发起线程可以获得Futrue的集合,从而可以跟踪异步计算结果。

Callable 和 Future接口
Runnable封装一个异步任务,可以把它想象成为一个没有参数和返回值的异步方法。Callable是类似于Runnable的接口,但有返回值,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。

Callable和Runnable有几点不同:
 (1)Callable规定的方法是call(),而Runnable规定的方法是run().
 (2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
 (3)call()方法可抛出异常,而run()方法是不能抛出异常的。
 (4)运行Callable任务可拿到一个Future对象。

Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果,把它交给某个线程。
Future对象的所有者在计算好结果后就可以获得它了。通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}第一个get方法的调用被阻塞,直到计算完成。如果在计算完成之前,
第二个get方法的调用超时,抛出一个TimeoutException异常。
如果运行该计算的线程被中断,两个方法都将抛出InterruptedException。如果计算已经完成,那么get方法立即返回。
如果计算还在进行,isDone方法返回false;如果完成了,则返回true。
可以用cancel方法取消该计算。如果计算还没有开始,它被取消且不再开始。如果计算处于运行之中,那么如果mayInterrupt参数为true,它就被中断。
JDK内置的Future主要使用到了Callable接口和FutureTask类。
FutureTask包装器,可将callable转换成Future和runnable,它同时实现了这个的接口。它实现了RunnableFuture<V>,RunnableFuture<V> 继承了Runnable, Future源码:

FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V>

 

FutureTask包装器,可将callable转换成Future和runnable,它同时实现了这个的接口。package createthread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
* 使用callable和Future创建线程。
* 创建并启动有返回值的线程步骤如下。
* 1.创建callable接口实现类,并实现call()方法,该call()方法将做线程的执行体,且该call()方法有返回值。
* 2.创建Callable实现类的实列,使用FutureTask类来包装Callable对象,该FutureTask对象封装该Callable对象的call
* ()方法的返回值。
* 3.使用FutureTask对象作为Thread对象参数创建并启动线程。
* 4.调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
*/
public class CallableFutureTask implements Callable<String> {
// 实现call()方法,实现线程执行体
@Override
public String call() throws Exception {
String reult = null;
for (int i = 0; i < 5; i++) {
reult = "返回值:---" + i;
System.out.println(reult);
}
System.err.println(reult);
return reult;
}

public static void main(String[] args) throws InterruptedException, ExecutionException {
// 1.创建Callable实现类的实例
CallableFutureTask tCallableFutureTask = new CallableFutureTask();
// 2.使用FutureTask(包装器)类来包装Callable对象。
FutureTask task = new FutureTask<String>(tCallableFutureTask);
// 3.使用FutureTask对象作为Thread对象参数创建并启动线程
// 实际还是以Callable来创建对象。并启动线程。。
new Thread(task).start();
// 4.调用FutureTask对象的get()(会阻塞)方法来获得子线程执行结束后的返回值。
String rs = (String) task.get();
System.out.println("main最终返回值:----" + rs);
}

}

返回值:---4
返回值:---0
返回值:---1
返回值:---2
返回值:---3
返回值:---4
main最终返回值:----返回值:---4
4.通过线程池来管理线程
public class ThreadPool {
private static int POOL_NUM = 2;

public static void main(String[] agrs) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < POOL_NUM; i++) {
RunnableThread thread = new RunnableThread();
executorService.execute(thread);
}
}
}

class RunnableThread implements Runnable {
private int THREAD_NUM =2;

public void run() {
for (int i = 0; i < THREAD_NUM; i++) {
System.out.println("线程" + Thread.currentThread() + i);
}

}
}
2.线程的状态及状态间的转换


 




1、新建状态(New):使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start()这个线程。2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。3、运行状态(Running):就绪状态的线程获取了CPU资源,执行程序代码。4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:(一)等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(二)同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。(三)其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

线程的调度1、调整线程优先级:Java线程有优先级,优先级高的线程会获得较多的运行机会。Java线程的优先级用整数表示,取值范围是1~10,Thread类有以下三个静态常量:static int MAX_PRIORITY线程可以具有的最高优先级,取值为10。static int MIN_PRIORITY  线程可以具有的最低优先级,取值为1。static int NORM_PRIORITY   分配给线程的默认优先级,取值为5。 Thread类的setPriority()和getPriority()方法分别用来设置和获取线程的优先级。每个线程都有默认的优先级。主线程的默认优先级为Thread.NORM_PRIORITY。线程的优先级有继承关系,比如A线程中创建了B线程,那么B将和A具有相同的优先级。JVM提供了10个线程优先级,但与常见的操作系统都不能很好的映射。如果希望程序能移植到各个操作系统中,应该仅仅使用Thread类有以下三个静态常量作为优先级,这样能保证同样的优先级采用了同样的调度方式。 2、线程睡眠:Thread.sleep(long millis)方法,使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态,sleep()平台移植性好,不释放锁。 3、线程等待:Object类中的wait()方法,导致当前执行代码的线程等待,执行此方法之前必须获得锁,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法也是Object类中的方法,释放锁。  4、线程让步:Thread.yield() 方法,放弃当前的cpu资源,将它让给其他任务线程去占用CPU执行时间,但放弃时间,不确定,有可能刚刚放弃,又马上获得CPU时间片。  5、线程加入:join()方法,所属线程正常运行,当前线程阻塞直到所属线程执行完。当前线程再由阻塞转为就绪状态,继续执行下面的代码。join()内部使用wait进行等待。xxxthread.join();     //执行此代码的  xxxthread.join()线程等待。t.join(1000);  //等待 t 线程,等待时间是1000毫秒。 6、线程唤醒:Object类中的notify()方法,唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,此方法也在同步代码块中执行,执行notify()方法后,当前线程并不会马上释放锁,要等到执行notify()方法的线程将程序执行完。类似的方法还有一个notifyAll(),唤醒在此对象监视器上等待的所有线程。注意:Thread中suspend()和resume()两个方法在JDK1.5中已经废除,不再介绍。因为有死锁倾向。 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息