Java并发之基本的线程机制

来源:互联网 发布:ubuntu samba 权限 编辑:程序博客网 时间:2024/05/22 14:38

并发

先说说Java代码的执行机制:java代码–》编译后成为java字节码–》字节码被类加载器加载到JVM–》JVM执行字节码–》最终转换成汇编指令在CPU执行

关于并发:

使用并发需要解决的问题有多个,实现并发的方式也有多种。

1. 基本的线程机制

1.1 实现线程的基本方法:

1.1.1 实现Runnable接口来定义任务

通过实现Runnable接口并编写run()方法来是实现一个线程类。

public class MyThread implements Runnable{    public void run(){    }}//使用:MyThread myThread = new MyThread();myThread.run();//开始执行线程

1.1.2 通过Thread类

将一个实现了Runnable接口的类作为参数传递给Thread构造器来实现线程:

Thread thread = new Thread(new MyThread());thread.start();//开始执行线程,start()方法会自动调用run()方法。其实start()会很快的返回,因为这个start()方法是有由main这线程执行的

1.2 关键方法解析

1.2.1 Thread.yield()方法:

这是一个静态方法,对该方法的调用表示对线程调度器的一种建议,声明:我已经执行完声明周期中最重要的部分了,此时可以把CPU切换给别的线程使用了。(简单点理解可以是降低当前线程的优先级,主动让出CPU)

1.3 使用Executor执行器(启动线程的优先选择)

Executor是在客户端和执行任务中间提供了一个间接层,帮我们管理线程对象从而简化并发编程。

我们一般使用Executor的子接口ExecutorService,ExecutorService知道如何构建恰当的上下文来执行Runnable对象。下面是一个典型的例子:

public class CachedThreadPool {  public static void main(String[] args) {    ExecutorService exec = Executors.newCachedThreadPool();    for(int i = 0; i < 5; i++)      exec.execute(new LiftOff());    exec.shutdown();  }}

这是很常见的情况,单个Executor来创建和管理所有的任务。

shutdown()方法的调用可以禁止新的任务呗提交给这个Executor,当前线程(示例中的main线程)将继续运行shutdown()调用之前所提交给Executor的所有任务,知道全部完成之后这个程序将尽快退出。

Executors创建的一些常用的线程池:

线程池名称 区别 newCachedThreadPool() 在程序执行过程中通常会创建与所需数量相同的线程,然后在它回收旧线程时停止创建新的线程。 newFixedThreadPool(int) 根据参数一次性预先执行代价昂贵的线程分配,但是也在初始时就限制了线程的数量。 newSingleThreadExecutor 创建一个单线程执行程序,如果提交了多个任务,将会把这些任务排队,每个任务都需要等待上一个任务结束才能执行。

1.4 从任务中产生返回值

Runnable是执行工作的独立任务,但是它没有返回值。如果我们希望任务在完成时能够返回一个值,那么可以通过实现Callable接口而不是Runnable接口。Callable是一种具有类型参数的泛型,它的类型是从call()(而不是run()方法)中返回的值的类型。并且使用ExecutorService.submit()方法调用它。下面是一个简单示例:

import java.util.ArrayList;import java.util.concurrent.Callable;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;/** * 从任务中产生返回值;返回值类型为Callable泛型的类型 * @author LL */class TaskWithResult implements Callable<String> {    private int id;    public TaskWithResult(int id){        this.id = id;    }    //任务    public String call() throws Exception {        return "result of TaskWithResult " + id;    }}/** * function: 从任务中产生返回值.并打印 * @author LL */public class CallableDemo {    public static void main(String[] args) {        ExecutorService exec = Executors.newCachedThreadPool();        //Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结.        //这个list用来保存每个任务的返回结果        ArrayList< Future<String> > results = new ArrayList<Future<String>>();        for (int i=0; i<10; i++){            //submit()提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。            //把返回的结果添加到List            Future<String> fs = exec.submit(new TaskWithResult(i));            results.add(fs);        }        for (Future<String> fs : results) {            try {                System.out.println(fs.get());            } catch (Exception e) {                e.printStackTrace();            } finally{                exec.shutdown();//启动一次顺序关闭,执行以前提交的任务,但不接受新任务。            }        }    }}

submit()方法会产生Future对象,它用Callable返回结果的特定类型进行了参数化。我们可以通过isDone()方法查看Future是否已经完成。当任务完成时我们可以通过调用get()方法来获取该结果。不过不用isDone()方法检查,get()方法将会阻塞知道获取到结果。

1.5 休眠

影响任务行为的一种简单方法是调用sleep()方法,这将使任务中止执行给定的时间。现在一般用TimeUnit这个类显示指定的函数代替原来的函数:当前线程休眠

//Thread.sleep(100);TimeUnit.MILLISECONDS.sleep(100);

1.6 优先级

我们一般在run()方法里面设置线程的优先级,如下所示

public void run(){    Thread.currentThread().setPriority(int)    ......}

不同的系统有不同等级的优先级,windows下一般只有7个优先级,而Solaris有2^31 个优先级,所以我们一把只设置三个优先级:Thread类下的静态常量
- static int MAX_PRIORITY 线程可以具有的最高优先级。
- static int MIN_PRIORITY 线程可以具有的最低优先级。
- static int NORM_PRIORITY 分配给线程的默认优先级。

1.7 后台线程

后台线程:指程序运行时在后台提供一种通用服务的线程,并且这种线程并不属于程序中不可或缺的部分。故当程序中所有的非后台线程结束时,程序也就终止了同时会杀死进程中所有的后台线程。

1)设置线程为后台线程的方法:

Thread t = new Thread(new LiftOff());t.setDaemon(true);//设置为后台线程

1.8 加入一个线程

一个线程A可以在另外的一个线程B之上(一般是run()方法里面)调用join方法,其结果是等待一段时间知道后一个线程结束才继续执行。比如:
1. 若在线程t上(t的run()方法中)调用线程s的s.join()方法(s一般作为t的构造器参数传入),则线程t将别挂起,直到线程s结束为止,t才可恢复继续执行。
2. 若在线程t上(t的run()方法中)调用t.join(),则当前线程会被挂起,直到线程t结束。

0 0
原创粉丝点击