- 921. Minimum Add to Make Parentheses Valid 使括号有效的最少添加
- 915. Partition Array into Disjoint Intervals 分割数组
- 932. Beautiful Array 漂亮数组
- 940. Distinct Subsequences II 不同的子序列 II
类似于在计算机中使用文件夹管理文件,也可以使用线程组来管理线程,在线程组中定义一组相似(相关)的线程。也可以在线程组中定义子线程组。
Thread类有几个构造方法,其中一个允许在创建线程时指定线程组,如果在创建线程时没有指定线程组,则该线程就属于父线程所在的线程组。
JVM在创建main线程时会为它指定一个线程组,因此每个Java线程都有一个线程组与之关联,可以调用线程的getThreadGroup()方法返回该线程的线程组。
代码演示:
package threadgroup;
public class Test01 {
public static void main(String[] args) {
//返回当前main线程的线程组
ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();
// System.out.println(mainGroup);
//定义线程组,如果没有指定它的父线程组,那么它就是当前这个线程组的子线程组
ThreadGroup group1 = new ThreadGroup("group1");
// System.out.println(group1);
// System.out.println(group1.getParent());
//也可以指定父线程组
ThreadGroup group2 = new ThreadGroup(mainGroup, "group2");
// System.out.println(group2.getParent());
//创建线程时指定所属线程组
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread());
}
};
//创建线程时,如果没有指定线程组,默认归属到父线程的线程组中
//我们在main线程中创建了t线程,所以t线程是main线程的子线程
//如果t线程没有指定线程组,那么它的线程组就是默认为main线程的线程组
Thread t = new Thread(r);
System.out.println(t);
//创建线程时,可以指定线程所属的线程组
Thread t2 = new Thread(group1, r, "t2");
Thread t3 = new Thread(group2, r, "t3");
System.out.println(t3);
}
}
线程组的基本操作:
activeCount():返回当前线程组及子线程组中活动线程的数量(近似值)。
activeGroupCount():返回当前线程组及其子线程组中活动线程组的数量(近似值)。
enumerate(Thread[] list):将当前线程组及其子线程组中的活动线程复制参数数组中。
enumerate(ThreadGroup[] list):将当前线程组中的活动线程组及其子线程组复制到参数数组中。
getMaxPriority():返回线程组的最大优先级,默认是10。在这个线程组中的线程的优先级不能超过这个值。
getName():返回线程组的名称
getParent():返回父线程组
interrupt():中断线程组中的所有线程
isDaemon():判断当前线程组是否为守护线程组
list():将当前线程组当中的活动线程打印出来
parentOf(ThreadGroup g):判断当前线程组是否为参数线程组的子线程组
setDaemon():设置线程组为守护线程组
复制线程组中的线程及其子线程:
enumerate(Thread[] list):将当前线程组及其子线程组中的活动线程复制参数数组中。
enumerate(Thread[] list, boolean recursive):如果第二个参数为false,则不复制它的子线程组的h活动线程。
enumerate(ThreadGroup[] list):将当前线程组中的活动线程组及其子线程组中的线程组复制到参数数组中。
enumerate(ThreadGroup[] list, boolean recurse):不复制子线程组中的线程组。
package threadgroup;
import java.util.concurrent.TimeUnit;
public class Test03 {
public static void main(String[] args) {
ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();
Runnable r = new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(1000);
} catch (InterruptedException e){
e.printStackTrace();
}
}
};
Thread m1 = new Thread(mainGroup, r);
Thread m2 = new Thread(mainGroup, r);
ThreadGroup group1 = new ThreadGroup("group1");
Thread g1 = new Thread(group1, r);
Thread g2 = new Thread(group1, r);
m1.start();
m2.start();
g1.start();
g2.start();
ThreadGroup group2 = new ThreadGroup(group1, "group2");
Thread[] list = new Thread[mainGroup.activeCount()];
ThreadGroup[] groupList = new ThreadGroup[mainGroup.activeGroupCount()];
mainGroup.enumerate(list, false);
mainGroup.enumerate(groupList, false);
System.out.println("开始打印");
System.out.println(list.length);
System.out.println(groupList.length);
for(Thread thread : list) {
System.out.println(thread);
}
for(ThreadGroup group : groupList) {
System.out.println(group);
}
}
}
线程组的批量中断:
线程组的interrupt()方法可以给线程组中的所有线程添加中断标志。
package threadgroup;
import java.util.concurrent.TimeUnit;
/**
* 线程组的批量中断
*/
public class Test04 {
public static void main(String[] args) throws InterruptedException {
Runnable r = new Runnable() {
@Override
public void run() {
while(!Thread.interrupted()) {
System.out.println(Thread.currentThread().getName() + "is running");
}
System.out.println(Thread.currentThread().getName() + "is interrupted");
}
};
ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();
Thread t1 = new Thread(mainGroup, r);
Thread t2 = new Thread(mainGroup, r);
ThreadGroup group1 = new ThreadGroup(mainGroup, "g1");
Thread t3 = new Thread(group1, r);
Thread t4 = new Thread(group1, r);
t1.start();
t2.start();
t3.start();
t4.start();
TimeUnit.SECONDS.sleep(1);
mainGroup.interrupt();
}
}
设置守护线程组:
守护线程是为其他线程服务的,当JVM中只有守护线程时,守护线程会被销毁,JVM退出。
线程组的setDaemon(true)方法可以把线程组设置为守护线程组,当守护线程组中没有活动线程时,守护线程组被销毁。
守护线程组的守护属性,与其线程的守护属性无关。
在线程的run方法中,如果有受检异常必须进行捕获处理,如果想要获得run()方法中出现的运行时异常信息,可以通过回调UncaughtExceptionHandler接口获得哪个线程出现了运行时异常。在Thread类中有关处理运行时异常的方法有:
getDefaultUncaughtExceptionHandler() 获得全局的(默认的)UncaughtExceptionHandler
getUncaughtExceptionHandler() 获得当前线程的UncaughtExceptionHandler
setDefaultUncaughtExceptionHandler() 设置全局的UncaughtExceptionHandler
setUncaughtExceptionHandler() 设置当前线程的UncaughtExceptionHandler
当线程运行过程中出现异常,JVM会调用Thread类的dispatchUncaughtException(Throwable e)方法,该方法会调用getUncaughtExceptionHandler().uncaughtException(this, e)。所以如果想要获得线程中出现的异常的信息,就需要设置线程的UncaughtExceptionHandler。
如果当前线程没有设定UncaughtExceptionHandler,则会调用线程组的,如果线程组也没有设定,则直接把异常的栈信息定向到System.err中。
package threadException;
/**
* 设置线程的UncaughtExceptionHandler回调接口
*/
public class Test01 {
public static void main(String[] args) {
//设置全局的回调接口
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
//t参数,表示发现异常的线程,e就是该线程中的异常
System.out.println(t.getName() + "发生了异常:" + e.getMessage());
}
});
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "is running");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
//受检异常必须捕获处理
e.printStackTrace();
}
System.out.println(12 / 0);
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
String txt = null;
System.out.println(txt.length());
}
});
t1.start();
t2.start();
}
}
现在很多软件,包括MySQL、Zoonkeeper、kafka等都存在Hook线程的校验机制,目的是校验进程是否已启动,防止重复启动程序。
Hook线程也称钩子线程,在JVM退出的时候会执行Hook线程。
比如为了防止重复启动线程,程序启动时会创建一个.lock文件,那么在程序结束时就需要删除这个.lock文件,我们通过注入一个Hook线程来完成删除.lock文件的操作。
关于Hook线程更多的知识可以查看这个博客:Java并发编程系列---Hook线程_逆流而上的博客-CSDN博客_hook线程
代码示例(防止程序重复启动):
package threadException;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Time;
import java.util.concurrent.TimeUnit;
/**
* Hook线程防止程序重复启动
*/
public class Test02 {
public static void main(String[] args) throws IOException, InterruptedException {
//1)注入hook线程,防止程序重复启动,JVM退出时删除.lock文件
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
System.out.println("JVM退出,会启动当前hook线程,在hook线程中删除.lock文件");
getLockFile().toFile().delete();
}
}));
if(getLockFile().toFile().exists()) {
throw new RuntimeException("程序已启动");
} else { //文件不存在,说明程序第一次启动
getLockFile().toFile().createNewFile();
System.out.println("程序第一次启动,创建lockfile");
}
for(int i = 0; i < 10; i++) {
System.out.println("程序正在运行");
TimeUnit.SECONDS.sleep(1);
}
}
private static Path getLockFile() {
return Paths.get("", "tmp.lock");
}
}
在真实生产环境中,可能需要很多线程来支撑整个应用,当线程数量非常多时,反而会耗尽CPU资源。如果不对线程进行控制与管理,反而会影响程序的性能。
线程池就是有效使用线程的一种常用方式。线程池内部可以预先创建一定数量的工作线程,客户端代码直接将任务作为一个对象提交给线程池,线程池将这些任务缓存在工作队列中,线程池中的工作线程不断地从队列中取出任务并执行。
JDK对线程池的支持:
JDK提供了一套Executor框架,可以帮助开发人员有效地使用线程池。
package threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test01 {
public static void main(String[] args) {
//创建有固定大小(5)的线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
//向线程池中提交18个任务
for(int i = 0; i < 18; i++) {
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getId() + "编号的任务正在正式,开始时间:" + System.currentTimeMillis());
}
});
}
}
}
线程池的计划任务:
package threadpool;
import java.sql.Time;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 线程池的计划任务
*/
public class Test02 {
public static void main(String[] args) {
//创建一个有调度功能的线程池
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
//延迟2秒后执行任务
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getId() + "--" + System.currentTimeMillis());
}
}, 2, TimeUnit.SECONDS);
// //以固定的频率执行任务。在3秒后执行任务,以后每隔5秒重新执行一次
// scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
// @Override
// public void run() {
// System.out.println(Thread.currentThread().getId() + "--" + System.currentTimeMillis());
// try {
// TimeUnit.SECONDS.sleep(3); //如果任务执行时间超过了时间间隔,则任务完成后立即执行下个任务。
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
// }, 3, 2, TimeUnit.SECONDS);
//上一次任务执行完后,在固定延迟后再次执行该任务。
//以下代码中,不管任务执行多长时间,总是任务完成后间隔两秒开启下个任务
scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getId() + "--" + System.currentTimeMillis());
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 3, 2, TimeUnit.SECONDS);
}
}
核心线程池的底层实现
查看Executors工具类中newCachedThreadPool()、newSingleThreadExecutor()、newFixedThreadPool()的源码:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
Executors工具类中返回线程池的方法底层都是ThreadPoolExecutor线程池的封装。
ThreadPoolExecutor()构造方法:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
参数说明:
corePoolSize:指定线程池中的核心线程数量
maximumPoolSize:指定线程池中最大线程数量
keepAliveTime:当线程池中线程的数量超过corePoolSize时,多余的空闲线程的存活时长
unit:keepAliveTime时长单位
workQueue:任务队列,把任务提交到该任务队列中等待执行
threadFactory:线程工厂,用于创建线程的
handler:拒绝策略,当任务太多来不及处理时,如何拒绝
workQueue工作队列是指提交未执行任务的队列,它是BlockingQueue接口的对象,仅用于存储Runnable任务。根据队列功能分类,在ThreadPoolExecutor构造方法中可以使用一下几种阻塞队列:
1)直接提交队列,由SynchronousQueue对象提供,该队列没有容量,来一个任务创建一个线程。如果没有空闲线程,则尝试创建新的线程。如果线程数量已达到最大值而无法创建线程时,则执行拒绝策略。
2)有界任务队列,有ArrayBlockingQueu实现,在创建该对象时,可以指定一个容量。当有任务需要执行时,如果线程数少于核心线程数,则创建新的线程,否则将任务加入等待队列。如果队列满,则在线程数少于最大线程数的情况下,创建新的线程。否则,无法加入,执行拒绝策略。
3)无界任务队列,由LinkedblockingQueue对象实现,与有界队列相比,除非系统资源耗尽,否则不存在任务入队失败的情况。线程数少于核心线程数时,先创建线程。否则加入阻塞队列。
4)优先任务队列,通过privateBlockingQueue实现的,是带有任务优先级的队列,一个特殊的无界队列。在这个队列中,优先级高的任务先执行。
newCacheThreadPool:来一个任务创建一个线程,适合时间短频率高的任务。
newFixedThreadPool:最大线程数等于核心线程数,如果没有空闲线程,任务会放到无界队列中。
newSingleThreadExecutor:核心线程数和最大线程数都是1,任何时刻只有1个线程在执行1个任务。如果没有空闲线程,任务会放到无界队列中。
拒绝策略:
1)AbortPolicy:会抛出异常
2)CallerRunsPolicy:只要线程池没关闭,会在调用者线程中运行当前被丢弃的任务
3)DiscardOldestPolicy:将任务队列中最早加入的任务丢弃,并提交新任务
4)DiscardPolicy:直接丢弃这个无法处理的任务,不会超出异常
Executors工具类提供的静态方法返回的线程池默认的拒绝策略是AbortPolicy策略。
ThreadFactory(线程工厂):
是一个接口,接口中只有一个方法,就是用来创建线程的:
public interface ThreadFactory {
Thread newThread(Runnable r);
}
可以在定义线程池的时候自定义线程工厂:
package threadpool;
import sun.nio.ch.ThreadPool;
import java.sql.Time;
import java.util.Random;
import java.util.concurrent.*;
/**
* 自定义线程工厂
*/
public class Test04 {
public static void main(String[] args) throws InterruptedException {
Runnable r = new Runnable() {
@Override
public void run() {
int num = new Random().nextInt(10);
System.out.println(Thread.currentThread().getId() + "--" + System.currentTimeMillis() + "开始睡眠");
try {
TimeUnit.SECONDS.sleep(num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//创建线程池,使用自定义线程工厂
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 0,
TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
//根据参数r接受的任务,创建一个线程
Thread thread = new Thread(r);
thread.setDaemon(true); //设置为守护线程,主线程执行完毕,线程池中的线程会自动退出
return thread;
}
});
for(int i = 0; i < 20; i++) {
threadPoolExecutor.submit(r);
}
TimeUnit.SECONDS.sleep(5);
}
}
监控线程池:
ThreadPoolExecutor提供了一组方法用于监控线程池:
intgetActiveCount() 获得线程池中当前活动线程的数量
long getCompletedTaskCount() 获得线程池完成任务的数量
intgetCorePoolSize() 线程池中核心线程的数量
intgetLargestPoolSize() 返回线程池曾经达到的线程的最大数
intgetMaximumPoolSize() 返回线程池的最大线程容量
intgetPoolSize() 返回当前线程池的大小
long getTaskCount() 返回线程池收到的任务数量
我会尽可能地解释我正在做的事情,以获得最好的可能的建议/解决方案。这一切都是在 java 中完成的。 我的客户有一个基于 SWING 的桌面应用程序,它将使用 WebStart 加载。我被指派为用户帐
看来这个page包含 Azure CLI 支持的与 Azure API 管理相关的所有功能。但它没有展示如何使用 Azure CLI 管理用户、产品、证书、订阅和 API 等实体。 Azure CLI
我设置了一个 Hadoop 1.2.x 版本,双节点集群。第一节点(NameNode、Jobtracker)和第二节点(Secondary NameNode、Datanode、TaskTracker)
对于内容驱动的网站,设计好坏的关键是关系型数据库。在这个教程中,我们已经使用了MySQL关系型数据库管理系统(RDBMS)建立了我们的数据库。对于网站的开发者来说,MySQL是一个较受欢迎的选择,这
在尝试运行MariaDB之前,首先确定其当前状态,运行或关闭。 有三个选项用于启动和停止MariaDB – 运行mysqld(MariaDB脚本)。 运行mysqld_safe启动脚本。
我在管理界面中遇到 StackedInlines 前缀的问题。我会尝试发布所有必要的代码。 models.py(简要) ##### Base classes class BaseItem(models
我是新来的。到目前为止,我一直在使用 MVC 模型并使用基本的 session 管理模型,即在 session 中存储一个 token 并检查每个请求。 我正在尝试对lift做同样的事情,但我的 se
我在 win 服务中使用 NHiberante。有时我得到 System.ObjectDisposedException: Session is closed! Object name: 'ISess
我正在尝试使用 HtmlUnit 登录 Facebook 页面并查看其 HTML 内容。我正在尝试通过 HtmlUnit 填写登录凭据,但在单击提交按钮时我没有看到正在执行的 session 。 在
我正在为一个相当大的项目开发一个带有 reactjs 的前端,该项目有两个主要接口(interface)。主站点的前端和管理员的前端。 我应该将它们开发为两个不同的项目还是 reactjs 中的一个项
短版 我有一个使用插件基础结构的应用程序。插件具有可配置的属性,可帮助它们了解如何完成工作。插件按配置文件分组以定义如何完成任务,配置文件存储在由 DataContractSerializer 序列化
如何管理 iPhone 应用程序中的用户 session ?我在应用程序的第一页上从用户那里获取了用户名和密码。用户可以随时注销。如何像其他 Web 应用程序一样在 iPhone 应用程序中存储 se
我正在使用 Azure API 管理,其中包含第三方论坛 (Discourse) 的链接。 api管理提供的默认登录系统用于注册用户。我想知道是否可以对 api 管理和论坛使用单点登录,这样用户就不必
我正在使用 Wordpress 建立一个网站,并且我想利用它的 session 。但我没有找到任何插件,甚至文档。在我开始破解之前有什么建议或引用吗? 注意:我问的是 WP 是否以及如何使用标准 PH
我已阅读《Azure in Action》一书中的以下内容:“在 Windows Azure 中,状态服务器或进程外 session 状态提供程序,不支持” 谁能告诉我为什么不支持这个。他们在书中没有
我有一个内联表单集,我想排除一些模型对象在表单集中显示。 例如。模型 B 具有模型 A 的外键,因此它是 1:n(A 对象有许多 B 对象)关系。现在在 A 管理编辑页面上,我已经获得了 B 的内联。
我正在开发一个基于 session 的项目。我在想,与银行类似,我会创建一张支票并为用户提供阻止 session 超时的能力。 我正在考虑创建一个 setInterval 来检查需要身份验证的空白页面
我正在为一位拥有 Magento 商店的客户工作。里面塞满了产品,但这些产品的名称有点乱。他并没有坚持一种命名约定,而是多年来使用了不同的约定。因此,每当他使用“管理”->“管理产品”部分中的“名称”
我使用大约十几个 XSLT 文件来提供大量输出格式。目前,用户必须知道导出的文件格式的扩展名,例如RTF、HTML、TXT。 我还想使用参数来允许更多选项。如果我可以将元数据嵌入 XSL 文件本身,那
我已阅读《Azure in Action》一书中的以下内容:“在 Windows Azure 中,状态服务器或进程外 session 状态提供程序,不支持” 谁能告诉我为什么不支持这个。他们在书中没有
我是一名优秀的程序员,十分优秀!