本人小白一枚,欢迎大家一起讨论学习,如有错误,还望大家指教。
线程状态概述:
在这里我们叙述下在线程生命周期中,线程存在的几种状态。
详细可以参考下图:
我们在这里可以思考下为什么操作线程的方法要定义在Object类中呢?
因为这些方法在操作同步中的线程中,都必须要表示它们所操作线程持有的锁,只有同一个锁上的被等待想成,可以被同一个锁上的notify唤醒。不可以对不同锁中的线程进行唤醒,而锁可以是任意对象,可以被任意对象调用的方法定义在Object中。
在这里探讨下死锁情况,当同步中嵌套同步时,可能出现死锁情况,代码如下:
public class MainDemo {
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread thread1 = new Thread(ticket);
Thread thread2 = new Thread(ticket);
thread1.start();
ticket.flag = false;
thread2.start();
}
}
class Ticket implements Runnable {
private int ticket = 100;
Object lock = new Object();
boolean flag = true;
@Override
public void run() {
if (flag) {
while (true) {
synchronized (lock) {
sellTicket();
}
}
} else {
while (true) {
sellTicket();
}
}
}
private synchronized void sellTicket() {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖:" + ticket--);
}
}
}
线程等待唤醒机制:
简述: 多个线程在处理同一个资源,但是处理的动作却不相同。比如:线程A用来做包子,线程B用来吃包子,包子可以理解为同一资源,线程A与线程B处理的动作,一个是生产,一个是消费,当A线程生产好了,通知B来消费,此时线程A与线程B就存在线程通信问题了。这种机制的出现解决了当我们希望多个线程有规律的执行时,此时多线程之间就需要一些协调通信,以此达到我们多线程共同操作同一份数据的目的。
等待唤醒中的方法:
wait():
线程不再活动,不再参与调度,进入wait set中,因此不会浪费CPU资源,也不会去竞争锁了,这时的线程状态即是WAITING。它还要等待别的线程执行一个特别的动作,也即是 “通知(notify)” 在这个对象上等待的线程从wait set中释放出来,重新进入到调度队列中。
notify():
选取所通知对象的wait set中的一个线程释放;例如餐馆有空位置时,排在前面的顾客最先入座。
notifyAll():
释放所通知对象的wait set上的全部线程。
注意一: 当我们唤醒一个等待的线程,被唤醒的线程不能立即恢复执行,因为它当初中断的地方是在同步块内,而此刻它已经不持有锁,所以它需要再次竞争锁,成功后才能在当初调用wait方法之后的地方恢复执行。
注意二: wait方法与notify方法必须要由同一个锁对象调用。因为对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。
注意三: wait方法与notify方法必须要在同步代码块或者同步函数中使用,因为:必须要通过锁对象调用这2个方法。
案例:
public class MainDemo {
public static void main(String[] args) {
// 测试类
BaoZi bz = new BaoZi();
ChiHuo chiHuo = new ChiHuo(bz);
BaoZiPu baoZiPu = new BaoZiPu(bz);
chiHuo.start();
baoZiPu.start();
}
}
class BaoZi {
String pier;
String xianer;
boolean flag = false;
}
class ChiHuo extends Thread {
private BaoZi bz;
ChiHuo(BaoZi bz) {
this.bz = bz;
}
@Override
public void run() {
while (true) {
synchronized (bz) {
if (!bz.flag) {
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("吃货正在吃" + bz.pier + bz.xianer);
bz.flag = false;
bz.notify();
}
}
}
}
class BaoZiPu extends Thread {
private BaoZi bz;
BaoZiPu(BaoZi bz) {
this.bz = bz;
}
@Override
public void run() {
int count = 0;
while (true) {
synchronized (bz) {
if (bz.flag) {
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (count > 1) {
break;
}
System.out.println("包子铺开始做包子");
if (count == 0) {
bz.pier = "冰皮";
bz.xianer = "五仁";
} else {
bz.pier = "薄皮";
bz.xianer = "牛肉大葱";
}
count++;
bz.flag = true;
System.out.println("包子做好了:" + bz.pier + bz.xianer);
bz.notify();
}
}
}
}
打印结果:
包子铺开始做包子
包子做好了:冰皮五仁
吃货正在吃冰皮五仁
包子铺开始做包子
包子做好了:薄皮牛肉大葱
吃货正在吃薄皮牛肉大葱
线程池:
简述: 就是一个可以容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,从而大大减小了反复创建线程而消耗的过多资源。
使用线程池的好处:
- 降低资源消耗,减少了创建和销毁线程的次数,每个工作线都可以被重复被利用,可执行多个任务。
- 提高响应速度。当任务到达时,任务可以不需要的等待线程创建而是能立即执行。
- 提高线程的可管理性,可以根据系统的承受能力,调整线程池中工作线程的数目,防止因为消耗过多的内存,从而导致服务器死机(每个线程需要大学1MB内存)。
线程池的使用:
Java里面线程池的顶级接口是java.util.concurrent.Executor
,但是严格意义上讲 Executor 并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是java.util.concurrent.ExecutorService
。
要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优 的,因此在java.util.concurrent.Executors
线程工厂类里面提供了一些静态工厂,生成一些常用的线程池。官方建议使用Executors
工程类来创建线程池对象。
使用Executors类中创建线程池的方法:
public static ExecutorService newFixedThreadPool(int nThreads) :
返回线程池对象。(创建的是有界线程池,也就是池中的线程个数可以指定最大数量)。public Future<?> submit(Runnable task) :
获取线程池中某一个线程对象,并执行。Future接口:
用来记录线程任务执行完毕后产生的结果,线程池创建与使用。
案例:
public class MainDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
Runnable myRunnable = new MyRunnable();
executorService.submit(myRunnable);
executorService.submit(myRunnable);
//这里我们创建了一个容量为2的线程池,在这里我们调用了三次,所以我们需要等待前两个执行完,在从池中取出
executorService.submit(myRunnable);
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
打印结果
pool-1-thread-2
pool-1-thread-1
pool-1-thread-1