初始化线程的四种方式
1、继承Thread
public static void main(String[] args) {
System.out.println("main .... statt");
new Thread01().start();
System.out.println("main ... end");
}
public static class Thread01 extends Thread{
@Override
public void run() {
System.out.println("当前线程:"+Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("运行结果:"+ i);
}
}
运行结果
2、实现Runnable接口
public static class Runable01 implements Runnable{
@Override
public void run() {
System.out.println("当前线程:"+Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("运行结果:"+ i);
}
}
public static void main(String[] args) {
System.out.println("main .... statt");
//new Thread01().start();
Runable01 runable01 = new Runable01();
new Thread(runable01).start();
System.out.println("main ... end");
}
3、实现Callable+futureTask
该方法是可以获取线程执行结果的。
public static class Callable01 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("当前线程:"+Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("运行结果:"+ i);
return i;
}
}
public static void main(String[] args) {
System.out.println("main .... start");
//new Thread01().start();
//Runable01 runable01 = new Runable01();
//new Thread(runable01).start();
Callable01 callable01 = new Callable01();
FutureTask<Integer> task = new FutureTask<Integer>(callable01);
new Thread(task).start();
System.out.println("main ... end");
}
但是,使用Callable是可以获取线程的返回结果的。
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main .... start");
//new Thread01().start();
//Runable01 runable01 = new Runable01();
//new Thread(runable01).start();
Callable01 callable01 = new Callable01();
FutureTask<Integer> task = new FutureTask<Integer>(callable01);
new Thread(task).start();
Integer integer = task.get();
System.out.println("返回结果为===="+integer);
System.out.println("main ... end");
}
执行结果:
虽然有了异步线程,但是代码还是从上往下执行,原因就是线程调用了fuutetask的get方法是阻塞式等待。
4、线程池
为什么要用线程池?
如果我们后来的业务逻辑非常多,都写我们的原生代码,new Thread().start();问题是非常大的。这就代表我们公司有新任务,就招聘一名员工。任务多了之后,最终会将资源(内存,堆,栈)耗尽。
我们以后在业务代码里边,以上三种启动线程的方式我们都不用。应该将所有的多线程异步任务,都交给线程池来执行处理。(公司就50个员工,以后什么活就分配给这50个员工中的某一个,当然如果这个人有活,就等他处理完了,再处理新任务。达到了资源控制。)
1、降低资源的消耗
通过重复利用已经创建好的线程降低线程创建和销毁带来的损耗;
2、提高响应速度
因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配的状态,当任务来的时候,无需创建新的线程就能执行,节省了线程创建的时间。
3、提高线程的可管理性
线程池会根据当前系统特点,对池内的线程进行优化处理,减少创建和销毁带来的系统开销。无限的创建和销毁不仅消耗系统资源,还降低系统的稳定性。
JUC里边有个最快的方式得到线程池,
ExecutorService pool = Executors.newFixedThreadPool(10);
当然,这个线程池应该是整个系统一个线程池,而不是每次都创建一个线程池。
线程池执行的方法调用如下:
// pool.submit(new Runable01());
pool.execute(new Runable01());
区别如下:
execute的方法类型是void。submit有返回结果。
返回结果:
线程池的创建方式
1、Executors类
2、原生线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor();
//JDK源码
public ThreadPoolExecutor(int corePoolSize, //核心线程数[一直存在,除非设置了核心线程超时销毁的属性-allowCoreThreadTimeOut]:线程池创建好以后,就准备就绪的线程数量,等待接收异步任务去执行。只是new了几个Thread,但是并没有start();
int maximumPoolSize, //线程池的最大线程数量,控制资源并发。
long keepAliveTime, //存活时间:当我们线程数量大于核心线程数量的时候,只有线程空闲大于指定的最大存活时间,非核心线程释放。
TimeUnit unit,
BlockingQueue<Runnable> workQueue,//阻塞队列,如果任务有很多,把(总的异步任务数量-最大线程数量),放入到队列里边。只要线程空闲了,就去队列里边取出新的任务,继续执行。
ThreadFactory threadFactory,//创建线程的工厂,默认的。
RejectedExecutionHandler handler) { //如果队列满了,按照指定的拒绝策略,拒绝执行我们的任务。
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
线程池的工作顺序
1、线程池创建好,准备好core数量的核心线程,准备接受任务。
2、 新的任务进来,用core准备好空闲线程执行。
(1)core满了,就将新进来的线程放入到阻塞队列中去,空闲的core就会自己去阻塞队列获取任务执行。
(2)阻塞队列满了,就直接开新线程执行,最大只能开到max的执行数量。
(3)max都执行好了,max-core数量空闲的线程会在keepAliveTime指定的时间后,自动销毁,最终保持到core大小。
(4)如果线程开到了max的数量,还有新任务进来,就会使用reject指定的拒绝策略进行处理。
1、可以丢弃
2、可以同步执行。new thread().start()是异步的。
AbortPolicy:抛弃策略;抛出异常;
DiscartPolicy:抛弃策略,不抛出异常;
CallerRunsPlicy:的run()是同步执行的,只有new Thread().start()才是异步执行的。
3、所有的线程都是由指定的factory创建的。
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
5,
200,
10,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(10000),//不指定的话,默认是Integer的最大值。一直占用内存。
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()//丢弃策略
);
线程池的种类
Executors.newCachedThreadPool();//核心线程数量是0个,但是最大线程数很大,60秒不用就回收
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
Executors.newFixedThreadPool();//最大线程数=核心线程数,核心线程从阻塞队列中取任务。阻塞队列满了不会创建非核心线程。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
Executors.newScheduledThreadPool(); //可以存放定时延期执行的任务线程
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
Executors.newSingleThreadExecutor() //核心线程=最大线程=1,线程执行完毕一个,就从阻塞队列中获取一个任务。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
总结
由此可见,方式1和方式2,都是异步的,主线程是无法获取到其他线程的结果的。
FutureTask不仅可以接收callable参数,Runable参数也是可以接收的。
JDK源码如下:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
runable方法配合futureTask实现返回值也是可以的,把返回值配置到result对象里边即可。
标签:java,System,线程,println,new,多线程,public,out From: https://blog.51cto.com/u_15890333/5883990