前言:
而今我们对“java子线程主线程”大约比较讲究,大家都想要学习一些“java子线程主线程”的相关内容。那么小编同时在网上网罗了一些有关“java子线程主线程””的相关内容,希望兄弟们能喜欢,姐妹们快快来学习一下吧!一、Java线程池使用
创建线程是一个耗资源的过程。创建大连线程会导致系统内存溢出。
所以为了更合理使用线程,通常我们使用线程池在解决
1、简单使用
jdk提供一个工具类Executors可以方便创建线程池
//核心线程0,最新线程Integer.MAX_VALUE,空闲线程存活时间60sExecutorService threadPool1=Executors.newCachedThreadPool();//核心线程10,最大线程10,空闲线程存活时间0ExecutorService threadPool2=Executors.newFixedThreadPool(10);//核心线程1,最大线程1,空闲线程存活时间0ExecutorService threadPool3=Executors.newSingleThreadExecutor();
2、自定义线程池
自定义线程池重点在于自定义以下几个参数
线程池名称初始值最大值空闲线程保留时间
1)使用jdk原生代码
ThreadFactory namedThreadFactory = new ThreadFactory() { private final AtomicInteger threadNumber = new AtomicInteger(1); @Override public Thread newThread(Runnable r) { Thread t = new Thread(Thread.currentThread().getThreadGroup(), r, "threadPoolDemo_" + threadNumber.getAndIncrement(), 0); if (t.isDaemon()) t.setDaemon(false); if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); return t; }};ExecutorService threadPool = new ThreadPoolExecutor(0, 100, 60L, TimeUnit.MILLISECONDS, new SynchronousQueue<>(), namedThreadFactory);
上面这段代码表示最大只能100个线程,超过就会拒绝。 正常情况,如果使用SynchronousQueue,那么maximumPoolSize就要设置到足够大
ThreadFactory namedThreadFactory = new ThreadFactory() { private final AtomicInteger threadNumber = new AtomicInteger(1); @Override public Thread newThread(Runnable r) { Thread t = new Thread(Thread.currentThread().getThreadGroup(), r, "threadPoolDemo_" + threadNumber.getAndIncrement(), 0); if (t.isDaemon()) t.setDaemon(false); if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); return t; }};ExecutorService threadPool = new ThreadPoolExecutor(10, 10, 60L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), namedThreadFactory);
上面这段代码表示最大只能10个线程,超过部分会放在队列中等待执行
2)使用google工具类
import com.google.common.util.concurrent.ThreadFactoryBuilder;public void initThreadPool() { ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("loopPrecreate-pool-%d").build(); ExecutorService threadPool = new ThreadPoolExecutor(5, 50, 60L, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(), namedThreadFactory);}
上面这段代码表示最大只能50个线程,超过就会拒绝
3)注意:
如果使用SynchronousQueue,那么maximumPoolSize就要设置到足够大,并发请求时maximumPoolSize会逐渐增加到maximumPoolSize,超过max会拒绝如果使用LinkedBlockingQueue,那么corePoolSize和maximumPoolSize要设置成一样,maximumPoolSize不会自动扩展
3、使用示例
并发100个线程池
int N = 100;CountDownLatch latch = new CountDownLatch(N);CountDownLatch lock = new CountDownLatch(1);for (int i = 0; i < N; i++) { Thread.sleep(100L); threadPool.submit(()->{ try { lock.await(); System.out.println("---> start " + Thread.currentThread().getName()); Thread.sleep(5000L); System.out.println("---> end " + Thread.currentThread().getName()); latch.countDown(); } catch (Exception e) { e.printStackTrace(); } });}lock.countDown();latch.await();System.out.println("--end all ---");
二、CountDownLatch 介绍
类似发令枪,CountDownLatch的构造函数可以传入一个数字,代表倒计时多少个数。
主要方法:
countDown:每调用一次countDown(),倒计时就会减少1,await:当倒计时到0时,await()方法就会停止阻塞
1、示例代码
public static void main(String[] args) throws InterruptedException { int N = 10; CountDownLatch latch = new CountDownLatch(N); CountDownLatch lock = new CountDownLatch(1); for (int i = 0; i < N; i++) { Thread.sleep(100L); new Thread(() -> { try { lock.await(); System.out.println("---> start " + Thread.currentThread().getName()); Thread.sleep(1000L); System.out.println("---> end " + Thread.currentThread().getName()); latch.countDown(); } catch (Exception e) { e.printStackTrace(); } }).start(); } lock.countDown(); latch.await(); }三、什么是Future
Future是一个未来对象,里面保存这线程处理结果,它像一个提货凭证,拿着它你可以随时去提取结果
1、什么时候使用
在两种情况下,离开Future几乎很难办。
一种情况是拆分订单,比如你的应用收到一个批量订单,此时如果要求最快的处理订单,那么需要并发处理,并发的结果如何收集,这个问题如果自己去编程将非常繁琐,此时可以使用CompletionService解决这个问题。CompletionService将Future收集到一个队列里,可以按结果处理完成的先后顺序进队。另外一种情况是,如果你需要并发去查询一些东西(比如爬虫),并发查询只要有一个结果返回,你就认为查询到了,并且结束查询,这时也需要用CompletionService和Future来解决。
使用过程既可以用CompletionService,也可以自己维护一个list将Future添加进去。
区别是:CompletionService是先完成的先返回,自己维护的list就是按顺序一个个取值
示例代码:
public class FutureDemo { private static class Task implements Callable<String> { @Override public String call() throws Exception { // 模拟真实事务的处理过程,这个过程是非常耗时的。 Thread.sleep(5000); return "call return " + Thread.currentThread().getName(); } } public static void main(String[] args) throws Exception { FutureDemo demo =new FutureDemo(); demo.testComplete();// demo.testFutrue(); } private void testFutrue() throws Exception{ List<Future<String>> futures = new ArrayList<Future<String>>(); ExecutorService executorService = Executors.newCachedThreadPool(); System.out.println("已经提交资源申请"); for (int i = 0; i < 10; i++) { futures.add(executorService.submit(new Task())); } for (Future<String> future : futures) { if (!future.isDone()) { System.out.println("资源还没有准备好"); } System.out.println(future.get()); } executorService.shutdown(); } private void testComplete() throws Exception { ExecutorService executorService = Executors.newCachedThreadPool(); CompletionService<String> ecs = new ExecutorCompletionService<>(executorService); System.out.println("已经提交资源申请"); int lenth =10; for (int i = 0; i < lenth; i++) { ecs.submit(new Task()); } //CompletionService会按处理完后顺序返回结果 List<String> res =new ArrayList<>(); for(int i = 0;i<lenth;i++ ){ Future<String> f = ecs.take(); System.out.println(f.get()); } executorService.shutdown(); }}四、Java前台线程和后台线程
1、前台线程
定义:普通线程又可以称为用户线程,只完成用户自己想要完成的任务,不提供公共服务。
通过new Thread()产生的线程默认都是前台线程。 前台线程在程序执行完成后,才会自己退出,不会随着主线程的退出而退出。假设main线程已经退出了,但是该线程还未执行完成,还会继续执行直到线程自己退出。
java线程池启动的是前台线程
2、后台线程
定义:指在程序运行的时候在后台提供一种通用服务的线程,并且这种线程并不属于程序中不可或缺的部分。
后台线程又称守护线程,在前台线程全部退出后,后台线程会随着一起退出,并且不会执行未执行的finally语句。
在线程start之前通过setDaemon(true)方法可以将当前线程设置为后台线程
Thread t1 =new Thread(()->{ try{ System.out.println(Thread.currentThread().getName()); //如果前台线程全部退出,后台线程会一起退出,不会一直休眠下去 Thread.sleep(10000); } catch (Exception e) { e.printStackTrace(); } finally { System.out.println(Thread.currentThread().getName()+"out"); }});t1.setDaemon(true);//设置为后台线程t1.start();
3、使用场景
jvm的垃圾回收器其实就是一个后台线程和主业务无关的服务线程五、信号量 Semaphore 重点语法availablePermits():判断是否有权限acquire():获得一个信号,阻塞线程,可被中断release():释放一个信号acquireUninterruptibly():获得一个信号,阻塞线程,不可被中断
代码示例
1、每次只允许2个线程执行
public class SemaphoreThread { public static void main(String[] args) { Semaphore semaphore = new Semaphore(2); // 建立一个缓存线程池 ExecutorService es = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { es.submit(new Thread(() -> { String name = Thread.currentThread().getName(); try { semaphore.acquire(); System.out.println(LocalDateTime.now().toLocalTime()+" "+name + "开始执行"); Thread.sleep(1000); semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } })); } // 关闭线程池 es.shutdown(); }}
输出:
13:42:11.763 pool-1-thread-2开始执行13:42:11.763 pool-1-thread-1开始执行13:42:12.764 pool-1-thread-3开始执行13:42:12.764 pool-1-thread-5开始执行13:42:13.764 pool-1-thread-6开始执行13:42:13.764 pool-1-thread-4开始执行13:42:14.764 pool-1-thread-9开始执行13:42:14.764 pool-1-thread-10开始执行13:42:15.765 pool-1-thread-7开始执行13:42:15.765 pool-1-thread-8开始执行
从输出日志可以看出,每秒只有2个线程在执行
2、控制两个线程执行顺序
public class SemaphoreThread { public static void main(String[] args) { // 设置公平锁 Semaphore semaphore = new Semaphore(1,true); // 建立一个缓存线程池 ExecutorService es = Executors.newCachedThreadPool(); es.submit( new Thread(() -> { String name = Thread.currentThread().getName(); for (int i = 0; i < 10; i++) { try { semaphore.acquire(); System.out.println(LocalDateTime.now().toLocalTime()+" "+name + "正在工作"); Thread.sleep(1000); semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } } })); es.submit( new Thread(() -> { String name = Thread.currentThread().getName(); for (int i = 0; i < 10; i++) { try { semaphore.acquire(); System.out.println(LocalDateTime.now().toLocalTime()+" "+name + "正在工作"); Thread.sleep(100); semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } } })); // 关闭线程池 es.shutdown(); }}
输出:
13:45:11.661 pool-1-thread-1正在工作13:45:12.662 pool-1-thread-2正在工作13:45:12.763 pool-1-thread-1正在工作13:45:13.763 pool-1-thread-2正在工作13:45:13.863 pool-1-thread-1正在工作13:45:14.864 pool-1-thread-2正在工作13:45:14.965 pool-1-thread-1正在工作13:45:15.965 pool-1-thread-2正在工作13:45:16.066 pool-1-thread-1正在工作13:45:17.067 pool-1-thread-2正在工作13:45:17.167 pool-1-thread-1正在工作13:45:18.168 pool-1-thread-2正在工作13:45:18.268 pool-1-thread-1正在工作13:45:19.268 pool-1-thread-2正在工作13:45:19.368 pool-1-thread-1正在工作13:45:20.369 pool-1-thread-2正在工作13:45:20.470 pool-1-thread-1正在工作13:45:21.470 pool-1-thread-2正在工作13:45:21.570 pool-1-thread-1正在工作13:45:22.571 pool-1-thread-2正在工作
从输出日志可以看出,两个线程交替执行,这里的关键是设置了信号量公平锁
标签: #java子线程主线程