Thread是Java中的一个类,用于表示一个线程,它实现了Runnable接口。
通过创建Thread对象,可以创建并启动一个新的线程,执行指定的代码。
public class Thread implements Runnable {private volatile String name;...public Thread(Runnable var1) {this.init((ThreadGroup)null, var1, "Thread-" + nextThreadNum(), 0L);}...
}
Runnable是一个接口,它定义了一个run()方法。通过实现Runnable接口并实现其中的run()方法,可以创建一个可执行的任务,然后可以通过将其传递给Thread类的构造函数来创建一个线程。
Runnable接口可以让一个类变成一个可执行的任务,从而实现多线程编程。
public interface Runnable {void run();
}
通常情况下,使用Runnable接口来创建线程比继承Thread类更为灵活,因为Java不支持多继承,如果一个类已经继承了其他类,就无法再继承Thread类了。
此外,使用Runnable接口可以使得代码更为清晰,因为线程的执行逻辑被定义在run()方法中,而不是在Thread类的子类中。
ThreadFactory是Java中的一个接口,用于创建新线程。
public interface ThreadFactory {Thread newThread(Runnable var1);
}
ThreadFactory接口定义了一个名为newThread的方法,需要Runnable对象作为其参数,并返回一个新的Thread对象。ThreadFactory提供了一种定制线程创建的方法,比如设置它们的名称、优先级、守护进程状态和线程组。
下面是一个如何使用ThreadFactory创建新线程:
ThreadFactory threadFactory = new ThreadFactory() {public Thread newThread(Runnable r) {Thread t = new Thread(r);t.setName("MyThread");t.setPriority(Thread.MAX_PRIORITY);t.setDaemon(true);return t;}
};ExecutorService executor = Executors.newFixedThreadPool(10, threadFactory);executor.execute(new Runnable() {public void run() {// Code to be executed in the new thread}
});
Thread的生命周期可以分为以下几个阶段:
· 新建状态(New):当创建一个Thread对象时,线程处于新建状态。此时线程尚未开始执行,也不具有执行上下文。
· 就绪状态(Runnable):当调用start()方法启动线程时,线程进入就绪状态。此时线程具有执行上下文,但还没有分配到CPU时间片。
· 运行状态(Running):当线程获得CPU时间片并开始执行时,线程进入运行状态。此时线程正在执行任务。
· 阻塞状态(Blocked):当线程因为某些原因无法继续执行时,线程进入阻塞状态。例如,线程等待IO操作完成、等待获取锁、等待其他线程执行完毕等情况都会导致线程进入阻塞状态。
· 死亡状态(Dead):当线程执行完毕或者因为异常终止时,线程进入死亡状态。此时线程不再具有执行上下文。
【1】通过Executor框架的工具类Executors来创建。可以创建多种类型的 ThreadPoolExecutor:
· FixedThreadPool:该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。
· SingleThreadExecutor:该方法返回一个只有一个线程的线程池。若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。
· CachedThreadPool:该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。
· ScheduledThreadPool:该返回一个用来在给定的延迟后运行任务或者定期执行任务的线程池。
【2】通过ThreadPoolExecutor构造函数手动定义创建。
public ThreadPoolExecutor(int corePoolSize,//线程池的核心线程数量int maximumPoolSize,//线程池的最大线程数long keepAliveTime,//当线程数大于核心线程数时,多余的空闲线程存活的最长时间TimeUnit unit,//时间单位BlockingQueue workQueue,//任务队列,用来储存等待执行任务的队列ThreadFactory threadFactory,//线程工厂,用来创建线程,一般默认即可RejectedExecutionHandler handler//拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务
)
· FixedThreadPool 和 SingleThreadExecutor : 使用的是无界的 LinkedBlockingQueue,任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。
· CachedThreadPool :使用的是同步队列 SynchronousQueue, 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致 OOM。
· ScheduledThreadPool 和 SingleThreadScheduledExecutor : 使用的无界的延迟阻塞队列DelayedWorkQueue,任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。
· 容量为 Integer.MAX_VALUE 的 LinkedBlockingQueue(无界队列):由于队列永远不会被放满,因此FixedThreadPool最多只能创建核心线程数的线程。
· SynchronousQueue(同步队列): SynchronousQueue 没有容量,不存储元素,目的是保证对于提交的任务,如果有空闲线程,则使用空闲线程来处理;否则新建一个线程来处理任务。也就是说,CachedThreadPool 的最大线程数是 Integer.MAX_VALUE ,可以理解为线程数是可以无限扩展的,可能会创建大量线程,从而导致 OOM。
· DelayedWorkQueue(延迟阻塞队列): DelayedWorkQueue 的内部元素并不是按照放入的时间排序,而是会按照延迟的时间长短对任务进行排序,内部采用的是“堆”的数据结构,可以保证每次出队的任务都是当前队列中执行时间最靠前的。DelayedWorkQueue 添加元素满了之后会自动扩容原来容量的 1/2,即永远不会阻塞,最大扩容可达 Integer.MAX_VALUE,所以最多只能创建核心线程数的线程。
1、使用同步机制:使用锁、条件变量等同步机制可以保证多个线程的执行顺序。通过这些机制,可以让线程按照一定的规则执行,从而保证线程执行的顺序。
2、使用信号量:信号量是一种计数器,用于控制对共享资源的访问。通过设置信号量的值,可以控制多个线程的执行顺序,以保证线程按照一定的顺序执行。
3、使用线程池:线程池是一种管理线程的机制,可以限制线程的数量,并对线程进行复用。通过线程池可以控制线程的执行顺序,从而保证线程按照一定的顺序执行。
4、使用同步工具:同步工具是一种通用的线程控制机制,可以控制线程的执行顺序。常见的同步工具有CountDownLatch、CyclicBarrier、Semaphore等,通过这些工具可以实现线程的同步和协调。
线程池(ThreadPoolExecutor)是Java多线程编程中的一个常用工具,它能够管理多个线程,提高线程的执行效率和资源利用率。在ThreadPoolExecutor中,submit()方法和execute()方法都可以用来向线程池提交任务,但它们之间有一些不同点。
1、返回值不同:
execute()方法没有返回值,而submit()方法返回一个Future对象,可以通过这个对象获取任务的执行结果。
2、抛出异常不同:
execute()方法在执行任务时如果抛出异常,会直接抛出到调用者的线程中,而submit()方法会将异常封装到Future对象中,调用者需要通过Future对象的get()方法获取异常信息。
3、任务类型不同:
execute()方法只能提交Runnable类型的任务,而submit()方法可以提交Runnable和Callable类型的任务。Callable是一种有返回值的任务类型,它可以返回一个泛型类型的结果。
4、使用方式不同:
execute()方法是无阻塞的,会直接将任务提交到线程池中,然后立即返回。而submit()方法是阻塞的,会等待任务执行完成后再返回。
综上所述,如果需要获取任务的执行结果或者捕获任务执行时可能抛出的异常,建议使用submit()方法;如果只是简单地提交一个任务,并不关心其执行结果和可能的异常,可以使用execute()方法。