一.多线程
1.线程的状态转换图
2. 线程组
2.1概述:
Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
2.2构造方法
ThreadGroup(String name) 构造一个新的线程组
2.3成员方法:
public final String getName() 返回此线程组的名称
public final ThreadGroup getThreadGroup() 返回此线程所属的线程组
演示:
/*
线程组:java可以将多个线程分装一个线程组中
ThreadGroup
*/
class Demo extends Thread {
private String name;
private ThreadGroup threadGroup;
public Demo() {
super();
}
public Demo(ThreadGroup group, String name) {
super(group, name);
}
@Override
public void run() {
for (int i = 0; i <= 200; i++) {
System.out.println(i);
}
}
}
public class ThreadGroupDemo {
public static void main(String[] args) {
//构造方法
//ThreadGroup(String name) 构造一个新的线程组。
ThreadGroup threadGroup = new ThreadGroup("A线程组");
//创建线程对象时候可以指定该线程是属于哪个线程组的
Demo d1 = new Demo(threadGroup, "线程1");
Demo d2 = new Demo(threadGroup, "线程2");
//ThreadGroup类中有一个方法public final String getName()返回此线程组的名称。
System.out.println(threadGroup.getName());
//public final ThreadGroup getThreadGroup()返回此线程所属的线程组
System.out.println(d1.getThreadGroup());
ThreadGroup threadGroup1 = new ThreadGroup("守护线程组");
Demo d3 = new Demo(threadGroup1, "线程3");
Demo d4 = new Demo(threadGroup1, "线程4");
//可以直接对线程组进行设置,线程组中的线程也一并进行设置
threadGroup1.setDaemon(true); //设置为守护线程组
}
}
3.线程池
1.概述:
线程池:java提供了一个工具类Executors帮助我们创建不同种类的线程池
2.常用的线程池:
(1)FixedThreadPool(固定大小线程池):
- 这个线程池创建固定数量的线程,一旦线程池创建完成,池中的线程数量将不再变化。
- 适用于执行长期任务,例如网络爬虫。
(2)CachedThreadPool(缓存线程池):
- 这个线程池的线程数量会根据需求动态变化,如果有可用的空闲线程,就会重用它们;如果没有可用的线程,则创建新的线程。
- 适用于执行大量的短期异步任务,例如处理I/O操作。
(3)ScheduledThreadPool(定时线程池):
- 这个线程池用于执行定时任务和周期性任务,例如执行定时的数据备份或者周期性的任务调度。
- 可以根据需要指定线程数量。
(4)SingleThreadPool(单线程池):
- 这个线程池只包含单个线程,所有任务按照顺序执行。
- 适用于需要保证任务按照提交的顺序依次执行的情况。
(5)WorkStealingPool (任务窃取线程池)
- 通常用于执行需要大量并行处理的任务
- 在工作窃取算法中,每个线程都有自己的任务队列,但是当一个线程执行完自己队列中的任务后,它会去其他线程的队列中“窃取”任务来执行,可以提高效率。
下面我们主要说第一个线程池的应用:
/*
线程池:java提供了一个工具类Executors帮助我们创建不同种类的线程池
public static ExecutorService newFixedThreadPool(int nThreads) 固定大小的线程池
线程的创建第三种方式:实现Callable接口,结合线程池创建。
面试题:
1、多线程的创建方式有几种,分别是哪几种?
2、线程池有哪些?分别什么特点。
*/
import com.shujia.day13.Demo1;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class Demo2 implements Callable {
private String name;
public Demo2(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Object call() throws Exception {
for (int i = 1; i <= 200; i++) {
System.out.println(getName()+":"+i);
}
return null;
}
}
public class ThreadPoolDemo {
public static void main(String[] args) {
//创建一个线程池
ExecutorService pool = Executors.newFixedThreadPool(2);
//创建线程,使用创建线程的第三种方式Callable
Demo2 demo2 = new Demo2("线程1");
Demo2 demo3 = new Demo2("线程2");
//将线程放入到线程组中运行
//Future<?> submit(Runnable task);
pool.submit(demo2);
pool.submit(demo3);
//关闭线程池
pool.shutdown();
}
}
注意:
submit()方法中的线程对象只能是实现Callable和Runable接口的,所以在此我们引出第三种创建线程的方式,那就是创建实现Callable接口的类并重写其中的call()方法。通常此方法创建的线程会结合线程池使用。
使用匿名内部类改写:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo {
public static void main(String[] args) {
//创建一个线程池
ExecutorService pool = Executors.newFixedThreadPool(2);
//因为是接口的实现,我们可以使用匿名内部类进行改写
pool.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
for (int i = 1; i <= 200; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
return null;
}
});
pool.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
for (int i = 1; i <= 200; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
return null;
}
});
//关闭线程池
pool.shutdown();
}
}
4.定时器
概述: 定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。在Java中,可以通过Timer和TimerTask类来实现定义调度的功能
Timer的构造方法以及成员方法:
Timer public Timer() 构造方法,创建一个定时器对象
public void schedule(TimerTask task, long delay) 定时一个延时任务 只会执行一次
public void schedule(TimerTask task,long delay,long period)
延迟执行任务之后,以后的每间隔一段时间重复执行
TimerTask(抽象类):
TimerTask public abstract void run() 该定时器要做的操作
public boolean cancel() 关闭定时器
定时任务演示:
import javafx.scene.input.DataFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
/*
在Java中,可以通过Timer和TimerTask类来实现定义调度的功能
*/
public class TimerDemo {
public static void main(String[] args) {
//Timer() 创建一个新的计时器。
Timer timer = new Timer();
//设置定时任务
//public void schedule(TimerTask task, long delay) 定时一个延时任务 只会执行一次
timer.schedule(new Demo3(timer), 5000); //毫秒级别
//public void schedule(TimerTask task,long delay,long period) 延迟执行任务之后,以后的每间隔一段时间重复执行
timer.schedule(new Demo3(timer), 5000, 3000);
}
}
class Demo3 extends TimerTask {
private Timer timer;
public Demo3(Timer timer) {
this.timer = timer;
}
@Override
public void run() {
Date date = new Date(System.currentTimeMillis());
if(new SimpleDateFormat("yyyy年MM月dd日 HH:mm :ss").format(date).equals("2024年3月14日 21:15 :30")){
timer.cancel();
System.out.println("该定时器已经关闭");
}else {
System.out.println("叮当叮当,快点开始敲代码~");
}
}
}
5. 设计模式
1.概述:
2.简单工厂模式:
概述:有一个类专门负责创建各种对象的,不需要在main方法中自己new
我们来举个列子来解释这个模式:
假如有一个Animal类,还有Cat类和Dog类都继承子Animal类,我们可以另外写一AnimalFactory类来专门来创建Cat类和Dog类的对象,而不是在main方法中去new,可以将AnimalFactory类的构造方法私有化,成员方法静态,使它变成一个工具类,这样我们在测试类要用到Cat和Dog对象的时候可以直接调用类方法去获取对象。
3.工厂方法模式
还是以上个猫狗例子举例:
我们也可以直接用CatFactory和DogFactory去创建各自的对象,而不是在AnimalFactory中去创建,在测试类中创建CatFactory和DogFactory对象对调用各自的创建方法去获取对象。
以上两种模式各自的优缺点:
如果创建动物的种类过多,简单工厂模式的AnimalFactory类就要去频繁的修改添加,工厂方法模式只需要新写一个该动物的工厂类就行,总的来说工厂方法模式脉络看起来更为清晰,但是代码量要比简单工厂模式稍多。
4.单例模式
(1)概述:
单(单一)例(实例)模式:整个java程序运行过程中,一个类的对象在内存中有且仅有一个
1. 构造方法私有化
2. 将自己类作为静态成员变量类型存在
3. 提供静态公共的成员方法获取这个对象
(2)分类:
a.懒汉式
b.饿汉式
(3)懒汉式:当调用获取对象的方法的时候,内存才会创建该类的对象
演示:
//懒汉式:
class LazyDemo {
private static LazyDemo lazydemo;
private LazyDemo() {
}
public static LazyDemo getLazydemo() {
if (lazydemo == null) {
lazydemo = new LazyDemo();
return lazydemo;
} else {
return lazydemo;
}
}
}
public class Lazy {
public static void main(String[] args) {
LazyDemo lazydemo1 = LazyDemo.getLazydemo();
LazyDemo lazydemo2 = LazyDemo.getLazydemo();
System.out.println(lazydemo1 == lazydemo2); // //true 说明对象在内存中有且仅有一个
}
}
(4)饿汉式:无论是否调用获取对象的方法,内存中始终都会有且仅有一个该类对象
演示:
//懒汉式:
class HungruDemo {
private static HungruDemo hungruDemo = new HungruDemo();
private HungruDemo() {
}
public static HungruDemo getHungruDemo() {
return hungruDemo;
}
}
public class Hungry {
public static void main(String[] args) {
HungruDemo hungruDemo1 = HungruDemo.getHungruDemo();
HungruDemo hungruDemo2 = HungruDemo.getHungruDemo();
System.out.println(hungruDemo1==hungruDemo2); //true
}
}
注意: 懒汉式涉及线程安全的问题,饿汉式不涉及。