Java多线程

发布于:2024-08-08 ⋅ 阅读:(114) ⋅ 点赞:(0)

多线程的创建

方式1:继承Thread类的方式进行实现

package day30_多线程;

public class Demo01 {
	// 方式1:继承Thread类的方式进行实现
	// 步骤1:继承Thread类
	// 步骤2:重写run方法
	// 步骤3:创建MyThread的对象,调用start方法启动线程
	public static void main(String[] args) {
		MyThread t1 = new MyThread();
		t1.setName("线程1");// 给线程设置名字
		// 开启线程--此处需要注意:不能直接调用run方法,否则就是对象的创建和方法的调用
		t1.start();
		t1.run();
		for (int i = 0; i <= 50; i++) {
			System.out.println("main" + i);
		}
	}
}
package day30_多线程;

public class MyThread extends Thread {
	// 重写run方法,线程要执行的代码
	@Override
	public void run() {
		for (int i = 0; i <= 50; i++) {
//			setName("测试");
			System.out.println(getName() + ":" + i);
		}
	}
}

方式2:实现Runnable接口的方式进行实现

package day30_多线程;

public class Demo02 {
	// 方式2:实现Runnable接口的方式进行实现
	// 1.自定义一个类实现Runnable接口
	// 2.重写run方法
	// 3.创建自定义类的对象,将其作为参数
	// 4.创建一个Thread对象,参数为3,并开启线程
	public static void main(String[] args) {
		// 创建MyRunable的对象,表示多线程要执行的任务
		MyRunable r1 = new MyRunable();

		// 创建线程对象
		Thread t1 = new Thread(r1);
		Thread t2 = new Thread(r1);
		t1.setName("子线程1");
		t2.setName("子线程2");
		t1.start();// 开启线程
		t2.start();

		// 设置主线程的名字
		Thread mainThread = Thread.currentThread();
		mainThread.setName("main主线程");
		for (int i = 0; i <= 50; i++) {
			System.out.println(mainThread.getName() + ":" + i);
		}
	}
}
package day30_多线程;

public class MyRunable implements Runnable {
	@Override
	public void run() {
		for (int i = 0; i <= 50; i++) {
			// getName()为Thread类的方法,不能直接使用getName()方法
			// MyRunable和Thread类没有继承关系
			// 通过Thread的静态方法获取当前线程对象后获取方法名字
			Thread t1 = Thread.currentThread();
			System.out.println(t1.getName() + ":" + i);
		}
	}
}

方式3:实现Callable接口的方式进行实现线程

package day30_多线程;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Demo03 {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		// 方式3:实现Callable接口的方式进行实现线程---和方式2相同
		// 特点:可以获取到多线程的运行结果
		// 步骤1:创建一个实现类来实现Callable接口
		// 步骤2:重写ca11方法(有返回值,表示多线程执行的任务的返回值)
		// 步骤3:创建实现类对象
		// 步骤4:创建FutureTask类型的对象(管理多线程执行后的结果)
		// 步骤5:Thread类的对象的创建,并启动

		// 创建Callable实现类对象(表示多线程要执行的任务)
		MyCallable m1 = new MyCallable();
		// 创建FutureTask对象(作用管理多线程的运行结果)
		FutureTask<Integer> ft = new FutureTask<Integer>(m1);
		// 创建线程对象
		Thread t1 = new Thread(ft);
		// 启动线程
		t1.start();

		// 获取多线程运行的结果
		Integer num = ft.get();
		System.out.println(num);
	}
}
package day30_多线程;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<Integer> {
	//线程要执行的代码
	@Override
	public Integer call() throws Exception {
		int sum = 0;
		for (int i = 0; i <= 100; i++) {
			sum += i;
		}
		return sum;
	}
}

多线程的方法

**getName()😗*获取此线程名字
细节:如果没有给线程设置名字.线程也会有默认名称
格式:Thread-X(X的序号从0开始)

**void setName(String name)**设置线程的名字,构造方法进行设置线程名称

**Thread.currentThread();**获取当前的线程

**Thread.sleep(XXX);**让指定的线程休眠多少毫秒
细节:哪条线程执行到此方法,那么哪条线程就会在这里停留对应的时间
参数为毫秒 1秒=1000毫秒
当时间到了以后,线程会自动的醒来,继续执行下面的代码

getPriority() 获取优先级

setPriority() 设置优先级,范围1~10,默认优先级为5

数字越大优先级越高,但不是一定先执行完

**setDaemon(true)**设置守护线程

Thread.yield()

让出当前的cpu的执行权,让结果更加均匀一点,也不是绝对均匀。

当前的线程让出执行权后,再次进行抢夺资源也可能会抢到。

join()线程插队

守护线程setDaemon(true)将t2线程标记为守护线程

package day30_多线程方法;

