基本的线程机制

Posted wuchao0508

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基本的线程机制相关的知识,希望对你有一定的参考价值。

定义任务

描述一个任务,使用Runnable接口,实现其run方法即可。以下为一个倒计时的任务,打印倒计时以及任务的ID。

public class LiftOff implements Runnable
    protected int countDown =10;
    private static int taskCount =0;
    private final int id = taskCount++;
    public LiftOff();
    public LiftOff(int countDown)
        this.countDown=countDown;
    
    public String status()
        return "#"+id+"("+
                (countDown>0?countDown:"LidtOff!")+").";
    
    @Override
    public void run() 
        while(countDown-->0)
            System.out.print(status());
            Thread.yield();
        
    

Thread.yield()的调用是对线程调度器的一种声明:当前线程已经完成生命周期中的最重要的部分,现在可以切换给其他任务执行一段时间了。

public static void yield()
A hint to the scheduler that the current thread is willing to yield its current use of a processor. The scheduler is free to ignore this hint.

Yield is a heuristic attempt to improve relative progression between threads that would otherwise over-utilise a CPU. Its use should be combined with detailed profiling and benchmarking to ensure that it actually has the desired effect.

It is rarely appropriate to use this method. It may be useful for debugging or testing purposes, where it may help to reproduce bugs due to race conditions. It may also be useful when designing concurrency control constructs such as the ones in the java.util.concurrent.locks package.

Thread类

将Runnable对象转变为工作任务的传统方式是把它提交给一个Thread构造器。

public class BasicThreads 

    public static void main(String[] args) 
        Thread t = new Thread(new LiftOff());
        t.start();
        System.out.println("Waiting for liftoff!");
    


输出:
Waiting for liftoff!
#0(9).#0(8).#0(7).#0(6).#0(5).#0(4).#0(3).#0(2).#0(1).#0(LidtOff!).

添加更多的线程以驱动更多的任务

public class BasicThreads 

    public static void main(String[] args) 
        for(int i=0;i<5;i++)
            new Thread(new LiftOff()).start();
        System.out.println("Waiting for liftoff!");
    


输出:
#0(9).#1(9).#1(8).#1(7).#1(6).#1(5).#1(4).#1(3).#1(2).#1(1).#1(LiftOff!).
#0(8).#0(7).#0(6).#0(5).#0(4).#0(3).#0(2).#2(9).#2(8).#2(7).#2(6).
#2(5).#2(4).#2(3).#2(2).#2(1).#2(LiftOff!).#3(9).#3(8).#3(7).#3(6).
#3(5).#3(4).#3(3).#3(2).#3(1).#3(LiftOff!).Waiting for liftoff! #0(1).#0(LiftOff!).#4(9).#4(8).#4(7).#4(6).#4(5).#4(4).#4(3).#4(2).
#4(1).#4(LiftOff!).

使用Executor

Executor在客户端和任务执行之间提供一个间接层,它允许你管理异步任务的执行,而无序显式的管理线程的生命周期。

Java通过Executors提供四种线程池,分别为:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CachedThreadPool 

    public static void main(String[] args) 
        ExecutorService exec = Executors.newCachedThreadPool();
        for(int i=0;i<5;i++)
            exec.execute(new LiftOff());
        
        System.out.println("waiting for liftoff!");
        exec.shutdown();
    


输出:
#0(9).#1(9).#2(9).#0(8).#1(8).#0(7).#1(7).#0(6).#1(6).#0(5).#1(5).#0(4).#1(4).#0(3).#1(3).#0(2).#1(2).#0(1).#1(1).#0(LiftOff!).#1(LiftOff!).
#2(8).#2(7).#2(6).#2(5).#2(4).#2(3).#2(2).
waiting for liftoff!
#4(9).#4(8).#4(7).#4(6).#4(5).#4(4).#4(3).#4(2).#4(1).#4(LiftOff!).
#2(1).#3(9).#2(LiftOff!).
#3(8).#3(7).#3(6).#3(5).#3(4).#3(3).#3(2).#3(1).#3(LiftOff!).

exec.shutdown()方法的调用可以防止新任务被提交,当前线程(即驱动main的线程)将继续运行shutdown()方法被调用之前的所有任务。

