一 案例引申
编写代码同时只允许五个线程并发访问(以下文的函数为例子)
private static void method() {
System.out.println("ThreadName" + Thread.currentThread().getName() + "进来了");
Thread.sleep(2000);
System.out.println("ThreadName" + Thread.currentThread().getName() + "出去了");
}
思考:
1 若果使用 synchronized的话避免了并发安全问题,但是不满足题目的要求,因为题目要求一次让五个线程访问,使用锁的时候一次只能访问一个。
2 解决思路:使用信号量或者使用线程池
二 信号量的简单使用(了解)
package ThreadPool;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Create by SunnyDay on 2018/11/27
*/
public class ThreadPoolDemo {
//信号量 Doug Lea 大牛写的(concurrent并发包下面的东西都是他写的) 参数控制并发线程的个数
private static Semaphore semaphore = new Semaphore(5);
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
method();
} catch (InterruptedException e) {
}
}
}).start();
}
private static void method() throws InterruptedException {
// 首先获取一把锁
semaphore.acquire();
System.out.println("ThreadName" + Thread.currentThread().getName() + "进来了");
Thread.sleep(2000);
System.out.println("ThreadName" + Thread.currentThread().getName() + "出去了");
// 释放锁
semaphore.release();
}
三 使用线程池解决(了解)
package ThreadPool;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* Create by SunnyDay on 2018/11/27
* 使用线程池解决
*/
public class ThreadPool {
/**
* java 提供了线程池的创建工具类( Executors),
* 我们可以通过工具类创建不同的线程池
*/
private static Executor executor = Executors.newCachedThreadPool();//缓冲线程池
private static Executor executor2 = Executors.newFixedThreadPool(5);//固定线程池
private static Executor executor3 = Executors.newScheduledThreadPool(5);//计划任务线程池
private static Executor executor4 = Executors.newSingleThreadExecutor();//单个线程池(池中只有一个线程)
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
// 线程池的简单使用(十分简单) 调用execute方法传递参数类型为runnable类型即可
executor2.execute(new Runnable() {
@Override
public void run() {
try {
method();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
private static void method() throws InterruptedException {
System.out.println("ThreadName" + Thread.currentThread().getName() + "进来了");
Thread.sleep(2000);
System.out.println("ThreadName" + Thread.currentThread().getName() + "出去了");
}
}
四 自定义线程池
1 小发现
/**
* 查看随便几个个线程池的源码:
* public static ExecutorService newCachedThreadPool() {
* return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
* 60L, TimeUnit.SECONDS,
* new SynchronousQueue());
* }
*
* 发现都是对线程池类(ThreadPoolExecutor)进行了简单的封装(传的参数不一致)
*/
2 ThreadPoolExecutor构造( 以6个参数的为例子)
* @param corePoolSize 核心池大小 int
* @param maximumPoolSize 最大池大小 int
* @param keepAliveTime 保活时间 long(任务完成后要销毁的延时)
* @param unit 时间单位 决定参数3的单位,枚举类型的时间单位
* @param workQueue 工作队列 用于存储任务的工作队列(BlockingQueue接口类型)
* @param threadFactory 线程工厂 用于创建线程
*
* */
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
3 自定义实现案例
package ThreadPool;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Create by SunnyDay on 2018/11/27
*/
public class ThreadPoolDemo {
public static void main(String[] args) {
/**手写线程池
* @param corePoolSize 核心池大小 int
* @param maximumPoolSize 最大池大小 int
* @param keepAliveTime 保活时间 long(任务完成后要销毁的延时)
* @param unit 时间单位 决定参数3的单位,枚举类型的时间单位
* @param workQueue 工作队列 用于存储任务的工作队列(BlockingQueue接口类型)
* @param threadFactory 线程工厂 用于创建线程
*
*线程不是越多越好,google工程师推荐 线程个数=cpu核心数+1(例如四核的开5个线程最好)
* */
// 参数任务上限
LinkedBlockingQueue blockingQueue = new LinkedBlockingQueue<>(100);
ThreadFactory threadFactory = new ThreadFactory() {
// int i = 0; 用并发安全的包装类
AtomicInteger atomicInteger = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
//创建线程 吧任务传进来
Thread thread = new Thread(r);
// 给线程起个名字
thread.setName("MyThread" + atomicInteger.getAndIncrement());
return thread;
}
};
ThreadPoolExecutor pool = new ThreadPoolExecutor(10, 10, 1, TimeUnit.SECONDS, blockingQueue, threadFactory);
for (int i = 0; i < 100; i++) {
pool.execute(new Runnable() {
@Override
public void run() {
try {
method();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
/**
* 编写代码(如下文的函数)同时只允许五个线程并发访问:
*
* 1 若果使用 synchronized的话避免了并发安全问题,但是不满足题目的要求,
* 因为题目要求一次让五个线程访问,使用锁的时候一次只能访问一个。
* 2 解决思路:使用信号量或者使用线程池
*
* 3 自己手动封装线程池(明白启动策略)
*/
private static void method() throws InterruptedException {
System.out.println("ThreadName" + Thread.currentThread().getName() + "进来了");
Thread.sleep(2000);
System.out.println("ThreadName" + Thread.currentThread().getName() + "出去了");
}
}
五 线程池补充
1 启动策略草图
image.png
2 流程图(摘抄)
image.png
六 总结
通过上面的总结相信对线程池的概念,使用,启动策略,大致的有了一些了解,但是这些还是不够的,想要深入了解还需我们不懈的努力探讨!!!