//守护线程
public class Demo03 {
	public static void main(String[] args) {
		MyThread1 t1 = new MyThread1();
		MyThread2 t2 = new MyThread2();
		t1.setName("女神线程");
		t2.setName("(备胎)守护线程");
		// 在调用start()方法前,
		// 调用setDaemon(true)将t2线程标记为守护线程
		t2.setDaemon(true);
		t1.start();
		t2.start();
	}
}

class MyThread1 extends Thread {
	@Override
	public void run() {
		for (int i = 0; i <= 10; i++) {
			System.out.println(getName() + "@" + i);
		}
	}
}

class MyThread2 extends Thread {
	@Override
	public void run() {
		for (char c = 'A'; c <= 'Z'; c++) {
			System.out.println(getName() + "@" + c);
		}
	}
}

Thread.yield();

package day30_多线程方法;

public class Demo04 {
	public static void main(String[] args) {
		MyThread3 t1 = new MyThread3("线程1");
		MyThread3 t2 = new MyThread3("线程2");

		t1.start();
		t2.start();
	}
}

class MyThread3 extends Thread {
	public MyThread3(String string) {
		super(string);
	}

	@Override
	public void run() {
		for (int i = 0; i <= 100; i++) {
			System.out.println(getName() + "@" + i);
			// 让出当前的cpu的执行权,让结果更加均匀一点,也不是绝对均匀
			// 当前的线程让出执行权后,再次进行抢夺资源也可能会抢到
			Thread.yield();
		}
	}
}

内部类创建线程

package day30_多线程方法;


public class Demo05 {
	public static void main(String[] args) {
		// 内部类创建线程
		Thread t1 = new Thread() {
			public void run() {
				for (char i = 'a'; i < 'z'; i++) {
					System.out.println(getName() + "@" + i);
					Thread.yield();
				}
			}
		};
		Thread t2 = new Thread() {
			public void run() {
				for (char i = 65; i < 'z'; i++) {
					System.out.println(getName() + "@" + i);
					Thread.yield();
				}
			}
		};
		t1.start();
		t2.start();
	}
}

t1.join()线程插队

package day30_多线程方法;

//join方法
public class Demo06 {
	public static void main(String[] args) throws InterruptedException {
		// 如果想要先执行线程1,再执行main方法,使用join,插队
		MyThread4 t1 = new MyThread4();
		t1.setName("线程");
		t1.start();

		// 将线程t1添加到当前线程的前面执行
		t1.join();
		for (int i = 0; i < 100; i++) {
			System.out.println("main@" + i);
		}
	}
}

class MyThread4 extends Thread {
	@Override
	public void run() {
		for (int i = 0; i <= 100; i++) {
			System.out.println(getName() + "@" + i);
		}
	}
}

线程安全

线程加锁是为了避免超卖问题,或者卖出同一张票的问题

加锁的方式有两种:

Synchronized锁

//线程安全

//多个线程实现先后依次访问共享的资源,解决线程安全
//加锁:每次只允许一个线程加锁,加锁后才可以访问资源,访问完毕后释放锁资源,
//其他线程才可以抢占资源

//方案1:同步代码块,注意传入的锁的对象一定是唯一的
//synchronized(同步锁) {
//	//共享的资源
//}

//方案2:同步方法,将访问的共享资源的核心方法上锁,以此来保证线程安全
//修饰符 synchronized 返回值类型 方法名(方法参数) {
//	
//}

//锁对象不能自己指定:
//	普通方法锁对象:this
//	静态方法锁对象:当前类的字节码文件对象

Lock锁

//方案3:为了更加清晰的表示如何加锁和解锁,JDK5以后提供了一个新的锁对象Lock
//获取锁:void lock()获得锁,然后获得资源
//释放锁:void unlock()释放锁,然后释放资源

Demo01

package day31_线程安全;

public class MyThread extends Thread {
	// 表示此成员变量为所有的对象共享
	static int ticket = 0 ;
	// 锁对象唯一
	static Object obj = new Object();

	public MyThread(String string) {
		super(string);
	}

	// 线程在执行过程中,cpu的执行权可能随时会被抢走
	@Override
	public void run() {
//		synchronized (obj) {
		while (true) {
			// 锁对象一定要唯一
			synchronized (obj) {
//			synchronized (MyThread.class) {
				if (ticket >= 1000) {
					break;
				}
				ticket++;
				System.out.println(getName() + "正在卖票" + ticket + "张");
			}
		}
	}
}
package day31_线程安全;
//线程加锁是为了防止超卖问题、或者卖出同一张票的问题
//加锁的方式有两种:关键字Synchronized,Lock对象
public class Demo01 {
	public static void main(String[] args) {
		MyThread t1 = new MyThread("窗口1");
		MyThread t2 = new MyThread("窗口2");
		MyThread t3 = new MyThread("窗口3");

		t1.start();
		t2.start();
		t3.start();
	}
}

Demo02