如果有大量的线程,它们运行的任务需要使用文件系统,那么可以用newSingleThreadExecutor来运行这些线程,确保任意时刻在任意线程中都只有唯一的任务在运行。

从任务中产生返回值

Runnable是执行工作的独立任务,但它不返回任何值。如果希望任务完成后返回值,那么可以实现Callable接口替代Runnable接口。该接口的方法是call(),而且必须使用ExecutorService.submit()来调用。

定义任务

import java.util.concurrent.Callable;
public class TaskWithResult implements Callable<String> 
    private int id;
    public TaskWithResult(int id)
        this.id=id;
    
    @Override
    public String call() throws Exception 
        // TODO Auto-generated method stub
        //if(id<2)Thread.sleep(2000);
        return "result of TaskWithResult is "+id;
        

使用ExecutorService.submit()启动任务

import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class CallableDemon 

    public static void main(String[] args) 
        ExecutorService exec = Executors.newCachedThreadPool();
        ArrayList<Future<String>> results = new ArrayList<Future<String>>();
        
        for(int i=0;i<5;i++)
            results.add(exec.submit(new TaskWithResult(i)));
        
        for(Future<String> fs:results)
            try
                System.out.println(fs.get());
                
            catch(InterruptedException e)
                e.printStackTrace();
            catch(ExecutionException e)
                e.printStackTrace();
            finally
                exec.shutdown();
            
        
    

输出:
result of TaskWithResult is 0
result of TaskWithResult is 1
result of TaskWithResult is 2
result of TaskWithResult is 3
result of TaskWithResult is

ExecutorService.submit()方法会返回一个Future对象。调用该对象的get()方法时,它会等待任务完成然后返回一个值,如果任务未完成则进入阻塞,所以可以通过该对象的isDone()方法进行检查任务是否完成。

睡眠

使用TimeUnit.MILLISECONDS.sleep()方法,让任务中止运行给定的时间。

优先级

Thread.current().setPriority(int priority)

让步

Thread.yield()的调用是对线程调度器的一种声明:当前线程已经完成生命周期中的最重要的部分,现在可以切换给其他任务执行一段时间了。

public static void yield()
A hint to the scheduler that the current thread is willing to yield its current use of a processor. The scheduler is free to ignore this hint.

Yield is a heuristic attempt to improve relative progression between threads that would otherwise over-utilise a CPU. Its use should be combined with detailed profiling and benchmarking to ensure that it actually has the desired effect.

It is rarely appropriate to use this method. It may be useful for debugging or testing purposes, where it may help to reproduce bugs due to race conditions. It may also be useful when designing concurrency control constructs such as the ones in the java.util.concurrent.locks package.

后台线程

又称为守护线程。当只剩下后台线程时,虚拟机将会退出。只有当非后台线程存在时,虚拟机才会继续运行。

建立一个任务

public class LiftOff implements Runnable
    @Override
    public void run() 
            try 
                while(true)
                    System.out.println("当前进程:"+Thread.currentThread().getName());
                    TimeUnit.MILLISECONDS.sleep(100);
                
             catch (InterruptedException e) e.printStackTrace();
    

建立多个线程,并设为后台线程,main()方法sleep()一段时间后结束

public class BasicThreads 

    public static void main(String[] args) throws InterruptedException 
        for(int i=0;i<5;i++)
            Thread t = new Thread(new LiftOff());
            t.setDaemon(true);
            t.start();
        
        TimeUnit.MILLISECONDS.sleep(100);
System.out.println("虚拟机即将退出!"); 输出:
当前进程:Thread-0
当前进程:Thread-1
当前进程:Thread-2
当前进程:Thread-3
当前进程:Thread-4
当前进程:Thread-0
当前进程:Thread-1
当前进程:Thread-2
当前进程:Thread-3
虚拟机即将退出!
当前进程:Thread-4

虽然在run方法里使用了while(true)无限循环,但是这些进程都被定义为后台线程,故当main()方法结束后,虚拟机将杀死这些线程并退出。记住,虚拟机杀死这些后台线程的行为是突然进行的。

加入一个线程

如果某个线程在另一个线程t上调用t.join(),此线程被挂起,直到目标线程t结束才恢复。此时t.isAlive()为false。

创建一个任务Sleeper类,继承Thread,在构造器中调用Thread的start方法,即创建后立即运行该线程。

