线程的状态以及状态之间的切换
线程从创建到死亡有七个状态,分别是初始状态,准备运行,运行状态,阻塞状态,睡眠状态(超时等待状态),等待状态,死亡状态,关系如图所示:
线程的创建
实现线程的几种方式:
- 继承Thread类
- 实现Runnable接口
- 匿名内部类的方式
- 带返回值的线程
- 定时器
- 线程池实现
- Lambda表达式实现
继承Thread
// 无名 public class Demo01 extends Thread { @Override public void run() { System.out.println(getName() + "执行了。。。"); } public static void main(String[] args) { Demo01 d1 = new Demo01(); Demo01 d2 = new Demo01(); d1.start(); d2.start(); } } // 有名 public class Demo01 extends Thread { @Override public void run() { System.out.println(getName() + "执行了。。。"); } public Demo01(String name) { super(name); } public static void main(String[] args) { Demo01 d1 = new Demo01("first thread"); Demo01 d2 = new Demo01("second thread"); d1.start(); d2.start(); } }
实现Runnable接口
实现了runnable接口的类是作为一个线程任务存在的,需要将实例化后的对象传入Thread中
public class Demo02 implements Runnable { @Override public void run() { System.out.println("线程启动"); } public static void main(String[] args) { Thread t = new Thread(new Demo02()); t.start(); } }
匿名内部类实现
// 继承Thread类的方式 public class Demo03 { public static void main(String[] args) { new Thread() { @Override public void run() { System.out.println("启动线程。。。"); } }.start(); } } // 实现Runnable接口的方式 public class Demo03 { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { System.out.println("线程启动。。。"); } }).start(); } }
带返回值的线程
上面创建的线程都是不带返回值以及不能抛异常的线程,接下来实现一种带返回值的线程
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class Demo04 implements Callable<Integer> { public static void main(String[] args) throws ExecutionException, InterruptedException { Demo04 d4 = new Demo04(); FutureTask<Integer> task = new FutureTask<>(d4); Thread t = new Thread(task); t.start(); System.out.println("计算结果是:" + task.get()); } @Override public Integer call() throws Exception { System.out.println("正在进行紧张的计算。。。"); Thread.sleep(3000); return 1; } }
定时器
定时器Timer通过schedule方法主席那个定时任务,可以指定延时多久执行,每隔多久执行,甚至是指定一个日期执行;或者是指定第一次执行的日期,然后每隔多久执行等等。
import java.util.Timer; import java.util.TimerTask; public class Demo05 { public static void main(String[] args) { Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { // 定时器执行的任务 System.out.println("定时器正在执行"); } }, 0, 1000); // 延时0秒执行,每隔一秒执行一次 } }
基于线程池实现
线程池用于降低线程创建和销毁的开销,需要线程时直接从池子里面拿,不需要时也不会销毁而是放回池子里面等待下一次使用,典型的拿空间换时间。
Executors创建
线程池最顶级的接口是Executor
,Executor里面只有一个execute方法,这个方法用于执行线程任务,但是线程池在执行完所有的任务后不会停止,需要执行shutdown()方法,但是这个方法没有在Executor中声明,而是在ExecutorService类中实现的,所以使用ExecutorService方法接收线程池对象从而执行shutdown()方法关闭线程池
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Demo06 { public static void main(String[] args) { ExecutorService e = Executors.newFixedThreadPool(10); for (int i = 0; i < 100; i ++) { e.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }); } e.shutdown(); } }
上面是通过Executors静态工厂创建的线程池,Executors一共提供了五个核心方法,分别是:
- Executors.newWorkStealingPool():这个方法在JDK1.8中引入,创建持有足够线程的线程池支持给定的并行度,并通过使用多个队列减少竞争
- Executors.newCachedThreadPool():maximumPoolSize最大可以到Integer.MAX_VLUE,线程数不够会自动增加,比较弹性。存在OOM异常,能够回收不工作的线程,keepAliveTime默认60,也就是60秒回收一个空闲线程
- Executors.newScheduledThreadPool():线程数最大至Integer.MAX_VLUE,同样存在OOM风险,支持定时及周期性执行任务,优于Timer,不会回收不工作的线程
- Executors.newSingleThreadExecutor():创建一个单线程的线程池,相当于单线程串行执行所有任务,保证按任务的提交顺序依次执行
- Executors.newFixedThreadPool():输入的参数就是固定的线程数,既是核心线程数也是最大线程数,不存在空闲线程,所以keepAliveTime为0
使用ThreadPoolExecutor创建
一般不推荐使用Executors创建二十建议使用ThreadPollExecutor,因为Executors中的方法允许的请求队列长度是Integer.MAX_VALUE,可能会导致堆积大量的请求,从而导致OOM
ThreadPoolExecutor的构造方法由7个参数:
- corePoolSize:表示核心线程数,核心线程在任务执行完毕后不会销毁
- maximumPoolSize:线程池能够容纳的最大线程数,必须大于1,如果执行的任务数超过了该值,多出的任务数会被放入队列中等待执行
- keepAliveTime:表示线程池中允许线程空闲的时间,当空闲时间达到keepAliveTime时该线程就会被销毁,直到池中只有corePoolSize数量的线程为止。如果想让核心线程超时后也被回收,只需要将ThreadPoolExecutor的allowCoreThreadTimeOut变量设置为true
- TimeUnit:表示keepAliveTime的时间单位,通常时TimeUnit.SECONDS
- workQueue:缓存队列,当请求的任务数大于maxiumPoolSize后,超过的任务会进入BlockingQueue阻塞队列中
- threadFactory:用于生产一组相同任务的线程,线程的命名就是通过给这个factory增加组名前缀来实现的
- handler:执行拒绝策略的对象,当执行的任务数超过第五个参数workQueue的缓存限制时,及就可以使用该策略处理请求,比方说将请求转向某个提示页面表示当前执行任务数过多
上面的队列、线程工厂、拒绝处理服务都必须有实例对象,但是再实际编程中都是通过Executors这个线程池静态工厂提供默认的实现,Executors用法上面已经说明,那么现在使用ThreadPoolExecutor来创建一个线程池
第一步:实现队列,线程工厂,拒绝服务
// 线程工厂 class UserThreadFactory implements ThreadFactory { private final String namePrefix; private final AtomicInteger nextId = new AtomicInteger(1); UserThreadFactory(String name) { namePrefix = "该线程来自:" + name + "----这是该线程池中的第"; } /** * 该方法用于创建线程池中的线程 * @param r * @return */ @Override public Thread newThread(Runnable r) { String name = namePrefix + nextId.getAndIncrement() + "个线程"; Thread thread = new Thread(null, r, name, 0); System.out.println(thread.getName()); // 打印线程名称 return thread; } } // 拒绝服务实现 class UserRejectHandler implements RejectedExecutionHandler { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { System.out.println("任务太多啦,被拒绝了" + executor.toString()); } }
第二步:实现线程任务
class Task implements Runnable { @Override public void run() { System.out.println("执行中。。。"); } }
创建线程池
public class Demo07 { public static void main(String[] args) { // 设置了缓存队列的大小为2 ThreadPoolExecutor tpe = new ThreadPoolExecutor(1, 2, 60, TimeUnit.SECONDS, new LinkedBlockingDeque<>(2), new UserThreadFactory("线程池工厂一"), new UserRejectHandler()); for (int i = 0; i < 200; i++) { tpe.execute(new Task()); } } }
Lambda表达式实现
使用parallelStream()实现
来源:https://www.cnblogs.com/Myarticles/p/12045999.html