面试关于多线程

1. 我们来温习下多线程的实现方式

1 继承 thread

public class myThread extends Thread{
	@override
	run{
		system.out.println("你好我是线程");
	}
}

启动线程

myThread  mt =  new myThread ();
mt.start();

2 实现接口Runnable

public class myThread implment Runnable{
	@override
	run{
		system.out.println("你好我是线程");
	}
}

启动线程

myThread  mt =  new myThread ();
Thread tt = new Thread (mt);
tt.start();

3 实现callable

public class mycall implement callable<Integer>{
	@override
	public Integer call{
		   	system.out.println("你好我是线程");
		   	retrun 0;
	}
}

启动线程

mycall  mc = new mycall();
FutureTask ft = new FutureTask(mc);
Thread thread =new Thread(ft);
thread.start();
system.out.println(ft.get());

4 使用线程池
启动方法

ExecutorService theadPool = Executor.newFixedThreadPool(4);

ArrayList<Future<Status>> arrayList=new ArrayList<Future<Status>>();

for (int i = 1; i <=4; i++) {
			mycall  mc = new mycall();
			Future<Integer> ft = 	theadPool.submit(mc);
			arrayList.add(ft);
			}
			theadPool.shutdown;
			
for (Future<Status> future2 : arrayList) {
			System.err.println(future2.get().name());
		}

2 . 你有没有使用concurrent工具类

Java 5 添加了一个新的包到 Java 平台,java.util.concurrent 包
点这里
接口Executor Runnable 是中有run方法

public interface Executor {
  void execute(Runnable command);//执行一个Runnable对象
  }

ExecutorService 是Executor接口的实现类,其子类有AbstractExecutorService,SchedualedExecutorService

Executors 是一个工具类,提供方法返回ExecutorService来创建线程池
点这里
Executors 所建立的线程池实际使用ExecutorService的子类ThreadPoolExecutor,我们来看个例子

  public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

所以Executors 只是一个帮我们填好参数的调用ThreadPoolExecutor的工具类,看到一些文章中阿里发布的java守则中建议不使用Executors来建立线程池,所以我们等下再来认真学习下ThreadPoolExecutor内的参数。

Executors 的四种线程池

 newCachedThreadPool  	  创建有缓存的线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
 newFixedThreadPool    	  创建一个定长的线程池,可控制线程最大并发数,超出的线程会在队列中等待。
 newScheduledThreadPool   创建一个定长线程池,支持定时及周期性任务执行。
 newSingleThreadExecutor  创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

例子:

ExecutorService theadpool = Exectors.newFixThreadPool(5);
Mycall mycall = new Mycall();
Future <Status> future = theadpool.submit(mycall );

线程池的5种状态:

    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;
  • RUNNING 运行
    状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。
  • SHUTDOWN 停止运行
    状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。
  • STOP
    状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
  • TIDYING
    当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。
  • TERMINATED
    线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。

关闭线程池:
shutdown() 停止加入新的线程任务
shutdownNow() 中断在执行的线程任务,清空缓存队列

线程池的工作过程如下:

  1. 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
  2. 当调用 execute() 方法添加一个任务时,线程池会做如下判断:
    1、 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
    2 、 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;
    3 、如果这个时候队列满了,而且正在运行的线程数量小于maximumPoolSize,那么创建线程运行这个任务;
    4 、如果队列满了,切正在运行的线程数量大于等于maximumPoolSize,那么就执行拒绝策略。
  3. 当一个线程完成任务时,它会从队列中取下一个任务来执行。
  4. 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

3 如何控制线程执行顺序

1 Executors.newSingleThreadExecutor
newSingleThreadExecutor产生一个单线程的线程池,而这个线程池的底层原理就是一个先进先出(FIFO)的队列。按照加入线程池的顺序执行。

ExecutorService theadpool = Exectors.newSingleThreadExecutor;
Mycall mycall = new Mycall();
Future <Status> future = theadpool.submit(mycall );

2 join
使用线程的join方法,该方法的作用是“等待线程执行结束”,即join()方法后面的代码块都要等待现场执行结束后才能执行。 实质是将主线程阻塞,等到线程执行完。

myThread my = new myThread();
Thread thread = new Thread(my);
thread.start();
thread.join;

4 ThreadPoolExecutor

点这里更好的理解线程池
ThreadPoolExecutor是ExecutorService的具体实现类,创建线程池一般使用这个类的方法,
例如:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

我们来解释下其中的参数:

  • corePoolSize:线程池的核心线程数,线程池中运行的线程数也永远不会超过 corePoolSize 个,默认情况下可以一直存活。可以通过设置allowCoreThreadTimeOut为True,此时 核心线程数就是0,此时
  • keepAliveTime控制所有线程的超时时间。
  • maximumPoolSize:线程池允许的最大线程数;
  • keepAliveTime: 指的是空闲线程结束的超时时间。当一个线程无事可做,超过keepAliveTime时,线程池会判断如果当前运行的线程数大约corePoolSize,那么这个线程就会被停掉。
  • unit :是一个枚举,表示 keepAliveTime 的单位;
  • workQueue:表示存放任务的BlockingQueue<Runnable队列。

ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;

ArrayBlockingQueue
ArrayBlockingQueue是一个用数组实现的有界阻塞队列。此队列按照先进先出(FIFO)的原则对元素进行排序。
LinkedBlockingQueue
LinkedBlockingQueue是一个用链表实现的有界阻塞队列。此队列的默认和最大长度为Integer.MAX_VALUE。此队列按照先进先出的原则对元素进行排序。
SynchronousQueue
SynchronousQueue是一个不存储元素的阻塞队列。每一个put操作必须等待一个take操作,否则不能继续添加元素。

了解了队列的基本知识,我们再回头看下四种线程池

  • newCachedThreadPool
    使用队列SynchronousQueue,多大线程数为Integer.MAX_VALUE,超时时间为60L, 时间单位为TimeUnit.SECONDS,
 public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
  • newFixedThreadPool
    使用队列LinkedBlockingQueue
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
  • newScheduledThreadPool
    使用队列DelayedWorkQueue 实现类是ScheduledThreadPoolExecutor,核心线程大小corePoolSize
public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
  • newSingleThreadExecutor
    使用LinkedBlockingQueue队列实现,最大线程数为1
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

下面只是简单的写一下,具体的事例有时间了再补上


5 线程间通讯及同步

  • synchronized
    可以锁住资源不被其他线程修改
  • wait/notify机制
    wai/notify 是object的方法

6 阻塞队列和非阻塞队列

点这里
实现队列的阻塞实质上对队列中的数据lock
而非阻塞队列没有lock
使用阻塞队列与非阻塞队列实现生产者和消费者。

7 线程池的拒绝策略

这个好
有四种拒绝策略

  • 1 DiscardPolicy
    放弃要加入队列的任务
  • 2 DiscardOldestPolicy
    放弃在队列最前端的任务
  • 3 RejectedExecutionException
    抛出异常
  • 4 CallerRunsPolicy
    直接执行execute

如何停止正在执行的线程

点这里
1 使用stop
2 使用interrupt
3 使用退出标识,使得线程正常退出

那么如何停止线程池中的线程呢
点这里
shutdown
shutdownNow

猜你喜欢

转载自blog.csdn.net/qq_31941773/article/details/83109900