多线程
学习多线程之前,我们需要了解什么是线程。但是我们在学习线程之前,还得了解什么是进程,因为线程是依赖于进程存在。
1.进程
通过任务管理器我们可以观察到进程的存在,并且我们发现,只有在程序运行的时候,任务管理器中才能查看到相应的进程。
**概述:**正在运行的程序就是进程。进程是系统进行资源分配和调用的独立单位,每一个进程都有它自己的内存空间和系统资源。
多进程的意义:
单进程的计算机只能做一个工作,而我们现在用的计算机都是多进程的,也就意味着它们可以同时做许多事情。
举个栗子:
一边做笔记(Typora进程),一边直播(钉钉继承)
多进程的意义在于,计算机可以在一个时间段内同时执行多个任务。并且,可以提高CPU的使用率。
思考:
一边做笔记(Typora进程),一边直播(钉钉继承) 这两个任务是同时进行的吗?
对于多核CPU 它有可能是同时的,但是对于单核CPU来说,它在某一个时间点,它只能做一件事情。
但是我们在做笔记的时候,同时在直播,我们感官上,这两个任务是同时进行的。但是实际上不是这样的。其实CPU在运行进程的时候进行了程序间的高速切换,这个切换时间非常的短,短到我们根本感觉不来它在切换,所以我们就感觉两个进程是在同时进行的。
2.线程
在同一个进程中,可以同时执行多个任务。而这每一个任务,就是一个线程。
线程: 是程序的执行单元,也是执行路径。线程是程序使用CPU资源的最基本单位。
**单线程:**只有一个执行单元或只有一条执行路径
**多线程:**有多个执行单元或多个执行路径
多线程的意义:
1.线程的执行是抢占式的。每一个线程都要去抢占CPU资源(CPU执行权)。一个多线程的程序在执行时,如果一些线程必须等待的时候,CPU就会将资源提供给其他线程去使用这些资源。这样的话就提高了CPU的使用率。
2.对于进程来说,如果它是多线程的,在抢占CPU资源时,就有更大的几率抢占到CPU资源。提高该程序使用率。
3.在抢占CPU资源的时候,是具有随机性,我们不能保证某一个线程在某一个时间段内能够抢到资源。所以说线程的执行是有随机性。
3.并行、并发
**并行:**多个处理器或多核处理器同时处理多个不同的任务。
**并发:**一个处理器同时处理多个任务。
4.多线程
java程序运行原理
java程序运行时,通过java命令,启动一个虚拟机(jvm)(jvm启动时多线程的,因为此时它不仅启动了一个主线程,还启动了一个垃圾回收线程,所以它至少启动了两个线程),相当于启动了一个应用程序,也就是启动了一个进程,该进程会自动启动一个主线程,然后主线程去调用某个类的main方法,所以main是运行再主线程中的。
由于线程是依赖于进程存在的,所以我们需要先创建一个进程。而进程是由系统来创建的,所以我们需要调用系统的某些功能来创建进程。但是我们的Java无法去调用系统功能。所以我们无法直接实现多线程程序。
但是,C/C++是可以调用系统功能的,我们可以把C/C++调用系统功能的代码进行封装,封装到一个类中,我们通过调用这个类,就可以间接的调用系统功能来创建进程,那封装的这个类是哪个类里呢?
这个类叫做Thread
创建一个线程有两种方式
方式1:
1.自定义类,继承Thread类
2.重写run()方法
3.创建线程对象
4.启动线程
问题1:为什么重写run()方法
因为线程类中的方法不一定都要被执行,可能只执行其中的一部分。所以我们把需要线程执行的代码写到run方法中
问题2:run()和 start()方法的区别
run:
run的调用就相当于方法的普通调用
**start:**1.启动线程
2.Jvm调用相应的run方法
**问题3:**线程能不能被多次启动?
不能,因为这相当于线程被调用了两次,而不是线程被启动
example1
package com.thread;
/**
* 线程的启动
*/
public class MyThread extends Thread{
//重写run方法
@Override
public void run() {
for (int i=0;i<100;i++){
System.out.println(i);
}
}
}
package com.thread;
public class ThreadDemo {
public static void main(String[] args) {
MyThread myThread1 = new MyThread();
// myThread1.run();//运行run但是是单线程的
//要运行多线程
MyThread myThread2 = new MyThread();
//开两个线程,并且让线程进行抢占
myThread1.start();
myThread2.start();
}
}
线程设置名称和获取
public final String getName();获取名称
public final void setName(String name);设置名称
方法1.直接设置名称
线程名称.setName("XXX");
方法2.
MyThread myThread1 = new MyThread("西门");
注意:要设置有参无参两种
如何获取main方法所在线程?
直接再main方法中不能使用getName方法,因为getName是Thread类中的方法
那么怎么办?
有另外一个方法
System.out.println(Thread.currentThread().getName());
输出位main,这就是jvm帮助我们启动的主线程
System.out.println(getName()+":"+i);
线程的优先级设置和使用
package com.thread;
/**
* 线程调度的两种方式
* 1.分支调度模型
* 所有线程轮流使用cpu的使用权,平均分给每个线程占用cpu的时间片
* 2.抢占式调度模型
* 让优先级高的线程优先使用cpu,若相同则随机选一个
* 优先级高的获取的时间相对多一些
* 再Java中我们使用的是第二种
*
*
* 优先级默认为5
* 如何设置优先级?
* public final void setPriority(new priority);
*
* 通过api我们知道优先级是有条件设置的
* 最高优先级为10
* 默认为5
* 最低为1
*
*/
public class ThreadDemo2 {
public static void main(String[] args) {
MyThread mt = new MyThread();
MyThread mt2 = new MyThread();
MyThread mt3 = new MyThread();
mt.setName("西门吹雪");
mt2.setName("叶孤城");
mt3.setName("陆小凤");
mt.setPriority(10);
mt2.setPriority(1);
mt3.setPriority(1);
// System.out.println(mt.getPriority());
// System.out.println(mt2.getPriority());
// System.out.println(mt3.getPriority());
mt.start();
mt2.start();
mt3.start();
}
}
线程控制
1.线程的休眠和加入
package com.thread;
import java.util.Date;
public class MySleep extends Thread{
@Override
public void run() {
for (int i = 0;i<30;i++){
// System.out.println(getName()+new Date()+"---"+i);
//
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println(getName()+":"+1);
}
}
}
package com.thread;
/**
* 线程控制:
* 线程的睡眠
* 1.public static void sleep(long millis);线程的休眠,单位毫秒
* 线程的加入
* 2.public final void join();
*/
public class ThreadDemo3 {
public static void main(String[] args){
MySleep ms = new MySleep();
MySleep ms2 = new MySleep();
MySleep ms3 = new MySleep();
ms.setName("西门吹雪");
try {
ms.join();//等ms运行完之后再运行后两个线程
} catch (InterruptedException e) {
e.printStackTrace();
}
ms2.setName("叶孤城");
ms3.setName("朱停");
ms.start();
ms2.start();
ms3.start();
}
}
2.线程的礼让和后台
package com.thread;
public class MyYield extends Thread {
@Override
public void run() {
for (int i=0;i<50;i++){
System.out.println(getName()+":"+i);
Thread.yield();
}
}
}
package com.thread;
/**
* * 线程礼让:
* * public static void yield();就是当前线程愿意让出正在使用的处理器资源
* * 这里仅仅是线程让出处理器的使用,但并不是交替出现的,由谁直营还是要抢占
*/
public class MyYieldDemo {
public static void main(String[] args) {
MyYield my = new MyYield();
MyYield my2 = new MyYield();
my.setName("马云");
my2.setName("马化腾");
my.start();
my2.start();
}
}
package com.thread;
public class MyDaemon extends Thread{
@Override
public void run() {
for (int i=0;i<100;i++){
System.out.println(getName()+":"+i);
Thread.yield();
}
}
}
package com.thread;
/**
* 后台线程:
* public final void setDaemon(boolean on);
* 将此线程标记为daemon线程或用户线程
* 当运行的唯一线程都是守护线程时,java虚拟机将推行胡
* 线程启动前必须调用此方法
*/
public class MyDeamonDemo {
public static void main(String[] args) {
MyDaemon md1 = new MyDaemon();
MyDaemon md2 = new MyDaemon();
md1.setName("张飞");
md2.setName("关羽");
//线程启动前必须调用此方法
md1.setDaemon(true);
md2.setDaemon(true);
//吕布这个线程跑完之后张飞和关羽也就不跑了
md1.start();
md2.start();
Thread.currentThread().setName("吕布");
for (int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
3.线程的中断
package com.thread;
import java.util.Date;
public class MyStop extends Thread {
@Override
public void run() {
//给线程记时
System.out.println("线程"+getName()+"开始了"+new Date());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {//捕获线程是否被中断
System.out.println("线程被中断");
}
System.out.println("线程"+getName()+"结束了"+new Date());
}
}
package com.thread;
/**
* 线程中断
* public final void stop();强制线程停止执行(已过时)
*
*/
public class MyStopDemo {
public static void main(String[] args) {
MyStop ms = new MyStop();
ms.setName("西门庆");
ms.start();
try {
Thread.sleep(3000);
// ms.stop();// * 这种方法本质上时不安全的
ms.interrupt();//线程被中断
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
4.线程的生命周期
- 创建:创建线程对象
- 就绪:线程对象已经启动,但是没有获取cpu执行权
- 运行:获取cpu执行权
- 阻塞:没有线程的执行权会回到就绪那一步
- 死亡:代码运行完毕,线程消亡
方式2
- 自定义一个类去实现runnable接口
- 重写run()
- 创建自定义接口对象
- 创建Thread对象,然后将自定义的对象作为参数传递
多线程的第二种实现方式和名称获取
package com.thread;
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i=0;i<=50;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
package com.thread;
/**
* Thread的构造方法要求传递一个接口
*
* 通过接口实现多线程的好处
* 1.可以避免由于Java单继承带来的局限性
* 2.适合多个相同程序的代码去处理同一个资源的情况,
* 将线程同程序的代码程序有效分离,较好的体现了面向对象的思想
*
*/
public class MyRunnableDemo {
public static void main(String[] args) {
//只需要创建一个接口思想
MyRunnable mr = new MyRunnable();
//创建两个Thread接口,缤纷且去启动它
//方式1.设置名称:
// Thread t1 = new Thread(mr);
// Thread t2 = new Thread(mr);
// t1.setName("西门吹雪");
// t2.setName("西门一白");
//方式二设置名称:参数设置
Thread t1 = new Thread(mr,"西门吹雪");
Thread t2 = new Thread(mr,"叶孤城");
t1.start();
t2.start();
}
}
text
需求:
卖电影票,有三个售票口,使用两种方式实现
方式1
package com.thread.text;
//用两种方式定义售票窗口(3)
//方式1
public class SaleTickets extends Thread {
// private int tickets = 30;
// private Object obj = new Object();
// @Override
// public void run() {
// while(true) {
//synchronized (obj){
// if (tickets > 0) {
// System.out.println(Thread.currentThread().getName() + ":第" + (tickets--) + "张票已被卖出");
// }else{
// break;
// }
// }
// try {
// Thread.sleep(200);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
// System.out.println(getName()+"All tickets were sold out");
// }
//tickets
static int tickets = 50;
//lock object
static Object obj = new Object();
@Override
public void run(){
while (true){
//Synchronized
synchronized (obj){
if (tickets > 0){
System.out.println(getName()+" : 第"+tickets--+"张票");
}else{
break;
}
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package com.thread.text;
//方式1测试类:使用Thread继承方式
public class SaleTicketsDemo {
public static void main(String[] args) {
SaleTickets st1 = new SaleTickets();
SaleTickets st2 = new SaleTickets();
SaleTickets st3 = new SaleTickets();
st1.setName("窗口1");
st2.setName("窗口2");
st3.setName("窗口3");
st1.start();
st2.start();
st3.start();
}
}
方式2
package com.thread.text;
/**
* //用两种方式定义售票窗口(3)
* //方式2:根据显示情况,我们的售票系统会有延迟
* 1.会出现重复售票
* 短暂的延迟,倒是线程同时进入售票系统,同时买票,然后同时输出
* 2.出现负数票
* 同时售出,多减了一次
*
* 如何判断是多线程问题
* 1.是否是多线程环境
* 2.是否有共享数据
* 3.是否有多条语句操作共享数据
* 解决方式:
* 基本思想:让程序没有安全问题的环境
* 把多个语句操作数据的代码锁起来,让任意时刻只有一个程序执行
* 而同步代码快可以解决
* synchronized(对象)(需要同步的代码)
*
*/
public class SaleTickets2 implements Runnable{
private int tickets = 50;
private Object obj = new Object();
@Override
public void run() {
while(true) {
synchronized (obj) {
if (tickets > 0) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":第" + (tickets--) + "张票已被卖出");
}
}
}
}
}
package com.thread.text;
//方式1测试类:使用runnable接口实现
/**
* 同步可以解决安全问题的根本原因在于对象上,该对象如同锁的功能
* 代码在运行时它只会运行它自己的Object对象,所以我们需要将Object共享出来,
* 如果直接在synchronized()上定义新的Object那只是给每一个对象又锁了一个Object,没有任何意义
* 共享的obj是每一次执行的时候,只有一个Object在运行,这样线程在运行的时候没有其他抢占
* 只有在其将obj运行完之后,三个创客再进行抢占,再执行,如此就不会出现冲突
*
* 同步代码块的特点
* 前提:
* 1.多线程
* 2.多西安城使用的是同一个锁对象
* 好处:
* 解决了多线程安全问题
* 弊病:
* 当线程相当多是,每个线程都会判断同步上的锁,这是很消耗资源的,降低了代码的运行效率
*/
public class SaleTicketsDemo2 {
public static void main(String[] args) {
SaleTickets2 st = new SaleTickets2();
Thread st1 = new Thread(st,"窗口1");
Thread st2 = new Thread(st,"窗口2");
Thread st3 = new Thread(st,"窗口3");
st1.start();
st2.start();
st3.start();
}
}
同步方法的三种使用
package com.thread.text;
import junit.framework.Test;
public class SaleTickets2_2 implements Runnable {
private static int tickets = 50;
private final Object obj = new Object();
private final Test t = new Test();
private int x = 0;
@Override
public void run() {
while (true) {
if (x % 2 == 0) {
// synchronized(this){
// synchronized(t){
synchronized(SaleTickets2_2.class){
if (tickets > 0) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":第" + (tickets--) + "张票");
}
}
} else {
ticket();
}
x++;
}
}
private synchronized static void ticket(){
if (tickets > 0) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":第" + (tickets--) + "张票");
}
}
class Test {
}
}
package com.thread.text;
/**
*1. 同步代码快锁对象到底是谁?
* 任意对象
*2. 同步方法的格式及锁对象的问题?
* 将关键字写在方法声明
* 同步方法块的锁是this
*3.静态方法的锁对象
* 静态方法是随着类的加载而加载
* 其实这里的锁对象是该类的字节码文件对象(反射知识点)
*/
public class SaleTicketsDemo2_2 {
public static void main(String[] args) {
SaleTickets2_2 st = new SaleTickets2_2();
Thread st1 = new Thread(st,"窗口1");
Thread st2 = new Thread(st,"窗口2");
Thread st3 = new Thread(st,"窗口3");
st1.start();
st2.start();
st3.start();
}
}
锁对象的基本操作
虽然我们可以理解同步代码块和同步方法的锁对象问题,但我们并没有直接看到在哪里加了锁,在哪里释放了锁,为了更清晰的表达苏荷加锁和释放锁,jdk5以后提供了一个新的锁对象
Lock:
概述:
这是一个接口,实现类比synchronized语句和方法实现类更广发的操作
Lock实现类:
构造方法:ReetrantLock:
成员方法:void lock:上锁
void lock:解锁
package com.thread.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SaleTickets implements Runnable{
private int tickets=50;
//创建一个锁对象
private Lock l = new ReentrantLock();
@Override
public void run() {
while(true){
//上锁
l.lock();
if (tickets>0){
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":第"+(tickets--+"张票"));
}
//解锁
l.unlock();
}
}
}
package com.thread.lock;
public class LockDemo {
public static void main(String[] args) {
SaleTickets st = new SaleTickets();
Thread t1 = new Thread(st,"窗口1");
Thread t2 = new Thread(st,"窗口2");
Thread t3 = new Thread(st,"窗口3");
t1.start();;
t2.start();;
t3.start();;
}
}
死锁问题
同步的弊病
- 效率低
- 如果出现了同步嵌套,就容易产生死锁问题
死锁问题:是指两个或两个以上的线程在执行的过程中,因争夺资源长生的相互等待的现象
死锁代码
package com.thread.lock;
import com.sun.org.apache.xpath.internal.operations.Bool;
public class DeadLock extends Thread{
private Boolean flag;
private static Object lockA = new Object();
private static Object lockB = new Object();
//有参构造初始化
public DeadLock(boolean flag){
this.flag = flag;
}
@Override
public void run() {
if (flag){
synchronized (lockA){
System.out.println("if lockA");
synchronized (lockB){
System.out.println("if lockB");
}
}
}else{
synchronized (lockB){
System.out.println("else lockB");
synchronized (lockA){
System.out.println("else lockA");
}
}
}
}
}
package com.thread.lock;
public class DeadLockDemo {
public static void main(String[] args) {
DeadLock d1 = new DeadLock(true);
DeadLock d2 = new DeadLock(false);
d1.start();
d2.start();
}
}
5.生产者消费者模型线程安全问题
等待唤醒机制
wait();当前线程等待
notify();唤醒等待的单个线程
notifyAll();唤醒等待的所有线程
实现等待唤醒机制
实现1正常实现
package com.unit14.Selltickets;
public class Student {
private String Name;
private int age;
boolean flag;
public Student() {
}
public Student(String name, int age) {
Name = name;
this.age = age;
}
public String getName() {
return Name;
}
public void setName(String name) {
this.Name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"Name='" + Name + '\'' +
", age=" + age +
", flag=" + flag +
'}';
}
}
package com.unit14.Selltickets;
public class SetThread implements Runnable {
private Student student;
private int x = 0;
private Object object = new Object();
public SetThread(Student student) {
this.student = student;
}
@Override
public void run() {
while(true){
synchronized (student){
if(student.flag){
//有数据,不生产,等待
try {
student.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//没有数据就生产,生产完毕,通知消费者
if(x%2==0){
student.setName("西门吹雪");
student.setAge(12);
}else{
student.setName("叶孤城");
student.setAge(13);
}
x++;
//修改标记
student.flag = true;
student.notify();
}
}
// student.setName("西门吹雪");
// student.setAge(18);
}
}
package com.unit14.Selltickets;
public class GetThread implements Runnable {
private Student student;
public GetThread(Student student) {
this.student = student;
}
@Override
public void run() {
while (true) {
synchronized (student) {
//判断是否有数据,没有就等待
if (!student.flag) {
try {
student.wait();//线程等待的同时,释放对应锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//有数据,就消费,消费完成就通知生产者生产
System.out.println(student.getName() + "---" + student.getAge());
//修改标记
student.flag = false;
student.notify();
}
}
}
}
package com.unit14.Selltickets;
public class StudentDemo {
public static void main(String[] args) {
Student student = new Student();
//创建自定义线程类对象
SetThread setThread = new SetThread(student);
GetThread getThread = new GetThread(student);
//创建Thread对象,将自定义像成雷对象作为参数传递
Thread thread = new Thread(setThread,"设置线程");
Thread thread2 = new Thread(getThread,"获取线程");
//启动线程
thread.start();
thread2.start();
}
}
实现2.同步方法实现
package com.thread.communication3;
public class Student {
private String name;
private int age;
private boolean flag;//默认问false//标记有没有数据
public synchronized void set(String name,int age) {
//有数据的情况
if (this.flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//添加数据
this.name = name;
this.age = age;
//修改标记
this.flag = true;
this.notify();
}
public synchronized void get(){
if (!this.flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//有数据就使用数据输出
System.out.println(this.name+"---"+this.age);
//修改标记
this.flag = false;
this.notify();
}
}
package com.thread.communication3;
public class SetStudent implements Runnable{
private Student s;
private int x = 0;
public SetStudent(Student s) {
this.s = s;
}
@Override
public void run() {
// Student s = new Student();
while(true){
//生产数据
if (x % 2 == 0) {
s.set("西门吹雪",18);
} else {
s.set("叶孤城",12);
}
x++;
}
}
}
package com.thread.communication3;
public class GetStudent implements Runnable{
private Student s;
public GetStudent(Student s) {
this.s = s;
}
@Override
public void run() {
// Student s = new Student();
while(true)//多次获取
{
s.get();
}
}
}
package com.thread.communication3;
/*
等待唤醒机制
通过同步方法来实现等待唤醒机制
*/
public class StudentDemo {
public static void main(String[] args) {
Student s = new Student();
//这样两边获取到的s是同一个s
SetStudent ss = new SetStudent(s);
GetStudent gs = new GetStudent(s);
Thread t1 = new Thread(ss);
Thread t2 = new Thread(gs);
t2.start();
t1.start();
}
}
问题:为什么将等待和唤醒的方法都定义在Object中,而不是Thread中
**答:**为了方便锁的调用
6.线程组
概述:他可以对一批线程进行分类管理,java允许程序直接对线程进行控制
好处:统一管理
package com.unit14.ThreadGroup;
public class MYRunnable implements Runnable{
@Override
public void run() {
for (int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
package com.unit14.ThreadGroup;
/**
* 返回此线程所在的线程组getThreadGroup()
*/
public class ThreadGroupDemo {
public static void main(String[] args) {
method1();
System.out.println("########################");
method2();
}
private static void method2() {
//设置线程组
ThreadGroup tg = new ThreadGroup("newThread");
//定义接口对象
MYRunnable mr = new MYRunnable();
Thread t1 = new Thread(tg,mr,"西门吹雪");
Thread t2 = new Thread(tg,mr,"叶孤城");
System.out.println(t1.getThreadGroup().getName());
System.out.println(t2.getThreadGroup().getName());
System.out.println(Thread.currentThread().getThreadGroup().getName());
}
private static void method1() {
MYRunnable mr = new MYRunnable();
Thread t1 = new Thread(mr,"西门吹雪");
Thread t2 = new Thread(mr,"叶孤城");
ThreadGroup tg1 = t1.getThreadGroup();
ThreadGroup tg2 = t2.getThreadGroup();
System.out.println(tg1.getName());
System.out.println(tg2.getName());
//通过链式编程,我们获取main方法
System.out.println(Thread.currentThread().getThreadGroup().getName());
}
}
7.线程池
1.为什么要学线程池
因为我们在创建线程的时候,成本是比较高的,因为每一次创建线程都需要和操作系统进行交互。线程池会在程序启动时,提前创建一些线程放在线程池中等待使用,这样可以大大的提高执行效率
2.特点
a.线程池中的线程在使用之后不会消亡,而是重新回到线程池中成为空闲状态等待下一个对象使用
b.JDK5之前需要手动配置线程池,之后Java开始内置线程池
Executors工厂类:
通过下面方法获得线程池对象
1.public static ExecutorService newCachedThreadPool()
2.public static ExecutorService newFixedThreadPool(int nThreads)
控制输入几个线程
使用:
1、创建线程池对象
public static ExecutorService newFixedThreadPool(int nThreads)
2,创建Runnable对象
可以执行Runnable对象或者callable对象代表的线程
3,提交Runnable实例
a.Futer<?> submit(Runnable task)
b. Future submit(callable task)
4,关闭线程池
shutdown();
代码实例
package com.unit14.ThreadPool;
public class MyThread implements Runnable{
@Override
public void run() {
for (int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}
package com.unit14.ThreadPool;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.*;
public class ThreadPoolDemo {
public static void main(String[] args) {
//创建线程池对象
ExecutorService es = Executors.newFixedThreadPool(2);
//创建自定义线程类对象
MyThread myThread = new MyThread();
//提交Runnable实例
es.submit(myThread);
es.submit(myThread);
//关闭线程池
es.shutdown();
}
}
3.public static ExecutorService newSingleThreadExector()
这些方法的返回值是ExecutorServicr对象,该对象表示一个线程池,它可以执行Runnable 对象或者Callable对象代表的线程
方式三
7.1多线程的第三种实现:Callable接口
Callable接口类似于Runnable接口,但是,callable有返回值,并且可能抛出异常
普通实现类似Runnable
package com.unit14.Callable;
import java.util.concurrent.Callable;
public class MyCallable implements Callable {
@Override
public Object call() throws Exception {
for (int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+"---"+i);
}
return null;
}
}
package com.unit14.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyCallableDemo {
public static void main(String[] args) {
//创建线程池对象
ExecutorService es = Executors.newFixedThreadPool(2);
//创建自定义线程类对象
MyCallable myCallable = new MyCallable();
//提交Callable对象
es.submit(myCallable);
es.submit(myCallable);
es.shutdown();
}
}
指定泛型实现(进行求和)
package com.unit14.Callable.test;
import java.util.concurrent.Callable;
public class MyCallable implements Callable <Integer>{
private int num;
public MyCallable(int num){
this.num = num;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for(int i=0;i<=num;i++){
sum+=i;
}
return sum;
}
}
package com.unit14.Callable.test;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 第三种实现线程的方式,
* 1.可以有返回值
* 2.抛出异常
* 弊端:
* 代码比较复杂,所以一般不用
*/
public class MyCallableDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建线程池对象
ExecutorService es = Executors.newFixedThreadPool(2);
//创建自定义线程类对象
// MyCallable myCallable = new MyCallable(100);
// MyCallable myCallable2 = new MyCallable(200);
//提交自定义线程类对象
Future<Integer> future = es.submit(new MyCallable(100));
Future<Integer> future2 = es.submit(new MyCallable(5));
//get()
int num = future.get();
int num2 = future2.get();
System.out.println("1-100的和:"+num);
System.out.println("1-200的和:"+num2);
es.shutdown();
}
}
7.2使用匿名内部类实现多线程
因为平时开发中可能会需要线程执行某一项工作,且只执行一次,这样就可以简化工程类结构
package com.unit14.Callable.test;
/**
* 通过笔名内部类使用多线程
* 1.继承Thread类
* 2.实现Runnable接口
*/
public class NoNameThreadDemo {
public static void main(String[] args) {
//1.Thread对象
new Thread(){
@Override
public void run() {
for(int i=0;i<=100;i++){
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}.start();
//2.Runnable接口:
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<=10;i++){
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}){}.start();
//3,当 Runnable和Thread同时引用,会走Thread方法,也就是会输出world
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
}){
public void run() {
System.out.println("world");
}
}.start();
}
}
匿名内部类格式
new Thread(){}.start();
9.定时器
是一个线程工具,可用于调度多个定时任务,通过TImer()和TimerTask()这两个类来实现定义和调度的功能
Timer
构造方法:Timer()
成员方法:
1.schedule(TimerTask task,long delay)
2.schedule(TimerTask task,long dealy,long period)
TimerTask
可以有定时器指定一次或重复的执行任务
构造方法:是一个抽象类
成员方法:
1.public abstract void run()
package com.unit14.Timer;
import java.util.Timer;
import java.util.TimerTask;
public class MyTaskDemo {
public static void main(String[] args) {
//创建定时器对象
Timer timer = new Timer();
//计划任务对象
//三秒后执行run,并且每疫苗执行一次
// timer.schedule(new MyTask(timer),3000,1000);
//如何执行一次就停止
// timer.cancel();不能在这里停止
timer.schedule(new MyTask(timer),3000);
}
static class MyTask extends TimerTask {
private Timer timer;
public MyTask(Timer timer){
this.timer = timer;
}
@Override
public void run() {
// for (int i=0;i<=100;i++){
// System.out.println(Thread.currentThread().getName()+"---"+i);
// }
System.out.println("唐僧洗发哎飘柔");
timer.cancel();
}
}
}
常用发的定时调度框架:Quartz调度框架
课堂练习:
需求:使用定时器在指定时间点删除指定目录
package com.unit14.Timer;
import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
/**
* 需求:删除目录下所有文件和文件加
*/
public class TimerDemo2 {
public static void main(String[] args) throws ParseException {
//创建定时器对象
Timer t = new Timer();
String s = "2021-05-02 23:32:00";
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse(s);
t.schedule(new delFile(),date);
}
}
//写我们的任务
class delFile extends TimerTask{
@Override
public void run() {
File f = new File("test");
deleteFolder(f);
}
//使用递归删除
public void deleteFolder(File f){
//获取目录下的文件对象
File[] files = f.listFiles();
if (files!=null){
//遍历每一个文件对象
for (File file:files){
//文件夹时
if (file.isDirectory()){
deleteFolder(file);
}else{
file.delete();
}
}
//最后删除文件夹
f.delete();
}
}
}