Java多线程学习笔记
1.程序:一段静态的代码。
进程:程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程,有它自身的产生,存在和消亡的过程。
线程:进程可进一步细化为线程,是程序内部的一个执行路径。
2.线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc)。 一个进程可能有多个线程,多个线程共享相同的内存单元,如图,方法区内是 static的静态方法,堆内是 创建的对象。
3. 一个进程的多个线程共享相同的内存单元,从一个堆中分配对象,可以访问相同的变量和对象,这就使得线程之间的通信更加简便高效。但多个线程操作共享的系统资源可能会带来安全隐患。
4.单核CPU:假多线程,即在一个时间单元内,只执行了一个线程的任务。例如:一个厨师同时做多个菜,只能分配给每一道菜部分时间,最后完成。 因为CPU的时间单元很短,因此感觉不到。
- 一个Java应程序java.exe,至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。
6.并行与并发
并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀,多个人做一件事。
7.线程创建方式一
package com.meiya.java;
/**
* 多线程创建 方式1:继承Thread类
* 步骤:
* 1. 创建一个继承于Thread类的子类
* 2. 重写Thread类的run()方法 --将此线程执行的操作声明在run中
* 3. 创建Thread类的子类的对象
* 4. 通过此对象调用start() 方法
*
* eg: 遍历100以内的偶数
*
*/
// 1. 创建一个继承于Thread类的子类
class MyThread extends Thread{
// 2. 重写Thread类的run()方法
public void run(){
for (int i = 0; i < 10000; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName() + i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
// 3. 创建Thread类的子类的对象 alt+enter
MyThread myThread = new MyThread();
// 4. 通过此对象调用start() 方法 :1.启动当前线程 2.调用当前的run方法
myThread.start();
// 问题1:如果不通过start 直接调用run ,run会执行,但不会新建一个线程,操作还是main主线程实现
// myThread.run()
// 问题2:不可以让 已经start的线程再去start(即同一个thread),会报异常 IllegalThreadStateException
// myThread.start();
// 需要重新创建一个Thread对象 start
MyThread myThread1 = new MyThread();
myThread1.start();
for (int i = 0; i <10000 ; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
}
8.线程常用方法
package com.meiya.java;
/**
*
* 测试 Thread 中的 常用方法:
* 1. start():启动当前线程;调用当前线程的run()
* 2. run(): 通常需要重写Thread 类中的此方法,将创建的线程要执行的操作声明在此方法中
* 3. currentThread():静态方法,返回执行当前代码的线程
* 4. getName(): 获取当前线程的名字
* 5. setName(): 设置当前线程的名字
* 6. yield(): 释放当前cpu的执行权。当前排队的线程进行重新争夺cpu执行权。
* 7. join(): 在线程a中调用线程b的join(),此时a线程进入到阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态。
* 8. stop(): 已过时,当执行此方法时,强制结束当前线程。
* 9. sleep(long millitime) : 让当前线程“睡眠” 指定的,millitime毫秒数,在指定的millitime毫秒时间内,
* 线程是阻塞状态。
* 10. isAlive() : 判断当前线程是否存活。
*
*
* 线程的优先级:
* 1. MAX_PRIORITY: 10
* 2. MIN_PRIORITY: 1
* 3. NORM_PRIORITY: 5 默认优先级
*
* 2.获取和设置当前线程的优先级
* getPriority: 获取线程的优先级
* setPriority(int i): 设置线程的优先级
*
* 说明:高优先级的线程要抢占低优先级线程的cpu的执行权,但是只是从概率上讲,
* 高优先级更容易抢占到cpu的执行权,并不是只有高优先级执行完成,才执行低优先级。
*
*
*/
class ThreadByTest extends Thread{
public void run(){
System.out.println(Thread.currentThread().getName() + ":的优先级是" +Thread.currentThread().getPriority());
for (int i = 0; i < 100 ; i++) {
if(i % 2 ==0){
System.out.println(Thread.currentThread().getName()+ ":" + i);
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// if(i % 20 ==0){
// yield();
// }
}
}
}
public class ThreadMethodTest {
public static void main(String[] args) {
ThreadByTest threadByTest = new ThreadByTest();
threadByTest.setName("线程1");
System.out.println(Thread.currentThread().getName() + ":的优先级是" +Thread.currentThread().getPriority());
// 设置分线程的优先级
//threadByTest.setPriority(10);
threadByTest.start();
// 给主线程命名
Thread.currentThread().setName("主线程");
for (int i = 0; i < 100 ; i++) {
if(i % 2 != 0){
System.out.println(Thread.currentThread().getName()+ ":" + i);
}
if(i == 20 ){
System.out.println(threadByTest.isAlive());
try {
threadByTest.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
ps:
run()方法内使用sleep()需要try/catch,而不能采用直接Throws抛出,原因是Thread的run()方法没有抛出异常,因此继承Thread的子类不可以抛出比父类范围大的异常。
9.线程的调度
调度方法:
1.同优先级线程组成先进先出队列(先到先服务),使用时间片策略
2.对高优先级,使用优先调度抢占式策略
10.通过实现Runnable接口创建线程
package com.meiya.java;
/**
*
* 创建多线程的方式二: 实现Runnable接口
* 1. 创建一个实现了Runnable接口的类
* 2,实现类去实现Runnable 的抽象方法:run()
* 3. 创建实现类的对象
* 4.将此对象作为参数传递到Thread类的构造器中,创建Thread对象
* 5.通过Thread类的对象调用start()
*
*
* 比较创建线程的两种方式:
* 开发中优先选择实现Runnable接口方式
* 原因:1.单继承多实现,单继承有局限性,相对宝贵
* 2.实现的方式更适合处理多个线程有共享数据的情况。
*
* 相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在Run()
* 中
*/
// 1. 创建一个实现了Runnable接口的类
class ThreadRunnable implements Run nable {
// 2,实现类去实现Runnable 的抽象方法:run()
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+":"+ i);
}
}
}
public class ThreadByRunnable {
public static void main(String[] args) {
// 3. 创建实现类的对象
ThreadRunnable threadRunnable = new ThreadRunnable();
// 4.将此对象作为参数传递到Thread类的构造器中,创建Thread对象
Thread thread = new Thread(threadRunnable);
// 5.通过Thread类的对象调用start()
thread.setName("线程1");
thread.start();
// 再创建一个线程
Thread thread1 = new Thread(threadRunnable);
thread1.setName("线程2");
thread1.start();
}
}
线程通信:wait() / notify() / notifyAll() : 此三个方法定义在Object类中。
线程分类:
Java中线程分为两类:一种是守护线程,一种是用户线程。
1.它们在几乎各个方面都是相同的,唯一区别是判断JVM何时离开。
2.守护线程是用来服务用户线程的,通过在start()方法前调用thread.setDaemon(true) 可以把一个用户线程变成一个守护线程。
3.Java垃圾回收就是一个典型的守护线程。
4.若JVM中都是守护线程,当前JVM将退出。
线程的生命周期
线程同步:
问题:如以上的卖票问题,在卖票的多线程程序执行中,出现了 重票,错票 ,的线程安全问题。
问题出现原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来。
如何解决:当一个线程a在操作共享资源(ticket)时候,其他线程不能参与进来,直到线程a操作完ticket时,其他线程才可以开始操作ticket,这种情况即使a线程进入阻塞状态,也不能被改变。
在Java中,我们通过同步机制,来解决线程安全的问题。
方式一:同步代码块
synchronized(同步监视器){
// 需要被同步的代码
}
说明: 1.操作共享数据的代码,即为需要被同步的代码
2.共享数据:多个线程共同操作的变量。如:ticket就是共享数据。
3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。
要求:多个线程必须要共用一把锁。
补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
package com.meiya.java;
/**
*
* 创建多线程的方式二: 实现Runnable接口
* 1. 创建一个实现了Runnable接口的类
* 2,实现类去实现Runnable 的抽象方法:run()
* 3. 创建实现类的对象
* 4.将此对象作为参数传递到Thread类的构造器中,创建Thread对象
* 5.通过Thread类的对象调用start()
*
*
* 比较创建线程的两种方式:
* 开发中优先选择实现Runnable接口方式
* 原因:1.单继承多实现,单继承有局限性,相对宝贵
* 2.实现的方式更适合处理多个线程有共享数据的情况。
*
* 相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在Run()
* 中
*
* // 补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this 充当同步监视器。
*
*/
// 1. 创建一个实现了Runnable接口的类
class ThreadRunnable implements Runnable {
private int ticket = 100;
Object object = new Object();
// 2,实现类去实现Runnable 的抽象方法:run()
public void run() {
while(true){
synchronized (object){
if(ticket > 0){
System.out.println(Thread.currentThread().getName() + ":票号为" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
public class ThreadByRunnable {
public static void main(String[] args) {
// 3. 创建实现类的对象
ThreadRunnable threadRunnable = new ThreadRunnable();
// 4.将此对象作为参数传递到Thread类的构造器中,创建Thread对象
Thread thread = new Thread(threadRunnable);
// 5.通过Thread类的对象调用start()
thread.setName("线程1");
thread.start();
// 再创建一个线程
Thread thread1 = new Thread(threadRunnable);
thread1.setName("线程2");
thread1.start();
}
}
方式二:同步方法
如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。
关于同步方法的总结:
1.同步方法仍然涉及到同步监视器,只是不需要我们显示的声明。
2.非静态的同步方法,同步监视器是:this
静态的同步方法,同步监视器是:当前类本身
package com.meiya.java;
import static java.lang.Thread.sleep;
/**
*
* 使用同步方法解决实现Runnable接口的线程安全问题
*
*
*/
class ThreadRunnable1 implements Runnable {
private int ticket = 100;
// 2,实现类去实现Runnable 的抽象方法:run()
public void run() {
while(true){
show();
}
}
private synchronized void show(){ //同步监视器 this
if(ticket > 0){
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":票号为" + ticket);
ticket--;
}
}
}
public class ThreadByTongBuMethod {
public static void main(String[] args) {
// 3. 创建实现类的对象
ThreadRunnable1 threadRunnable = new ThreadRunnable1();
// 4.将此对象作为参数传递到Thread类的构造器中,创建Thread对象
Thread thread = new Thread(threadRunnable);
// 5.通过Thread类的对象调用start()
thread.setName("线程1");
thread.start();
// 再创建一个线程
Thread thread1 = new Thread(threadRunnable);
thread1.setName("线程2");
thread1.start();
}
}
同步的优缺点:
1.同步的方式,解决了线程的安全问题。—好处
2.操作同步代码时,只有一个线程参与,其他线程等待,相当于一个单线程的过程,效率低。 —局限性(但必须接受)
线程的死锁问题
1.死锁
a.不同的线程分别占用对方需要的同步资源不放弃,都在等对方放弃自己需要的同步资源,就形成了线程的死锁。
b.出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续。
2.解决办法
a.专门的算法,原则
b.尽量减少同步资源的定义
c.尽量避免嵌套同步
package com.meiya.java;
import static java.lang.Thread.sleep;
/**
*
* 线程的死锁问题
* 1.死锁
* a.不同的线程分别占用对方需要的同步资源不放弃,都在等对方放弃自己需要的同步资源,就形成了线程的死锁。
* b.出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续。
*
* 2. 说明
* 1).出现死锁后,不会出现异常,不会出现提示,只是所有的下称都处于阻塞状态,无法继续
* 2).我们使用同步时,要避免出现死锁。
*
*/
public class DeadLockTest {
public static void main(String[] args) {
final StringBuffer stringBuffer1 = new StringBuffer();
final StringBuffer stringBuffer2 = new StringBuffer();
new Thread(){
public void run(){
synchronized (stringBuffer1){
stringBuffer1.append("a");
stringBuffer2.append("1");
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (stringBuffer2){
stringBuffer1.append("b");
stringBuffer2.append("2");
System.out.println(stringBuffer1);
System.out.println(stringBuffer2);
}
}
}
}.start();
new Thread(new Runnable() {
public void run() {
synchronized (stringBuffer2){
stringBuffer1.append("a");
stringBuffer2.append("1");
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (stringBuffer1){
stringBuffer1.append("b");
stringBuffer2.append("2");
System.out.println(stringBuffer1);
System.out.println(stringBuffer2);
}
}
}
}).start();
}
}
解决线程安全问题的方式三:Lock锁
package com.meiya.java;
import java.util.concurrent.locks.ReentrantLock;
import static java.lang.Thread.sleep;
/**
*
* 解决线程安全问题的方式三:Lock锁 --JDK5.0新增
*
* 1. 面试题:synchronized 与 lock 异同 ?
* 同: 都是用来解决线程安全问题
* 不同:synchronized在执行完同步代码块以后,自动释放同步监视器
* lock需要手动的启动同步(lock()),手动结束同步(unlock())
*
* 2.优先使用顺序
* Lock -> 同步代码块(已经进入方法体,分配了相应 资源) -> 同步方法(在方法体之外)
*
* 3. 面试题:如何解决线程安全问题?有几种?
* 1)synchronized 同步代码块,同步方法
* 2)lock锁
*
*
*
*/
class Window implements Runnable{
private int ticket = 100;
// 实例化ReentrantLock
private ReentrantLock lock = new ReentrantLock();
public void run() {
while (true) {
try {
// 调用锁定lock方法
lock.lock();
if(ticket > 0){
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+ticket);
ticket--;
}else{
break;
}
}finally {
// 调用解锁方法:unlock
lock.unlock();
}
}
}
}
public class LockTest {
public static void main(String[] args) {
Window window = new Window();
Thread window1 = new Thread(window);
Thread window2 = new Thread(window);
Thread window3 = new Thread(window);
window1.setName("窗口1");
window2.setName("窗口2");
window3.setName("窗口3");
window1.start();
window2.start();
window3.start();
}
}
线程通信案例
package com.meiya.java;
import static java.lang.Thread.sleep;
/**
*
* 线程通信的例子:是同两个线程打印1-100 ,线程1,线程2 交替打印
*
* 涉及到线程通信的3个方法:
* wait(): 一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
* notify(): 一旦执行此方法,就会唤醒被wait()的一个线程,如果多个线程被wait()就唤醒优先级高的。
* notifyAll(): 一旦执行此方法,就会唤醒所有被wait()的线程。
*
* 说明:
* 1.wait(),notify(),notifyAll() 必须使用在 同步代码块 或 同步方法 中。
* 2.wait(),notify(),notifyAll() 调用者必须是同步代码块或同步方法的同步监视器。
* 否则会出现IllegalMonitorStateException异常。
* 3.wait(),notify(),notifyAll() 三个方法定义在java.lang.Object类中。
*
*
* 面试题:sleep()和wait()方法异同?
* 相同点:
* 1.都会使线程进入阻塞状态
* 不同点:
* 1.两个方法声明的位置不同:Thread类中声明的sleep(),Object中声明wait()
* 2.调用范围不同:sleep()可以在任何需要的场景下调用。wait()必须在同步代码块或同步方法中。
* 2.关于是否释放同步监视器:sleep()不会交出同步监视器,wait()会交出同步监视器。
*/
class Number implements Runnable{
private int number = 1;
private Object object = new Object();
public void run() {
while (true){
synchronized (object){
object.notify();
if(number <= 100){
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+number);
number++;
try {
// 使得调用如下wait()方法的线程进入阻塞状态,并释放锁
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}
}
}
}
public class ThreadCommunicationTest {
public static void main(String[] args) {
Number number = new Number();
Thread thread1 = new Thread(number);
Thread thread2 = new Thread(number);
thread1.setName("线程1");
thread2.setName("线程2");
thread1.start();
thread2.start();
}
}
线程通信经典案例:生产者,消费者,店员
package com.meiya.java;
import static java.lang.Thread.sleep;
/**
*
* 线程通信的应用:经典例题:生产者、消费者问题
*
* 生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,
* 店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫
* 生产者停一下,如果店中有空位放产品再通知生产者继续生产;如果店里没有产品了,店员会
* 告诉消费者等一下,如果店里有产品了再通知消费者来取走产品;
*
* 分析:
* 1. 是否多线程问题?是 生产者线程,消费者线程
* 2. 是否有共享数据?是,店员(产品)
* 3. 如何解决线程安全问题? 同步机制
* 4. 如何解决 等待操作: wait() notify()
*
*/
// 生产者
class Product implements Runnable{
private Clerk clerk;
public Product(Clerk clerk) {
this.clerk = clerk;
}
public void run() {
System.out.println(Thread.currentThread().getName() + ":开始生产产品...");
while (true){
synchronized (clerk){
// 解除 正在wait的 消费者线程
clerk.notify();
// 产品数量低于20
if(clerk.getProduct() <20){
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 生产者生产1件商品
clerk.setProduct(clerk.getProduct()+1);
System.out.println(Thread.currentThread().getName() + ":生产1件产品,当前剩余产品"+clerk.getProduct());
}else { // 产品 >= 20 生产者停止生产,等待不足20时再被唤醒
try {
System.out.println(Thread.currentThread().getName() + ":无法生产产品,当前产品已满,数量为:"+clerk.getProduct());
clerk.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
// 消费者
class Customer implements Runnable{
private Clerk clerk;
public Customer(Clerk clerk) {
this.clerk = clerk;
}
public void run() {
System.out.println(Thread.currentThread().getName() + ":开始消费产品...");
while (true){
synchronized (clerk){
// 解除 正在wait的 生产者线程
clerk.notify();
// 产品数量 >0
if(clerk.getProduct() >0){
try {
sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 消费者消费1件产品
clerk.setProduct(clerk.getProduct()-1);
System.out.println(Thread.currentThread().getName() + ":消费1件产品,当前剩余产品"+clerk.getProduct());
}else {
try {
System.out.println(Thread.currentThread().getName() + ":无法消费产品,当前产品已空,数量为:"+clerk.getProduct());
// 产品 <=0 消费者线程等待
clerk.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
// 店员
class Clerk {
// 产品数量
private int product;
public Clerk(int product) {
this.product = product;
}
public int getProduct() {
return product;
}
public void setProduct(int product) {
this.product = product;
}
}
public class ProductTest {
public static void main(String[] args) {
// 创建店员 产品数量为10
Clerk clerk = new Clerk(10);
// 创建生产者
Product product = new Product(clerk);
// 创建消费者
Customer customer = new Customer(clerk);
// 创建生产者线程
Thread thread1 = new Thread(product);
// 创建消费者线程
Thread thread2 = new Thread(customer);
thread1.setName("生产者");
thread2.setName("消费者");
// 开启 生产者线程
thread1.start();
// 开启 消费者线程
thread2.start();
}
}
创建线程的方式三:实现callable接口。-- JDK 5.0 新增
package com.meiya.java;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
*
* 创建线程的方式三:实现callable接口。-- JDK 5.0 新增
*
* 如何理解实现Callable接口的方式比实现Runnable接口创建多线程强大???
* 1.call()可以有返回值。
* 2.call()可以抛出异常,被外面的操作捕获,获取异常信息。
* 3.Callable是支持泛型的。
*
*
*/
// 1. 创建一个实现 Callable 的实现类
class NumThread implements Callable<Integer>{
// 2. 重写Call() 方法,将此线程需要执行的操作声明在call() 中
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100 ; i++) {
if(i%2==0){
System.out.println(i);
sum+=i;
}
}
return sum;
}
}
public class ThreadByCallable {
public static void main(String[] args) {
// 3. 创建Callable接口实现类的对象
NumThread numThread = new NumThread();
// 4. 将此Callable接口实现类的对象作为参数传递到FutureTask构造器中,用来接收 call() 的 返回值
FutureTask objectFutureTask = new FutureTask(numThread);
// 5. 将FutureTask的对象传递到Thread的构造器中
Thread thread = new Thread(objectFutureTask);
// 6. 调用start()方法
thread.start();
try {
// 7. get方法返回值即为FutureTask构造器参数Callable实现类重写call()的返回值
Object sum = objectFutureTask.get();
System.out.println(sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
创建线程的方式四:使用线程池。-- JDK 5.0 新增
**背景:**经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
**思路:**提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁,实现重复利用。类似生活中的公共交通工具。
好处:
1.提高响应速度(减少了创建新线程的时间)
2.降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
3.便于管理线程
package com.meiya.java;
import java.util.concurrent.*;
/**
*
* 创建线程的方式四:使用线程池
*
* 好处:
* 1.提高响应速度(减少了创建新线程的时间)
* 2.降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
* 3.便于管理线程
* corePoolSize : 核心池的大小
* maximumPoolSize: 最大线程数
* KeepAliveTime: 线程没有任务时最多保持多长时间后停止
* ...
*
* 面试题: 创建多线程几种方式? 4种
*
*/
class NumberThread implements Runnable{
public void run() {
for (int i = 0; i < 100 ; i++) {
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":"+ i);
}
}
}
}
class Number1Thread implements Runnable{
public void run() {
for (int i = 0; i < 100 ; i++) {
if(i % 2 != 0){
System.out.println(Thread.currentThread().getName() + ":"+ i);
}
}
}
}
class Number2Thread implements Callable{
public Object call() throws Exception {
int sum = 0;
for (int i = 0; i < 100 ; i++) {
if(i % 2 == 0){
sum+=i;
}
}
// System.out.println(Thread.currentThread().getName() + ":" + sum);
return sum;
}
}
public class ThreadByThreadPool {
public static void main(String[] args) {
// 1.提供指定线程数量的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
ThreadPoolExecutor executorService1 = (ThreadPoolExecutor)executorService;
// 设置线程池属性
//System.out.println(executorService.getClass());
executorService1.setCorePoolSize(15);
// 2.执行指定的线程的操作,需要提供实现 Runnable接口 或 Callable接口 实现类的对象
// executorService.execute() 适合使用于Runnable
executorService.execute( new NumberThread());
executorService.execute( new Number1Thread());
// 适合使用于Callable
Future future = executorService.submit(new Number2Thread());
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
// 3.关闭线程池
executorService.shutdown();
}
}