public class Sleeper extends Thread 
    private int duration;
    public Sleeper(String name,int sleepTime)
        super(name);
        duration = sleepTime;
        start();
    
    public void run()
        try
            sleep(duration);
        catch(InterruptedException e)
            System.out.println(getName()+" is interrupted!"+"isInterruped():"+isInterrupted());
            return;
        
        System.out.println(getName()+"has awakened!");
    

创建一个Joiner类,该类对象会传入一个Sleeper类的对象,即传入一个线程。Joiner的run方法调用sleeper.join()表示等待sleeper线程完成再继续执行。

public class Joiner extends Thread 
    private Sleeper sleeper;
    public Joiner(String name ,Sleeper sleeper)
        super(name);
        this.sleeper = sleeper;
        start();
    
    
    public void run()
        try
            sleeper.join();
        catch(InterruptedException e)
            System.out.println("Interrupted!");
        
        System.out.println(getName()+" join completed!");
    

创建四个线程,其中线程dopey传入了线程sleepy, 线程doc传入了一个线程grumpy。

public class Joining 

    public static void main(String[] args) 
        Sleeper sleepy,grumpy;
        Joiner dopey,doc;
        
        sleepy = new Sleeper("Sleepy",1500);
        grumpy = new Sleeper("Grumpy",1500);
        
        dopey = new Joiner("Dopey",sleepy);
        doc = new Joiner("Doc",grumpy);
        
        grumpy.interrupt();
    


输出:
Grumpy is interrupted!isInterruped():false
Doc join completed!
Sleepyhas awakened!
Dopey join completed!

main线程在grumpy线程上调用interrupt()表明grumpy线程被中断,会被异常捕获,并清除isInterruped()的标志,因此我们看到isInterruped()打印出来是false。

线程组

不成功的尝试,建议不用。

捕获异常

由于线程的本质,我们无法捕获从线程中逃逸出来的异常。

Thread.UncaughtExceptionHandler是一个接口,它允许我们为每一个Thread附着一个异常处理器。当线程出现异常时,Thread.UncaughtExceptionHandler.uncaughtException()方法就会被调用。实现方法如下:

public class ExceptionThread implements Runnable 
    public ExceptionThread()
        System.out.println("ExceptionThread构造器");
    
    
    @Override
    public void run() 
        Thread t = Thread.currentThread();
        System.out.println("exceptionHandler:"+t.getUncaughtExceptionHandler());
        throw new RuntimeException("这是一个运行时异常");
    



//
public class MyUncaughtExceptionhandler implements Thread.UncaughtExceptionHandler

    @Override
    public void uncaughtException(Thread t, Throwable e) 
        System.out.println("线程 "+t.getName()+" 遇到一个异常: "+e.getMessage());
    



/
public class HandlerThreadFactory implements ThreadFactory 
    private AtomicInteger count = new AtomicInteger(0);
    @Override
    public Thread newThread(Runnable r) 
        int n = count.incrementAndGet();
        System.out.println("创建第"+n+"个线程!");
        Thread t = new Thread(r);
        t.setUncaughtExceptionHandler(new MyUncaughtExceptionhandler());
        return t;
    

//
public class CaptureUncaughtException 

    public static void main(String[] args) 
        ExecutorService exec = Executors.newCachedThreadPool(new HandlerThreadFactory());
        exec.execute(new ExceptionThread());

        exec.shutdown();
    


///
输出:
ExceptionThread构造器
创建第1个线程!
exceptionHandler:Thread6.MyUncaughtExceptionhandler@44039a64
线程 Thread-0 遇到一个异常: 这是一个运行时异常

在这里使用了一个新的接口ThreadFactory,它有一个方法newThread(Runnable r)可以创建新的线程。根据需要创建新线程的对象。使用线程工厂就无需再手工编写对 new Thread 的调用了,从而允许应用程序使用特殊的线程子类、属性等等。后面执行 exec.execute(new ExceptionThread()),都是通过HandlerThreadFactory对象的newThread方法创建新线程。

 

 

 

 

 

 

以上是关于基本的线程机制的主要内容,如果未能解决你的问题,请参考以下文章

js运行机制

python线程同步

Java并发编程之多线程同步

进程线程协程

java synchronized使用

java线程详解