package day31_线程安全;
public class Demo02 {
	public static void main(String[] args) {
		MyThread2 t1 = new MyThread2("窗口1");
		MyThread2 t2 = new MyThread2("窗口2");
		MyThread2 t3 = new MyThread2("窗口3");

		t1.start();
		t2.start();
		t3.start();
	}
}
package day31_线程安全;

public class MyThread2 extends Thread {
	// 表示此成员变量为所有的对象共享
	static int ticket;

	public MyThread2(String string) {
		super(string);
	}

	// 线程在执行过程中,cpu的执行权可能随时会被抢走
	@Override
	public void run() {
		while (true) {
			if (method()) {
				break;
			}
		}
	}

	public static synchronized boolean method() {
		if (ticket >= 1000) {
			return true;
		}
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		ticket++;
		System.out.println(Thread.currentThread().getName() + "正在卖票" + ticket + "张");
		return false;
	}
}

Demo03

package day31_线程安全;

public class Demo03 {
	public static void main(String[] args) {
		MyRun m1 = new MyRun();
		
		Thread t1 = new Thread(m1, "窗口1");
		Thread t2 = new Thread(m1, "窗口2");
		Thread t3 = new Thread(m1, "窗口3");

		t1.start();
		t2.start();
		t3.start();
	}
}
package day31_线程安全;

public class MyRun implements Runnable {
	// 表示此成员变量为所有的对象共享
	static int ticket;
	// 线程在执行过程中,cpu的执行权可能随时会被抢走
	@Override
	public void run() {
		while (true) {
			if (method()) {
				break;
			}
		}
	}

	public synchronized boolean method() {
		if (ticket >= 2000) {
			return true;
		}
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		ticket++;
		System.out.println(Thread.currentThread().getName() + "正在卖票" + ticket + "张");
		return false;
	}
}

Demo04

package day31_线程安全;

public class Demo04 {
	public static void main(String[] args) {
		MyThread3 t1 = new MyThread3("窗口1");
		MyThread3 t2 = new MyThread3("窗口2");
		MyThread3 t3 = new MyThread3("窗口3");

		t1.start();
		t2.start();
		t3.start();
	}
}
package day31_线程安全;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyThread3 extends Thread {
	// 表示此成员变量为所有的对象共享
	static int ticket;
	static Lock lock = new ReentrantLock();

	public MyThread3(String string) {
		super(string);
	}

	// 线程在执行过程中,cpu的执行权可能随时会被抢走
	@Override
	public void run() {
		while (true) {
			try {
				lock.lock();
				if (ticket >= 2000) {
					break;
				}
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				ticket++;
				System.out.println(Thread.currentThread().getName() + "正在卖票" + ticket + "张");
			} finally {
				lock.unlock();
			}
		}
	}

}

线程状态及常用方法

在Java程序中,一个线程对象通过调用start()方法启动线程,并且在线程获取CPU时,自动执行run()方法。run()方法执行完毕,代表线程的生命周期结束。
●在整个线程的生命周期中,线程的状态有以下6种:

○New:新建状态,新创建的线程,此时尚未调用start()方法;
○Runnable:运行状态,运行中的线程,已经调用start()方法,线程正在或即将执行run()方法;
○Blocked:阻塞状态,运行中的线程,在等待竞争锁时,被阻塞,暂不执行;
○Waiting:等待状态,运行中的线程,因为join()等方法调用,进入等待;
○Timed Waiting:计时等待状态,运行中的线程,因为执行sleep(等待毫秒值)join(等待毫秒值)等方法,进入计时等待;
○Terminated:终止状态,线程已终止,因为run()方法执行完毕。

●当线程启动后,它可以在Runnable、Blocked、Waiting和Timed Waiting这几个状态之间切换,直到最后变成Terminated状态,线程终止

线程终止的原因有:
○线程正常终止:run()方法执行到return语句返回;
○线程意外终止:run()方法因为未捕获的异常导致线程终止;
○对某个线程的Thread实例调用stop()方法强制终止(宇宙超级无敌强烈不推荐);

image.png

NEW新建状态

package day31_线程状态;
//NEW
//新线程创建出来,还没有调用start方法,此时状态为新建状态
public class New {
	public static void main(String[] args) {
		Thread t1 = new Thread() {
			@Override
			public void run() {
				System.out.println("子线程正在执行任务");
			}
		};
		System.out.println(t1.getState());
	}
}

RUNNABLE可运行状态

package day31_线程状态;
//RUNNABLE
//线程对象调用start方法,等待Cpu分配资源RUNNABLE状态
public class Runable {
	public static void main(String[] args) {
		Thread t1 = new Thread() {
			@Override
			public void run() {
				System.out.println("子线程正在执行任务");
			}
		};
		t1.start();
		System.out.println(t1.getState());
	}
}

TERMINATED终止状态

package day31_线程状态;

//TERMINATED终止状态,run()方法执行完毕,操作系统可能已经注销了此线程
public class Terminated {
	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new Thread() {
			@Override
			public void run() {
				System.out.println("子线程正在执行任务");
			}
		};
		t1.start();
		Thread.sleep(1000);
		System.out.println(t1.getState());
	}
}

