基本的线程机制

线程是比进程更轻量级的调度执行单位,各个线程既可以共享进程资源(内存地址、文件IO),又可以独立调度(线程是CPU调度的最小单元)。

多进程和多线程的区别:
1、本质区别在于每个进程拥有自己一整套变量,线程则是共享数据。因此共享变量使线程之间的通信比进程之间的通信更有效、更容易。
2、与进程相比较,线程更“轻量级”,创建、撤销一个线程比启动新进程的开销要小的多。

一、定义任务
1)通过实现Runnable接口并编写run()方法,使得任务可以执行我们的命令。
Runnable执行工作的独立任务,但不会返回任何值。

class LiftOff implements Runnable{
    protected int countDown = 10;
    private static int taskCount = 0;
    private final int id = taskCount++;
    @Override
    public void run() {
        while (countDown-- > 0){
            System.out.println(countDown);
            Thread.yield();
        }
    }
}

2)希望任务结束后有一个返回值,需要实现Callable接口并编写call方法。Callable是一个具有类型参数的泛型,它的类型参数表示是从方法call中返回的值,并且必须使用ExecutorService.submit()方法调用它。

class TaskWithResult implements Callable<String>{
    private int id;
    public TaskWithResult(int id){
        this.id = id;
    }   
    @Override
    public String call() throws Exception {
        return "result of TaskWithResult " + id;
    }
}
public static void main(String[] args){
    ExecutorService exec = Executors.newCachedThreadPool();
    ArrayList<Future<String>> results = new ArrayList<>();
    for (int i = 0; i < 10; i++){
        results.add(exec.submit(new TaskWithResult(i)));
    }
    try {
        for (Future<String> future: results){
            future.get();
        }
    }catch (ExecutionException e){
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

submit()方法会产生Future对象,它用Callable返回结果的特定类型进行参数化,isDone()方法用来查询Future是否已经完成,任务完成时,它具有一个结果,可以调用get()获取该结果。也可以不用isDone()检查直接调用get(),get()方法将阻塞,直至结果准备就绪。

二、线程的创建

1)Thread类来创建线程。Thread构造器只需一个Runnable对象。调用Thread对象的start()方法为该线程执行必需的初始化操作,然后调用Runnable的run()方法,以便在这个新线程中启动该任务
Thread t = new Thread(new LiftOff());
t.start();

start()调用后会快速的返回,run()方法会在新线程中执行
2)使用Executor创建线程。执行器(Executor)管理Thread对象,从而简化并发编程。Executor在客户端和任务执行之间提供了一个间接层;与客户端直接执行任务不同,中介对象将执行任务。Executor允许你管理异步任务的执行,而无需显式地管理线程的生命周期。
一般单个的Executor被用来创建和管理系统中所有的任务。shutDown()方法的调用可以防止新任务被提交给这个Executor。

FixedThreadPool可以一次性预先执行高昂的线程分配,限定线程的数量。可以节省时间,因为不用为每个线程都固定地付出创建线程的开销。
CachedThreadPool通常会创建与所需数量相同的线程,然后在它回收旧线程时停止创建新线程。因此是首选,当这种方式会引发问题时,才需要切换到FixedThreadPool.
SingleThreadExecutor创建一个线程,每个任务都会在下一个任务开始之前运行结束,所有任务都使用相同的线程

三、线程常见名词

3.1休眠 sleep()
调用sleep()将使任务终止执行给定的时间
对sleep()的调用可以抛出InterruptedException异常,异常不能夸线程传播,所以必须在本地处理所有任务内部产生的异常

try {
    TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

3.2线程优先级
线程的优先级将线程的重要性传递给调度器,尽管CPU处理线程集的顺序不确定,但是调度器将倾向于让优先级高的线程先执行。并不是优先级底的线程得不到执行,仅仅是执行的评率较低。
获取线程优先级Thread.currentThread().getPriority();
设置线程优先级Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
设置优先级实在run()方法的开头设定的,在构造器中设定不会有任何好处,因为Executor在此刻还没有执行任务
JDK有10个优先级,因为与操作系统不能很好的映射,只使用MAX_PRIORITY, MIN_PRIORITY,NORM_PRIORITY三种级别。

3.3让步 yield()
通过调用yield()可以给线程调度器暗示,让别的线程使用CPU。yield()的调用也是建议具有相同优先级的线程可以运行。对于任何重要的控制或在调整应用时,都不能依赖于yield()。

3.4后台线程 daemon
后台线程指在程序运行的时候在后台提供一种通用服务的线程,并且这种线程不属于程序中不可或缺的部分。所有非后台线程结束时,程序也就终止了,同时会杀死所有后台线程。
必须在线程启动之前调用setDaemon()方法,才能把它设置为后台线程。
Thread t = new Thread(new LiftOff());
t.setDaemon(true);
t.start();

通过定制ThreadFaactory可以定制由Executor创建的线程的属性(后台、优先级、名称)

class DaemonThreadFactory implements ThreadFactory{
    @Override
    public Thread newThread(@NonNull Runnable r) {
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t;
    }
}
ExecutorService exec = Executors.newCachedThreadPool(new DaemonThreadFactory());

可以通过isDaemon()来判断一个线程是否为后台线程。如果一个线程为后台线程那么它创建的任何线程都是后台线程。
注意:当最后一个非后台线程终止时,后台线程会“突然”终止,finally子句也不会执行

3.5加入一个线程join()
一个线程可以在其他线程之上调用join()方法,效果为等待一段时间直到第二个线程结束才继续执行。如某个线程在另一个线程t上调用t.join(),此线程将被挂起,直到目标线程t结束才恢复。
也可以在join()方法中带一个参数,如果目标线程在参数时间到期还没有结束,join()方法总能返回。

3.6捕获异常
由于线程的本质特性,不能捕获从线程中逃逸的异常。一旦异常逃逸出任务的run()方法,就会向外传播到控制台,除非采取特殊的步骤捕获异常。
Thread.UncaughtExceptionHandler允许在每个Thread对象上附着一个异常处理器

Thread t = new Thread(new LiftOff());
   t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
      @Override
       public void uncaughtException(Thread t, Throwable e) {      
         }
       });
       t.start();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值