线程的概念
一个程序是一个进程,比如qq和腾讯会议,当我们运行这些软件的时候,相当于在运行一个进程。
进程中包含多个进程,多个进程相互争夺CPU资源,轮流执行任务,完成一个进程的任务。
举一个例子,我们在做Swing 窗口化界面的时候,为不同的组件添加监听器的时候,其实每个监听器相当于不同的线程。
我们在写java程序的时候,也可以自己创建线程。
创建线程一共有两种方式。
一个是继承Thread的类
另外一个是实现Runable的接口,并可以实现同一资源的多个使用。
两种也有不同的启动方式:
例子:卖票的功能:
一共有5张票,需要3个卖票窗口买卖:
第一种方式
public class MyThread extends Thread{
private int tickets=5; // 一共有5张票
private String name; // 窗口即线程的名字
// 定义构造方法
public MyThread(String name){
this.name=name;
}
// 实现run方法
@Override
public void run() {
super.run();
while(tickets>0){
tickets--;
System.out.println(name+"卖了一张票,还有"+tickets+"张票");
}
}
}
主函数实现:
public class ThreadTicket {
public static void main(String[] args) {
MyThread mt1 = new MyThread("窗口1");
MyThread mt2 = new MyThread("窗口2");
MyThread mt3 = new MyThread("窗口3");
mt1.start();
mt2.start();
mt3.start();
}
}
运行结果:
窗口1卖了一张票,还有4张票
窗口1卖了一张票,还有3张票
窗口1卖了一张票,还有2张票
窗口1卖了一张票,还有1张票
窗口1卖了一张票,还有0张票
窗口3卖了一张票,还有4张票
窗口2卖了一张票,还有4张票
窗口3卖了一张票,还有3张票
窗口2卖了一张票,还有3张票
窗口2卖了一张票,还有2张票
窗口2卖了一张票,还有1张票
窗口3卖了一张票,还有2张票
窗口2卖了一张票,还有0张票
窗口3卖了一张票,还有1张票
窗口3卖了一张票,还有0张票
说明每个窗口都卖了5张票
第二种方式
public class Theadrunable implements Runnable{
private int tickets=5;
@Override
public void run() {
while (tickets>0){
tickets--;
System.out.println(Thread.currentThread().getName()+"卖了一张票,剩余票数为:"+tickets);
}
}
}
主函数调用:
public class ThreadTicket {
public static void main(String[] args) {
Theadrunable mt= new Theadrunable();
Thread th1=new Thread(mt,"窗口1");
Thread th2=new Thread(mt,"窗口2");
Thread th3=new Thread(mt,"窗口3");
th1.start();
th2.start();
th3.start();
}
}
运行结果:
窗口1卖了一张票,剩余票数为:4
窗口1卖了一张票,剩余票数为:2
窗口1卖了一张票,剩余票数为:1
窗口2卖了一张票,剩余票数为:3
窗口3卖了一张票,剩余票数为:0
说明三个窗口一共卖了五张票
第二种是可以共同处理一个资源
线程的生命周期
守护线程
java的线程分为两种
前台运行的线程和后台运行的守护线程
守护线程创建是在线程的start()方法启动前:设置
thread.setDaemon(true);
来实现该线程是守护线程
线程池的概念
线程的创建是比较消耗内存的,所以我们要事先创建若干个可执行的线程放进一个“池(容器)” 里面,需要的时候就直接从池里面取出来不需要自己创建,使用完毕也不需要销毁而是放进“池”中, 从而减少了创建和销毁对象所产生的开销。
ExecutorService:线程池接口 ExecutorService pool(池名称) = Executors.常用线程池名;
常用线程池:
- newsingleThreadExecutor :单个线程的线程池,即线程池中每次只有一个线程在工作,单线程 串行执行任务
- newfixedThreadExecutor(n):固定数量的线程池,每提交一个任务就是一个线程,直到达到线程 池的最大数量,然后在后面等待队列前面的线程执行或者销毁
- newCacheThreadExecutor:一个可缓存的线程池。当线程池超过了处理任务所需要的线程数,那 么就会回收部分闲置线程(一般是闲置60s)。当有任务来时而线程 不够时,线程池又会创建新的线程,当线程够时就调用池中线程。适 用于大量的耗时较少的线程任务。
- newScheduleThreadExecutor:一个大小无限的线程池,该线程池多用于执行延迟任务或者固定周 期的任务。
示例:
一个买票的线程:
public class TicketTask1 implements Runnable {
// 默认10张票
private int ticket = 10;
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 1; i <= 15; i++) {
if (ticket > 0) {
//获得当前执行线程名
System.out.println(Thread.currentThread().getName() + "买票,剩余" + ticket--);
} else {
System.out.println(Thread.currentThread().getName() + "票卖完了");
break;
}
}
}
}
创建线程池:
public class TicketPool {
public static void main(String[] args) {
// TODO Auto-generated method stub
/*
* 利用线程池创建线程
*/
//创建放5个线程的线程池
//ExecutorService service=Executors.newFixedThreadPool(5);
//可缓存的
ExecutorService service=Executors.newCachedThreadPool();
for(int i=1;i<=5;i++) {
//执行线程
service.execute(new TicketTask1());
}
//关闭线程池
service.shutdown();
}
}
生产消费者模型:
奶箱
当有奶的时候,拿出,没有奶的时候,放进去,保持一个稳定的状态
/**
* @author:Xiao Chenglong
* @Date:2021/5/11
* 生产消费者模型
* 奶箱
**/
public class Box {
private int milk;
// 定义一个成员变量定义奶箱的状态
private boolean state = false;
public synchronized void put(int milk){
if(state){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.milk=milk;
System.out.println("送奶工将第"+this.milk+"奶放入奶箱");
state=true;
notifyAll();
}
public synchronized void get(){
if(!state){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("用户拿到第"+this.milk+"瓶奶");
state=false;
notifyAll();
}
}
生产者
public class Producer implements Runnable{
private Box b;
// 构造方法
public Producer (Box b){
this.b = b;
}
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
b.put(i);
}
}
}
消费者
public class Customer implements Runnable{
private Box b;
public Customer(Box b) {
this.b=b;
}
@Override
public void run() {
while (true){
b.get();
}
}
}
测试类
public class BoxDemo {
public static void main(String[] args) {
// 创建奶箱对象
Box box = new Box();
// 创建生产对象
Producer p = new Producer(box);
// 创建消费对象
Customer c = new Customer(box);
// 创建线程对象
Thread t1 = new Thread(p,"生产者");
Thread t2 = new Thread(c,"消费者");
// 启动线程
t1.start();
t2.start();
}
}
总结
- 介绍了线程的简单概念
- 线程的两种创建方式以及具体实现(利用买车票的概念)
- 线程的生命周期
- 守护线程的概念以及设置
- 线程池的概念和简单应用
- 经典的生产消费者模型