WAITING等待状态

package day31_线程状态;

//WAITING等待状态 wait() join()
public class WaitingState01 {
	public static void main(String[] args) throws InterruptedException {
		Runnable run = new Runnable() {
			Object obj = new Object();

			@Override
			public void run() {
				synchronized (obj) {
					try {
						obj.wait();// 等待
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		};
		Thread t1 = new Thread(run);
		t1.start();
		Thread.sleep(1000);
		System.out.println(t1.getState());
	}
}
package day31_线程状态;

//WAITING等待状态
public class WaitingState02 {
	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new Thread() {
			@Override
			public void run() {
				while (true) {

				}
			}
		};
		t1.start();
		// t1线程插队到当前的主线程的前面,主线程处于等待状态
		System.out.println("111");
		t1.join();
		System.out.println("222");
		// 获取不到,因为当前的主线程被t1线程给插队,t1为死循环,无法结束任务,主线程处于WATTING状态
		System.out.println(Thread.currentThread().getState());
	}
}
package day31_线程状态;
//WAITING状态 等待状态 wait() join()

//wait()
//1.和synchronized同时存在
//2.锁对象和调用wait方法的需要是同一个

//notify() 唤醒--锁对象的让等待的对象
public class WaitingState03 {
	public static void main(String[] args) throws InterruptedException {
		Object object = new Object();
		Runnable run = new Runnable() {
			@Override
			public void run() {
				synchronized (this) {
					try {
						this.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println("正在执行");
				}
			}
		};
		Thread t1 = new Thread(run);
		t1.start();
		Thread.sleep(1000);
		System.out.println(t1.getState());
	}
}

BLOCKED阻塞状态

package day31_线程状态;
//BLOCKED
public class Blocked {
	public static void main(String[] args) throws InterruptedException {
		Runnable run = new Runnable() {
			@Override
			public void run() {
				synchronized (this) {
					// 死循环
					while(true){
						
					}
				}
			}
		};
		// 创建线程t1和t2并启动子线程
		Thread t1 = new Thread(run, "子线程1");
		Thread t2 = new Thread(run, "子线程2");
		
		t1.start();
		t2.start();
		// 主线程休眠,让t1和t2同时竞争一个锁资源
		Thread.sleep(1000);
		System.out.println(t1.getName() + t1.getState());
		System.out.println(t2.getName() + t2.getState());
	}
}

TIMED_WAITING计时等待状态

package day31_线程状态;

//sleep() 不释放锁资源
//wait()  释放锁资源
public class Timed_Waiting01 {
	public static void main(String[] args) throws InterruptedException {
		Runnable run = new Runnable() {
			@Override
			public void run() {
				synchronized (this) {
					try {
						Thread.sleep(5000);
//						this.wait(5000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		};
		Thread t1 = new Thread(run, "线程1");
		Thread t2 = new Thread(run, "线程2");
		t1.start();
		t2.start();
		// 让主线程等待
		Thread.sleep(1000);
		System.out.println(t1.getName() + t1.getState());
		System.out.println(t2.getName() + t2.getState());
	}
}
package day31_线程状态;

//TIMED_WAITING 线程计时等待状态
//join(参数),可以将线程切换为计时等待状态
//sleep() 不释放锁资源
//wait()  释放锁资源

public class Timed_Waiting02 {
	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new Thread() {
			@Override
			public void run() {
				Thread t2 = new Thread() {
					@Override
					public void run() {
						while (true) {

						}
					}
				};
				t2.start();
				System.out.println("t2的状态计时等待前" + t2.getState());
				// join方法让当前线程处于计时等待
				try {
					t2.join(5000 * 1000);
					System.out.println("t2的状态计时等待后" + t2.getState());
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		};
		t1.start();
		Thread.sleep(1000);
		System.out.println("t1的状态" + t1.getState());
	}
}

线程池

线程池内部维护了若干个线程,没有任务的时候,这些线程都处于等待空闲状态。如果有新的线程任务,就分配一个空闲线程执行。如果所有线程都处于忙碌状态,线程池会创建一个新线程进行处理或者放入队列(工作队列)中等待。

┌─────┐ execute  ┌──────────────────┐
│Task1│─────────>ThreadPool        │
├─────┤          │┌───────┐┌───────┐│
│Task2│          ││Thread1││Thread2││
├─────┤          │└───────┘└───────┘│
│Task3│          │┌───────┐┌───────┐│
├─────┤          ││Thread3││Thread4││
│Task4│          │└───────┘└───────┘│
├─────┤          └──────────────────┘
│Task5│
├─────┤
│Task6│
└─────┘
  ...

线程池常用类和接口

在Java标准库提供了如下几个类或接口,来操作并使用线程池:
1.ExecutorService接口:进行线程池的操作访问;
2.Executors类:创建线程池的工具类;
3.ThreadPoolExecutor及其子类:封装线程池的核心参数和运行机制;

线程池的基本使用方式

// 线程池基本使用方式
// 创建一个ThreadPoolExecutor类型的对象,代表固定大小的线程池
ExecutorService executor = Executors.newFixedThreadPool(3); // 该线程池拥有3个线程

// 执行线程任务
executor.execute(task1);
executor.execute(task2);
executor.execute(task3);
executor.execute(task4);
executor.execute(task5);

// 使用结束后,使用shutdown关闭线程池
executor.shutdown();

线程池的常见方法

● 执行无返回值的线程任务:void execute(Runnable command);
● 提交有返回值的线程任务:Future submit(Callable task);
● 关闭线程池:void shutdown(); 或 shutdownNow();
● 等待线程池关闭:boolean awaitTermination(long timeout, TimeUnit unit);

执行线程任务

execute()和submit()提交任务时的区别?

execute()只能提交Runnable类型的任务,没有返回值,而submit()既能提交Runnable类型任务也能提交Callable类型任务,可以返回Future类型结果,用于获取线程任务执行结果。
execute()方法提交的任务异常是直接抛出的,而submit()方法是捕获异常,当调用Future的get()方法获取返回值时,才会抛出异常。

// 计算1-100w的之间所有数字的累加和,每10w个数字交给1个线程处理
// 创建一个固定大小的线程池:
ExecutorService executorService = Executors.newFixedThreadPool(4);

// 创建集合,用于保存Future执行结果
List<Future<Integer>> futureList = new ArrayList<Future<Integer>>();

// 每10w个数字,封装成一个Callable线程任务,并提交给线程池
for (int i = 0; i <= 900000; i += 100000) {
    Future<Integer> result = executorService.submit(new CalcTask(i+1, i + 100000));
    futureList.add(result);
}

// 处理线程任务执行结果
try {
    int result = 0;
    for (Future<Integer> f : futureList) {
        result += f.get();
    }
    System.out.println("最终计算结果" + result);
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
}

// 关闭线程池
pool.shutdown();

关闭线程池

线程池在程序结束的时候要关闭。

①使用shutdown()方法关闭线程池的时候,它会等待正在执行的任务先完成,然后再关闭。

②shutdownNow()会立刻停止正在执行的任务;

③使用awaitTermination()方法时,主线程会处于一种等待的状态,按照指定的timeout检查线程池。
第一个参数指定的是时间,

第二个参数指定的是时间单位(当前是秒)。返回值类型为boolean型。
如果等待的时间超过指定的时间,但是线程池中的线程运行完毕,awaitTermination()返回true。
如果等待的时间超过指定的时间,但是线程池中的线程未运行完毕,awaitTermination()返回false。
如果等待时间没有超过指定时间,则继续等待。
该方法经常与shutdown()方法配合使用,用于检测线程池中的任务是否已经执行完毕:

线程池的执行流程

1.提交一个新线程任务,线程池会在线程池中分配一个空闲线程,用于执行线程任务;
2.如果线程池中不存在空闲线程,则线程池会判断当前“存活的线程数”是否小于核心线程数corePoolSize。
○如果小于核心线程数corePoolSize,线程池会创建一个新线程(核心线程)去处理新线程任务;
○如果大于核心线程数corePoolSize,线程池会检查工作队列;
■如果工作队列未满,则将该线程任务放入工作队列进行等待。线程池中如果出现空闲线程,将从工作队列中按照FIFO的规则取出1个线程任务并分配执行;
■如果工作队列已满,则判断线程数是否达到最大线程数maximumPoolSize;
●如果当前“存活线程数”没有达到最大线程数maximumPoolSize,则创建一个新线程(非核心线程)执行新线程任务;
●如果当前“存活线程数”已经达到最大线程数maximumPoolSize,直接采用拒绝策略处理新线程任务;

综上所述,执行顺序为:核心线程、工作队列、非核心线程、拒绝策略。

image.png

线程池的配置参数

	参数1:int corePoolSize, 指定线程池的核心线程数量   正式员工
	参数2:int maximumPoolSize, 指定线程池的最大线程数量  正式员工  临时工
	参数3:long keepAliveTime,  指定临时线程的最大存活时间  临时工空闲多久被开除
	参数4:TimeUnit unit, 临时线程最大的存活时间单位
	参数5:BlockingQurue workQueue,  指定线程池的任务队列(客户排队的地方)
	参数6:ThreadFactory threadFactory, 指定线程工厂(负责招聘员工的hr)
	参数7:RejectedExecutionHandler handler  线程池的拒绝策略

参数1corePoolSize线程池核心线程数:也可以理解为线程池维护的最小线程数量,核心线程创建后不会被回收。大于核心线程数的线程,在空闲时间超过keepAliveTime后会被回收;
在创建了线程池后,默认情况下,线程池中并没有任何线程,当调用 execute() 方法添加一个任务时,如果正在运行的线程数量小于corePoolSize,则马上创建新线程并运行这个任务。
IO密集计算:由于 I/O 设备的速度相对于 CPU来说都很慢,所以大部分情况下,I/O 操作执行的时间相对于 CPU 计算来说都非常长,这种场景我们一般都称为 I/O 密集型计算。最佳线程数 =CPU 核数 * [ 1 +(I/O 耗时 / CPU 耗时)]。
CPU密集型:CPU 密集型计算大部分场景下都是纯 CPU 计算,多线程主要目的是提升CPU利用率,最佳线程数 =“CPU 核心数 +1”。这样的话,当线程因为偶尔的内存页失效或其他原因导致阻塞时,这个额外的线程可以临时替补,从而保证 CPU 的利用率。

参数2maximumPoolSize线程池最大线程数:线程池允许创建的最大线程数量;(包含核心线程池数量)
参数3keepAliveTime非核心线程线程存活时间:当一个可被回收的线程的空闲时间大于keepAliveTime,就会被回收。
当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会被回收,直到线程池中的线程数不超过corePoolSize。
如果设置allowCoreThreadTimeOut = true,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;

参数4TimeUnit时间单位:参数keepAliveTime的时间单位;
参数5BlockingQueue阻塞工作队列:用来存储等待执行的任务;
参数6ThreadFactory线程工厂 : 用于创建线程,以及自定义线程名称,需要实现ThreadFactory接口;
参数7RejectedExecutionHandler拒绝策略:当线程池线程内的线程耗尽,并且工作队列达到已满时,新提交的任务,将使用拒绝策略进行处理;

四种拒绝策略

ThreadPoolExecutor.AbortPolicy:默认策略,丢弃任务并抛出RejectedExecutionException异常;
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常;
ThreadPoolExecutor.DiscardOldestPolicy:丢弃工作队列中的队头任务(即最旧的任务,也就是最早进入队列的任务)后,继续将当前任务提交给线程池;
ThreadPoolExecutor.CallerRunsPolicy:由原调用线程处理该任务 (谁调用,谁处理);

线程池的优点

1.提高响应速度
2.降低线程的可管理行
3.降低资源消耗(线程执行完后,不销毁,可以执行其他的任务)

创建线程池的方式

创建无上限的线程池
线程池是一个可以复用线程的技术
ExecutorService
方式1:Executors工具类创建线程对象
FixedThreadPool

package day31_线程池;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo02 {
	public static void main(String[] args) throws InterruptedException {
		// 获取有上限的线程池对象
		// 计算密集型任务:核心线程数量=CPU核数+1
		// IO密集型任务:CPU*2
		ExecutorService pool1 = Executors.newFixedThreadPool(3);
		// 提交任务
		pool1.submit(new MyRunnable());
		Thread.sleep(1000);
		System.out.println("=========1=========");
		pool1.submit(new MyRunnable());
		Thread.sleep(1000);
		System.out.println("=========2=========");
		pool1.submit(new MyRunnable());
		Thread.sleep(1000);
		System.out.println("=========3=========");
		pool1.submit(new MyRunnable());
		Thread.sleep(1000);
		System.out.println("=========4=========");
		pool1.submit(new MyRunnable());

		pool1.shutdown();
	}
}

​ CachedThreadPool

package day31_线程池;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

//创建无上限的线程池
//线程池是一个可以复用线程的技术
//ExecutorService
//方式1:Executors工具类创建线程对象
public class Demo01 {
	public static void main(String[] args) throws InterruptedException {

		// 获取线程池
		ExecutorService pool = Executors.newCachedThreadPool();
		// 提交线程任务
		pool.submit(new MyRunnable());
		pool.submit(new MyRunnable());
		pool.submit(new MyRunnable());
		pool.submit(new MyRunnable());
		Thread.sleep(1000);// 线程休眠1秒,体现复用
		System.out.println("============");
		pool.submit(new MyRunnable());

		// 关闭线程池
		pool.shutdown();
	}
}

​ SingleThreadExecutor

package day32_线程池;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo04 {
	public static void main(String[] args) {
		//单例线程池 单独线程的ThreadPoolExecutor对象
		ExecutorService pool = Executors.newSingleThreadExecutor();
		
		pool.execute(new MyRun());
		pool.execute(new MyRun());
		pool.execute(new MyRun());
		pool.execute(new MyRun());
		pool.execute(new MyRun());
	}

}

​ ScheduledThreadPool

package day32_线程池;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Demo05 {
	public static void main(String[] args) {
		// 能实现定时、周期性任务的线程池
		Runnable run = new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName());
			}
		};
		ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
		// 延时5秒执行任务
		// 参数1:任务 2:延时多久 3:时间单位
//		pool.schedule(run, 5, TimeUnit.SECONDS);

		// 延时5秒执行此任务,后每过3秒重复执行此任务
		pool.scheduleAtFixedRate(run, 5, 3, TimeUnit.SECONDS);
		
		pool.shutdown();
	}
}

方式2:ThreadPoolExecutor

package day32_线程池;

public class MyRun implements Runnable {
	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName() + "正在执行");
//		try {
//			//线程执行的时间长一点,便于结果的观察
//			Thread.sleep(Integer.MAX_VALUE);
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
	}
}
package day32_线程池;

import java.util.concurrent.Callable;

public class MyCall implements Callable<Integer> {
	private int n;

	public MyCall(int n) {
		this.n = n;
	}

	@Override
	public Integer call() throws Exception {
		int sum = 0;
		for (int i = 0; i <= n; i++) {
			sum += i;
		}
		return sum;
	}

}

01

package day32_线程池;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
//创建无上限的线程池
//线程池是一个可以复用线程的技术
//ExecutorService
//方式1:Executors工具类创建线程对象
//	FixedThreadPool
//	CachedThreadPool
//	SingleThreadExecutor
//	ScheduledThreadPool
//方式2:ThreadPoolExecutor
//参数1:int corePoolSize, 指定线程池的核心线程数量   正式员工
//参数2:int maximumPoolSize, 指定线程池的最大线程数量  正式员工  临时工
//参数3:long keepAliveTime,  指定临时线程的最大存活时间  临时工空闲多久被开除
//参数4:TimeUnit unit, 临时线程最大的存活时间单位
//参数5:BlockingQurue workQueue,  指定线程池的任务队列(客户排队的地方)
//参数6:ThreadFactory threadFactory, 指定线程工厂(负责招聘员工的hr)
//参数7:RejectedExecutionHandler handler  线程池的拒绝策略

//四种拒绝策略:
//AbortPolicy() 丢弃当前线程任务并抛出异常,是默认的策略
//DiscardPolicy() 丢弃当前线程任务但不抛出异常[不推荐的做法]
//DiscardOldestPolicy() 丢弃等待队列中的最老的线程任务,然后将当前任务添加到队列中
//CallerRunsPolicy() 直接由"当前调用线程"直接执行该线程任务,绕过线程池直接执行

//线程池优点:
//1.提高响应速度
//2.降低线程的可管理行
//3.降低资源消耗(线程执行完后,不销毁,可以执行其他的任务)
public class Demo01 {
	public static void main(String[] args) throws InterruptedException {
//		参数1:int corePoolSize, 指定线程池的核心线程数量   正式员工
//		参数2:int maximumPoolSize, 指定线程池的最大线程数量  正式员工  临时工
//		参数3:long keepAliveTime,  指定临时线程的最大存活时间  临时工空闲多久被开除
//		参数4:TimeUnit unit, 临时线程最大的存活时间单位
//		参数5:BlockingQurue workQueue,  指定线程池的任务队列(客户排队的地方)
//		参数6:ThreadFactory threadFactory, 指定线程工厂(负责招聘员工的hr)
//		参数7:RejectedExecutionHandler handler  线程池的拒绝策略
		ExecutorService pool1 = new ThreadPoolExecutor(3, 6, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3),
				Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
		
		// 2.提交任务
		MyRun run = new MyRun();
		pool1.submit(run);// 提交任务给线程池对象,线程池会自动的创建一个新的线程,自动处理任务
		pool1.submit(run);
		pool1.submit(run);

		pool1.submit(run);// 提交第4个任务时,没有达到临时线程的创建条件,复用核心线程,执行此任务
		pool1.submit(run);// 提交第5个任务时,没有达到临时线程的创建条件,复用核心线程,执行此任务
		pool1.submit(run);// 提交第6个任务时,核心线程满,队列也满

//		Thread.sleep(1000);//任务短

		pool1.submit(run);// 提交第7个任务时,创建临时线程1
		pool1.submit(run);// 提交第8个任务时,创建临时线程2
		pool1.submit(run);// 提交第9个任务时,创建临时线程3 此时核心线程满,队列也满,临时线程满

		pool1.submit(run);// 再来新的任务会进行拒绝策略
		pool1.submit(run);// 再来新的任务会进行拒绝策略
		pool1.submit(run);// 再来新的任务会进行拒绝策略

		// 等线程池中的内容执行完毕关闭线程池,不接收新任务,但仍处理工作队列中的任务
		pool1.shutdown();

		// 立即关闭线程池,不接收新任务,同时中断正在执行的任务
//		pool1.shutdownNow();
	}
}

02

package day32_线程池;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
//创建无上限的线程池
//线程池是一个可以复用线程的技术
//ExecutorService
//方式1:Executors工具类创建线程对象
//方式2:ThreadPoolExecutor

//四种拒绝策略:
//AbortPolicy() 丢弃当前线程任务并抛出异常,是默认的策略
//DiscardPolicy() 丢弃当前线程任务但不抛出异常[不推荐的做法]
//DiscardOldestPolicy() 丢弃等待队列中的最老的线程任务,然后将当前任务添加到队列中
//CallerRunsPolicy() 直接由"当前调用线程"直接执行该线程任务,绕过线程池直接执行

//线程池优点:
//1.提高响应速度
//2.降低线程的可管理行
//3.降低资源消耗(线程执行完后,不销毁,可以执行其他的任务)
public class Demo02 {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
//		参数1:int corePoolSize, 指定线程池的核心线程数量   正式员工
//		参数2:int maximumPoolSize, 指定线程池的最大线程数量  正式员工  临时工
//		参数3:long keepAliveTime,  指定临时线程的最大存活时间  临时工空闲多久被开除
//		参数4:TimeUnit unit, 临时线程最大的存活时间单位
//		参数5:BlockingQurue workQueue,  指定线程池的任务队列(客户排队的地方)
//		参数6:ThreadFactory threadFactory, 指定线程工厂(负责招聘员工的hr)
//		参数7:RejectedExecutionHandler handler  线程池的拒绝策略
		ExecutorService pool1 = new ThreadPoolExecutor(3, 6, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3),
				Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

		// 2.提交任务
		Future f1 = pool1.submit(new MyCall(5));
		Future f2 = pool1.submit(new MyCall(100));
		Future f3 = pool1.submit(new MyCall(200));
		Future f4 = pool1.submit(new MyCall(300));
		Future f5 = pool1.submit(new MyCall(400));
		Future f6 = pool1.submit(new MyCall(500));
		System.out.println(f1.get());
		System.out.println(f2.get());
		System.out.println(f3.get());
		System.out.println(f4.get());

		pool1.execute(new MyRun());
		pool1.execute(new MyRun());
		pool1.execute(new MyRun());
		

		// 等线程池中的内容执行完毕关闭线程池,不接收新任务,但仍处理工作队列中的任务
		pool1.shutdown();
	}
}

03

package day32_线程池;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
//创建无上限的线程池
//线程池是一个可以复用线程的技术
//ExecutorService
//方式1:Executors工具类创建线程对象
//方式2:ThreadPoolExecutor

//四种拒绝策略:
//AbortPolicy() 丢弃当前线程任务并抛出异常,是默认的策略
//DiscardPolicy() 丢弃当前线程任务但不抛出异常[不推荐的做法]
//DiscardOldestPolicy() 丢弃等待队列中的最老的线程任务,然后将当前任务添加到队列中
//CallerRunsPolicy() 直接由"当前调用线程"直接执行该线程任务,绕过线程池直接执行

//线程池优点:
//1.提高响应速度
//2.降低线程的可管理行
//3.降低资源消耗(线程执行完后,不销毁,可以执行其他的任务)
public class Demo03 {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
//		参数1:int corePoolSize, 指定线程池的核心线程数量   正式员工
//		参数2:int maximumPoolSize, 指定线程池的最大线程数量  正式员工  临时工
//		参数3:long keepAliveTime,  指定临时线程的最大存活时间  临时工空闲多久被开除
//		参数4:TimeUnit unit, 临时线程最大的存活时间单位
//		参数5:BlockingQurue workQueue,  指定线程池的任务队列(客户排队的地方)
//		参数6:ThreadFactory threadFactory, 指定线程工厂(负责招聘员工的hr)
//		参数7:RejectedExecutionHandler handler  线程池的拒绝策略

		// 参数6:线程工厂自定义线程名称
		ThreadFactory factory = new ThreadFactory() {
			private int count = 1;

			@Override
			public Thread newThread(Runnable r) {
				Thread thread = new Thread(r, "线程名-" + count++);
				return thread;
			}
		};
		// 参数7:线程池的拒绝策略定义
		RejectedExecutionHandler policy = new RejectedExecutionHandler() {
			@Override
			public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
				System.out.println("任务" + r + "线程满队列满不能执行了");
			}
		};
		ExecutorService pool1 = new ThreadPoolExecutor(3, 6, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3), factory,
				policy);

		// 2.提交任务
		MyRun run = new MyRun();
		pool1.submit(run);// 提交任务给线程池对象,线程池会自动的创建一个新的线程,自动处理任务
		pool1.submit(run);
		pool1.submit(run);

		pool1.submit(run);// 提交第4个任务时,没有达到临时线程的创建条件,复用核心线程,执行此任务
		pool1.submit(run);// 提交第5个任务时,没有达到临时线程的创建条件,复用核心线程,执行此任务
		pool1.submit(run);// 提交第6个任务时,核心线程满,队列也满

//		Thread.sleep(1000);//任务短

		pool1.submit(run);// 提交第7个任务时,创建临时线程1
		pool1.submit(run);// 提交第8个任务时,创建临时线程2
		pool1.submit(run);// 提交第9个任务时,创建临时线程3 此时核心线程满,队列也满,临时线程满

		pool1.submit(run);// 再来新的任务会进行拒绝策略
		// 等线程池中的内容执行完毕关闭线程池,不接收新任务,但仍处理工作队列中的任务
		pool1.shutdown();
	}
}

网站公告

今日签到

点亮在社区的每一天
去签到