我们需要使用线程时,我们会临时创建一个线程,然后启动,但是这样太消耗时间,所以就有了线程池的思想。
线程池:存放很多线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象,线程池可以降低资源的消耗,提高响应的速度。
java中线程池的顶层接口:
java.util.concurrent.Executor;
线程池的子接口:
java.util.concurrent.ExecutorService;
线程池有一个工具类,其作用是帮助我们创建一个线程池对象。
java.util.concurrent.Executors;
工具类中的静态方法:创建一个线程池对象
public static ExecutorService newFixedThreadPool(int nThreads);//用于创建一个具有指定线程个数的线程池对象
如何向线程池中添加任务?
调用ExecutorService接口中规定的方法
public Future<?> submit (Runnable task);//向线程池中添加无返回值的任务
public Future<?> submit (Callable<T> task);//向线程池中添加有返回值的任务,返回Future类型,表示返回了封装线程结束之后结果的对象
代码实现:
TestThreadPool.java
public class TestThreadPool {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//1、创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(1);
//2、向线程池中添加无返回值的任务
//executorService.submit(new Runnable() {
// @Override
// public void run() {
// System.out.println(Thread.currentThread().getName()+"执行了");//获得当前线程的名称【pool-1-thread-1执行了】
// }
//});
//3、向线程池中添加有返回值的任务,
Future<Integer> future = executorService.submit(new Callable<Integer>() {
@Override
public Integer call() {
int sum = 0;
for (int i = 0; i < 101; i++) {
sum += i;
}
return sum;
}
});
//从future中提取结果
Integer result = future.get();//get方法具有阻塞功能,会等任务直接完毕后再返回结果
System.out.println("结果是:" + result);//结果是:5050
//如果想要整个线程停止,则需要关闭整个线程池
executorService.shutdown();//关闭整个线程池
}
}
案例需求:
public class MyRunnable implements Runnable {
private String name;
public MyRunnable(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("我需要一个老师");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+"教完后,回办公室了");
}
}
Test.java
public class Test {
public static void main(String[] args) {
//创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
//创建两个线程
MyRunnable myRunnable1 = new MyRunnable("李老师");
MyRunnable myRunnable2 = new MyRunnable("刘老师");
//将两个线程添加到线程池中,并执行
executorService.submit(myRunnable1);
executorService.submit(myRunnable2);
//关闭整个线程池
executorService.shutdown();
}
}
在多线程中有多把锁,最后导致所有的线程都在等待,造成的现象称为死锁。
1、至少有两个线程
2、至少有两个锁对象
3、必须有synchronized嵌套
public class DeadLockTest {
public static void main(String[] args) {
//至少有两个锁对象
Object obj1 = new Object();
Object obj2 = new Object();
//至少有两个线程
new Thread(new Runnable() {
@Override
public void run() {
//必须有synchronized嵌套
synchronized (obj1) {
System.out.println("线程1抢到了obj1,还需要抢obj2");
synchronized (obj2) {
System.out.println("抢到obj2");
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj2) {
System.out.println("线程2抢到了obj2,还需要抢obj1");
synchronized (obj1) {
System.out.println("抢到obj1");
}
}
}
}).start();
}
}
如果出现了死锁,无解,我们只能事先避免死锁。
1、线程的六种状态
线程状态。 线程可以处于以下状态之一:
NEW (新建状态)
尚未启动的线程处于此状态。
RUNNABLE (可运行状态)
处于新建状态的线程调用了start方法之后,线程处于此状态。只有新建状态的线程才能调用start()
BLOCKED (受阻塞/锁阻塞状态)
被阻塞等待监视器锁定的线程处于此状态。
WAITING (无限等待)
正在等待另一个线程执行特定动作的线程处于此状态。
线程如何进入Wating?
1、该线程必须先持有锁对象
2、调用锁对象的wait方法,进入无限等待
3、进入无限等待之前,会自动释放锁对象
其他线程如何唤醒Waiting状态的过程
1、其他线程必须持有锁对象
2、调用锁对象的.nofity()方法
3、waiting的线程就会醒来,先进入blocked状态,直到再次获取到锁对象
TIMED_WAITING (限时等待/休眠)
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
TERMINATED (退出/消亡状态)
已退出的线程处于此状态。
一个线程可以在给定时间点处于一个状态。 这些状态是不反映任何操作系统线程状态的虚拟机状态。
public static void sleep(long time);//让当前线程进入睡眠状态,到毫秒后自动醒来执行
public void wait ();//让当前线程进入到等待状态,此方法必须锁对象调用
生产者消费者案例
需求两个线程:线程1包子铺线程,负责生产包子,线程2负责吃货线程,负责吃包子。
如果没有包子,那么吃货线程等待,包子铺线程执行(做包子),做完之后,唤醒吃货线程。
如果有包子,那么包子铺线程等待,吃货线程执行完(吃包子),吃完之后,唤醒包子铺线程。
代码实现:
public class TestDemo {
public static void main(String[] args) {
ArrayList<String> arr = new ArrayList<>();
arr.add("包子");
Object obj = new Object();
//包子铺线程
Thread ti = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
synchronized (obj) {
if (arr.size() > 0) {
try {
System.out.println("包子铺发现有包子,进入无限等待");
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//没有包子,做包子
System.out.println("包子做了一个包子");
arr.add("包子");
//通知吃货线程
obj.notify();
}
}
}
});
//吃货线程
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
synchronized (obj) {
if (arr.size() == 0) {
try {
System.out.println("吃货发现没有包子,进入无限等待");
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//有包子,吃包子
System.out.println("吃货吃了一个包子");
arr.remove(0);
//吃完后,通知吃货线程
obj.notify();
}
}
}
});
ti.start();
t2.start();
}
}
可以让某个线程在某个时间做指定任务,或者在某个时间以后指定的时间间隔中反复做某个任务。
构造方法:
publc Timer();//构造一个定时器
成员呢方法:
public void schedule(TimerTask task, long delay);//在指定的时间之后执行指定的任务
public void schedule(TimerTask task, long delay,long period);//在指定的时间之后开始周期性的执行指定的任务,周期的时间间隔是period
public void schedule(TimerTask task, Date time);//在指定的时间点执行指定的任务
public void schedule(TimerTask task,Date firstTime,long period);//在指定的时间点第一次执行任务,继续周期性执行任务
public class TestTimer {
public static void main(String[] args) {
//1、创建一个定时器
Timer timer = new Timer();
//任务一
//timer.schedule(new TimerTask() {
// @Override
// public void run() {
// System.out.println("任务一");
// }
//}, 2000);
//任务二
//timer.schedule(new TimerTask() {
// @Override
// public void run() {
// System.out.println("任务二");
// }
//}, 2000, 2000);
任务三
//Calendar cc = Calendar.getInstance();
//cc.add(Calendar.SECOND, 10);
//Date time = cc.getTime();
//
//timer.schedule(new TimerTask() {
// @Override
// public void run() {
// System.out.println("任务三");
// }
//}, time);
//任务四
Calendar cc = Calendar.getInstance();
cc.add(Calendar.SECOND, 10);
Date time = cc.getTime();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("任务四");
}
}, time, 1000);
}
}
关于线程池是面试中的重点,同时也是难点,感觉好抽象,容易搞混。这样的情况下就只能通过多写对应练习即可。
俗话说,熟能生巧嘛。加油!!!