一.多线程
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);}@Overridepublic 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;}@Overridepublic 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);//创建线程,使用创建线程的第三种方式CallableDemo2 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>() {@Overridepublic 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>() {@Overridepublic 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;}@Overridepublic 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}
}
注意: 懒汉式涉及线程安全的问题,饿汉式不涉及。