长沙需要做网站的企业,深圳网站建设 信科便宜,深圳标识制作公司,wordpress设置域名ip地址本篇讲解java多线程
基本概念#xff1a; 程序、进程、线程
**程序(program)**是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码#xff0c;静态对象。
**进程(process)**是程序的一次执行过程#xff0c;或是正在运行的一个程序。是一个动态的过程…
本篇讲解java多线程
基本概念 程序、进程、线程
**程序(program)**是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码静态对象。
**进程(process)**是程序的一次执行过程或是正在运行的一个程序。是一个动态的过程有它自身的产生、存在和消亡的过程。 ——生命周期 如运行中的QQ运行中的MP3播放器程序是静态的进程是动态的进程作为资源分配的单位 系统在运行时会为每个进程分配不同的内存区域 线程(thread)进程可进一步细化为线程是一个程序内部的一条执行路径。 若一个进程同一时间并行执行多个线程就是支持多线程的 线程作为调度和执行的单位每个线程拥有独立的运行栈和程序计数器(pc)线程切换的开销小 一个进程中的多个线程共享相同的内存单元/内存地址空间 -- 它们从同一堆中分配对象可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。 单核CPU和多核CPU的理解
单核CPU其实是一种假的多线程因为在一个时间单元内也只能执行一个线程的任务。例如虽然有多车道但是收费站只有一个工作人员在收费只有收了费才能通过那么CPU就好比收费人员。如果有某个人不想交钱 那么收费人员可以把他“挂起”晾着他等他想通了准备好了钱再去收费 。 但是因为CPU时间单元特别短因此感觉不出来。如果是多核的话才能更好的发挥多线程的效率。现在的服务器都是多核的一个Java应用程序java.exe其实至少有三个线程 main()主线程 gc()垃圾回收线程异常处理线程。当然如果发生异常会影响主线程。
并行与并发
并行 多个CPU同时执行多个任务。比如多个人同时做不同的事。并发 一个CPU(采用时间片)同时执行多个任务。比如秒杀、多个人做同一件事。
使用多线程的优点 提高应用程序的响应。对图形化界面更有意义可增强用户体验。 提高计算机系统CPU的利用率 改善程序结构。将既长又复杂的进程分为多个线程独立运行利于理解和修改
线程的创建和使用
Java语言的JVM允许程序运行多个线程它通过java.lang.Thread类来体现。 Thread类的特性 每个线程都是通过某个特定Thread对象的run()方法来完成操作的经常把run()方法的主体称为线程体 通过该Thread对象的start()方法来启动这个线程而非直接调用run()
Thread类
Thread类的使用
构造器
Thread() 创建新的Thread对象
Thread(String threadname) 创建线程并指定线程实例名
Thread(Runnable target) 指定创建线程的目标对象它实现了Runnable接口中的run方法
Thread(Runnable target, String name) 创建新的Thread对象 创建线程的两种方式 JDK1.5之前创建新执行线程有两种方法 继承Thread类的方式 定义子类继承Thread类。 子类中重写Thread类中的run方法。 创建Thread子类对象即创建了线程对象。 调用线程对象start()方法启动线程调用run方法 //1. 创建一个继承于Thread类的子类
class MyThread extends Thread {//2. 重写Thread类的run()Overridepublic void run() {for (int i 0; i 100; i) {if(i % 2 0){System.out.println(Thread.currentThread().getName() : i);}}}
}//另一个类中
public static void main(String[] args) {//3. 创建Thread类的子类的对象MyThread t1 new MyThread();//4.通过此对象调用start():①启动当前线程 ② 调用当前线程的run()t1.start();
}注意 调用start()方法如果调用的是run()并不会开启新的线程而是当前线程直接执行内部的代码和之前定义方法然后让对象调用是一样的。如果想再生成一个线程那就再new一个线程对象。不可以还让已经start()的线程去执行。会报IllegalThreadStateException 使用匿名子类简化创建方式 new Thread(){public void run(){for (int i 0; i 100; i) {System.out.println(Thread.currentThread().getName() : i);}}
}.start();实现Runnable接口的方式 创建一个实现了Runnable接口的类实现类去实现Runnable中的抽象方法run()创建实现类的对象将此对象作为参数传递到Thread类的构造器中创建Thread类的对象通过Thread类的对象调用start() class MyThreadObj implements Runnable{//1. 创建一个实现了Runnable接口的类Overridepublic void run() {//2.实现类去实现Runnable中的抽象方法run()for (int i 0; i 100; i) {System.out.println(Thread.currentThread().getName() : i);}}
}public class ThreadTest {public static void main(String[] args) {//3.创建实现类的对象MyThreadObj myThreadObj new MyThreadObj();//4.将此对象作为参数传递到Thread类的构造器中创建Thread类的对象Thread thread new Thread(myThreadObj);thread.setName(hengxing);thread.start();//5.通过Thread类的对象调用start()}
}比较创建线程的两种方式 开发中优先选择–实现Runnable接口的方式 原因 实现的方式没有类的单继承性的局限性实现的方式更适合来处理多个线程有共享数据的情况。 联系public class Thread implements Runnablethread其实也是实现了Runnable接口实际上和第二种方式没区别 相同点两种方式都需要重写run()将线程要执行的逻辑声明在run()中。
Thread类的常用方法
方法作用void start()启动线程并执行对象的run()方法run()线程在被调度时执行的操作String getName()返回线程的名称void setName(String name)设置该线程名称static Thread currentThread()返回当前线程。在Thread子类中就是this通常用于主线程和Runnable实现类static void yield()线程让步- 暂停当前正在执行的线程把执行机会让给优先级相同或更高的线程- 若队列中没有同优先级的线程忽略此方法join()当某个程序执行流中调用其他线程的join()方法时 调用线程将被阻塞直到join()方法加入的 join 线程执行完为止低优先级的线程也可以获得执行但是要注意执行前确保线程已被启动。这个方法是等待join的线程完成但是你如果连线程都没有开始执行那不就直接结束了吗static void sleep(long millis)令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。抛出InterruptedException异常boolean isAlive()返回boolean判断线程是否还活着
线程的调度
调度策略
时间片 抢占式 高优先级的线程抢占CPU
Java的调度方法
同优先级线程组成先进先出队列先到先服务使用时间片策略对高优先级使用优先调度的抢占式策略
线程的优先级
MAX_PRIORITY 10MIN _PRIORITY 1NORM_PRIORITY 5 -- 默认优先级
如何获取和设置当前线程的优先级
getPriority(): 获取线程的优先级
setPriority(int p): 设置线程的优先级
说明
高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上讲高优先级的线程高概率的情况下被执行。并不意味着只有当高优先级的线程执行完以后低优先级的线程才执行。
线程创建时继承父线程的优先级
理解线程
线程的分类
Java中的线程分为两类一种是守护线程一种是用户线程
它们在几乎每个方面都是相同的唯一的区别是判断JVM何时离开守护线程是用来服务用户线程的通过在start()方法前调用thread.setDaemon(true)可以把一个用户线程变成一个守护线程Java垃圾回收就是一个典型的守护线程若JVM中都是守护线程当前JVM将退出
线程的生命周期
线程有五种状态
新建 当一个Thread类或其子类的对象被声明并创建时新生的线程对象处于新建状态就绪 处于新建状态的线程被start()后将进入线程队列等待CPU时间片此时它已具备了运行的条件只是没分配到CPU资源运行 当就绪的线程被调度并获得CPU资源时,便进入运行状态 run()方法定义了线程的操作和功能阻塞 在某种特殊情况下被人为挂起或执行输入输出操作时让出 CPU 并临时中止自己的执行进入阻塞状态死亡 线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束 关于声明周期我们需要关注两个概念 状态、相应方法 状态a -- 状态b哪些方法执行了回调方法 某个方法主动调用状态a -- 状态b 例如wait(),sleep() 阻塞只是临时状态死亡才是最终状态。程序如果一直卡在阻塞状态就是一种异常的状态。例如死锁。
线程的同步
同步是为了解决线程安全问题。先来看一个例子会更好理解
创建三个窗口卖票总票数为100张。使用实现Runnable接口的方式
public class WindowTest1 {public static void main(String[] args) {window w new window();Thread t1 new Thread(w);Thread t2 new Thread(w);Thread t3 new Thread(w);t1.setName(窗口1);t2.setName(窗口2);t3.setName(窗口3);t1.start();t2.start();t3.start();}
}class window implements Runnable{private int ticket 100;Overridepublic void run() {if (ticket 0) {break;}try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() :买票票号为 ticket--);}
}我们会发现有重票的情况出现这是因为在某个线程操作车票的过程中尚未操作完成时其他线程参与进来也操作车票。 当多条语句在操作同一个线程共享数据时一个线程对多条语句只执行了一部分还没有执行完另一个线程参与进来执行。导致共享数据的错误。这就是线程安全问题。
理想状态下三个线程会同时进入判断语句均判断票号为0跳出循环 极端状态下三个线程均进入阻塞状态结束阻塞后都执行后面的买票代码。后两个线程便会输出错票。 所以我们使用同步代码块的方式解决这个问题。
同步代码块
先来介绍同步代码块
synchronized (同步监视器){//需要被同步的代码
}操作共享数据的代码即为需要被同步的代码。 --不能包含代码多了也不能包含代码少了。 共享数据多个线程共同操作的变量。比如ticket就是共享数据。 同步监视器俗称锁。任何一个类的对象都可以充当锁。 ⭐要求多个线程必须要共用同一把锁。
在上面的例子中我们需要在操作共享数据时使用同步锁
Override
public void run() {while (true) {synchronized (this) {if (ticket 0) {break;}try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() :买票票号为 ticket--);}}
}但如果时使用继承方式实现的线程由于其生成了多个对象所以不能使用this作为当前同步的锁考虑使用window.class当前类名来作为锁类在程序中只会加载一次这个知识会在讲“反射”时提到。
Override
public void run() {while (true) {synchronized (window.class) {if (ticket 0) {break;}try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() :买票票号为 ticket--);}}
}同步方法
如果一整个方法都需要同步那不妨将方法声明为同步方法。
private synchronized void ticket(){if (ticket 0) return;try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() :买票票号为 ticket--);
}但如果是在使用继承Thread类方式实现的线程中你会发现同步锁失效了。这就是我们的另一个知识点
细心的你一定发现了同步方法没有要求我们写同步监视器那他就不存在了吗
不是的。它默认使用this代替。恰巧我们这种方式实现的线程又会生成多个对象用当前对象肯定不行。
解决方式就是将此同步方法声明为静态的这时他会使用当前类来代替this–window.class
class window extends Thread{private static int ticket 100;Overridepublic void run() {while (true) {ticket();}}private static synchronized void ticket(){if (ticket 0) {return;}try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() :买票票号为 ticket--);}
}同步的利弊
同步的方式解决了线程的安全问题。—好处
操作同步代码时只能有一个线程参与其他线程等待。相当于是一个单线程的过程效率低。 —局限性
懒汉式单例改进
之前我们写懒汉式单例提到它是线程不安全的。现在进行改进
public static Bank getInstance(){//方式一只解决线程安全效率低所有synchronized (Bank.class) {if (bank null) {bank new Bank();}return bank;}//方式二效率更高之后的线程不必在同步锁外等待if (bank null) {synchronized (Bank.class) {if (bank null) {bank new Bank();}}}return bank;
}死锁问题
不同的线程分别占用对方需要的同步资源不放弃都在等待对方放弃自己需要的同步资源就形成了线程的死锁
出现死锁后不会出现异常不会出现提示只是所有的线程都处于阻塞状态无法继续
如何解决
专门的算法、原则尽量减少同步资源的定义尽量避免嵌套同步
一个死锁的实例
class A {public synchronized void foo(B b) { //同步监视器A类的对象aSystem.out.println(当前线程名: Thread.currentThread().getName() 进入了A实例的foo方法); // ①try {Thread.sleep(200);} catch (InterruptedException ex) {ex.printStackTrace();}System.out.println(当前线程名: Thread.currentThread().getName() 企图调用B实例的last方法); // ③b.last();}public synchronized void last() {//同步监视器A类的对象aSystem.out.println(进入了A类的last方法内部);}
}class B {public synchronized void bar(A a) {//同步监视器bSystem.out.println(当前线程名: Thread.currentThread().getName() 进入了B实例的bar方法); // ②try {Thread.sleep(200);} catch (InterruptedException ex) {ex.printStackTrace();}System.out.println(当前线程名: Thread.currentThread().getName() 企图调用A实例的last方法); // ④a.last();}public synchronized void last() {//同步监视器bSystem.out.println(进入了B类的last方法内部);}
}public class DeadLock implements Runnable {A a new A();B b new B();public void init() {Thread.currentThread().setName(主线程);// 调用a对象的foo方法a.foo(b);System.out.println(进入了主线程之后);}public void run() {Thread.currentThread().setName(副线程);// 调用b对象的bar方法b.bar(a);System.out.println(进入了副线程之后);}public static void main(String[] args) {DeadLock dl new DeadLock();new Thread(dl).start();//副线程启动dl.init();//主线程启动}
}Lock(锁)
从JDK 5.0开始 Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。 锁提供了对共享资源的独占访问每次只能有一个线程对Lock对象加锁线程开始访问共享资源之前应先获得Lock对象。
ReentrantLock类实现了 Lock 它拥有与synchronized相同的并发性和内存语义 在实现线程安全的控制中比较常用的是ReentrantLock 可以显式加锁、释放锁。
注意Lock方式中并没有同步监视器这个概念但是我们可以把private ReentrantLock lock new ReentrantLock();中的lock视为同步监视器如果线程间没有使用同一个lock对象就相当于没有使用同一把锁。lock不可调用wait()、notify()、notifyAll()方法但是可以通过相关的Condition对象来实现更多操作。
使用方式
//1.实例化ReentrantLock
private ReentrantLock lock new ReentrantLock();Override
public void run() {while(true){try{//2.调用锁定方法lock()lock.lock();//需要同步的代码}finally {//3.调用解锁方法unlock()lock.unlock();}}
}因为最后一步一定要解锁所以使用try--finally的方式
synchronized 与 Lock 的对比
Lock是显式锁手动开启和关闭锁别忘记关闭锁 synchronized是隐式锁出了作用域自动释放Lock只有代码块锁 synchronized有代码块锁和方法锁使用Lock锁 JVM将花费较少的时间来调度线程性能更好。并且具有更好的扩展性提供更多的子类
推荐使用顺序
优先使用顺序 Lock -- 同步代码块已经进入了方法体分配了相应资源– 同步方法在方法体之外 练习
银行有一个账户。
有两个储户分别向同一个账户存3000元每次存1000存3次。每次存完打印账户余额。
使用继承Thread方式synchronized同步方法
public class DepositTest {public static void main(String[] args) {Account account new Account();Customer c1 new Customer(account);Customer c2 new Customer(account);c1.setName(Tom);c2.setName(Jerry);c1.start();c2.start();}
}class Account{double balance;public synchronized void deposit(double awt){if (awt 0) {return;}balance awt;try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() :sucessful deposit.The balance is balance);}
}class Customer extends Thread{Account acct;public Customer(Account acct) {this.acct acct;}Overridepublic void run() {for (int i 0; i 3; i) {acct.deposit(1000);}}
}使用继承Thread方式lock同步
public void deposit(double awt){if (awt 0) {return;}lock.lock();try {balance awt;try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() :sucessful deposit.The balance is balance);} finally {lock.unlock();}
}使用继承Runnable方式synchronized同步方法
import java.util.concurrent.locks.ReentrantLock;
public class DepositTest {public static void main(String[] args) {Account account new Account();Customer c1 new Customer(account);Customer c2 new Customer(account);Thread t1 new Thread(c1);Thread t2 new Thread(c2);t1.setName(Tom);t2.setName(Jerry);t1.start();t2.start();}
}class Account{double balance;ReentrantLock lock new ReentrantLock();public synchronized void deposit(double awt){if (awt 0) {return;}balance awt;try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() :sucessful deposit.The balance is balance);}class Customer implements Runnable{Account acct;public Customer(Account acct) {this.acct acct;}Overridepublic void run() {for (int i 0; i 3; i) {acct.deposit(1000);}}
}使用继承Runnable方式lock同步
import java.util.concurrent.locks.ReentrantLock;
public class DepositTest {public static void main(String[] args) {Account account new Account();Customer c1 new Customer(account);Customer c2 new Customer(account);Thread t1 new Thread(c1);Thread t2 new Thread(c2);t1.setName(Tom);t2.setName(Jerry);t1.start();t2.start();}
}class Account{double balance;ReentrantLock lock new ReentrantLock();public void deposit(double awt){if (awt 0) {return;}lock.lock();try {balance awt;try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() :sucessful deposit.The balance is balance);} finally {lock.unlock();}}
}class Customer implements Runnable{Account acct;public Customer(Account acct) {this.acct acct;}Overridepublic void run() {for (int i 0; i 3; i) {acct.deposit(1000);}}
}线程通信
使用两个线程打印 1-100。线程1, 线程2 交替打印
/*** 使用两个线程打印 1-100。线程1, 线程2 交替打印*/
public class CommunicationTest {public static void main(String[] args) {Number number new Number();Thread t1 new Thread(number);Thread t2 new Thread(number);t1.setName(线程一);t2.setName(线程二);t1.start();t2.start();}
}class Number implements Runnable{private int number 1;Overridepublic void run() {while (true) {synchronized (this) {notify();//将阻塞的进程唤醒if (number 100) {return;}System.out.println(Thread.currentThread().getName() : number); try {wait();//令当前进程阻塞等待唤醒。} catch (InterruptedException e) {e.printStackTrace();}}}}
}涉及到的三个方法
wait()一旦执行此方法当前线程就进入阻塞状态并释放同步监视器。
notify()一旦执行此方法就会唤醒被wait的一个线程。如果有多个线程被wait就唤醒优先级高的那个。
notifyAll()一旦执行此方法就会唤醒所有被wait的线程。
说明 wait()notify()notifyAll()三个方法必须使用在同步代码块或同步方法中lock锁的方式都不可以。意味着这三个方法是依赖于同步监视器的。 wait()notify()notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。 否则会出现IllegalMonitorStateException异常。 wait()notify()notifyAll()三个方法三个方法是定义在java.lang.Object类中。
面试题sleep() 和 wait()的异同
相同点一旦执行方法都可以使得当前的线程进入阻塞状态。
不同点 两个方法声明的位置不同 Thread类中声明sleep() Object类中声明wait() 调用的要求不同sleep()可以在任何需要的场景下调用。 wait()必须使用在同步代码块或同步方法中 关于是否释放同步监视器如果两个方法都使用在同步代码块或同步方法中sleep()不会释放锁wait()会释放锁。
线程通信的应用
经典例题生产者/消费者问题
生产者(Productor)将产品交给店员(Clerk)而消费者(Customer)从店员处取走产品店员一次只能持有固定数量的产品(比如:20如果生产者试图生产更多的产品店员会叫生产者停一下如果店中有空位放产品了再通知生产者继续生产如果店中没有产品了店员会告诉消费者等一下如果店中有产品了再通知消费者来取走产品。
分析 是否是多线程问题是生产者线程消费者线程是否有共享数据是店员或产品如何解决线程的安全问题同步机制,有三种方法是否涉及线程的通信是生产者通知消费者进行消费消费者通知生产者进行生产 public class CommunicationTest {public static void main(String[] args) {Clerk clerk new Clerk();Customer customer new Customer(clerk);Productor productor new Productor(clerk);customer.setName(customer);productor.setName(productor);productor.start();customer.start();}
}class Clerk{private int number 0;public synchronized void produce(){if (number 20) {//等待消费try {wait();} catch (InterruptedException e) {e.printStackTrace();}return;}System.out.println(Thread.currentThread().getName() 开始生产第 number 个产品);notify();//生产后唤醒消费者}public synchronized void custom(){if (number 0) {//等待生产try {wait();} catch (InterruptedException e) {e.printStackTrace();}return;}System.out.println(Thread.currentThread().getName() 开始消费第 number-- 个产品);notify();//消费后唤醒生产者}}class Productor extends Thread{Clerk clerk;public Productor(Clerk clerk) {this.clerk clerk;}Overridepublic void run() {while (true) {try {Thread.sleep(250);} catch (InterruptedException e) {e.printStackTrace();}clerk.produce();}}
}class Customer extends Thread{Clerk clerk;public Customer(Clerk clerk) {this.clerk clerk;}Overridepublic void run() {while (true) {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}clerk.custom();}}
}新增线程创建方式
JDK5.0 新增
新增方式一实现Callable接口
与使用Runnable相比 Callable功能更强大些
相比run()方法可以有返回值方法可以抛出异常支持泛型的返回值需要借助FutureTask类比如获取返回结果
实现步骤为 创建一个实现Callable的实现类 实现call方法将此线程需要执行的操作声明在call()中 创建Callable接口实现类的对象 将此Callable接口实现类的对象作为传递到FutureTask构造器中创建FutureTask的对象 将FutureTask的对象作为参数传递到Thread类的构造器中创建Thread对象并调用start() 获取Callable中call方法的返回值可选 get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
//1.创建一个实现Callable的实现类
class Number implements Callable{private int count 0;//2.实现call方法将此线程需要执行的操作声明在call()中Overridepublic Object call() throws Exception {for (int i 0; i 100; i) {if (i % 2 0){System.out.println(i);}count i;}return count;}
}public class NewThread {public static void main(String[] args) {//3.创建Callable接口实现类的对象Number number new Number();//4.将此Callable接口实现类的对象作为传递到FutureTask构造器中创建FutureTask的对象FutureTask futureTask new FutureTask(number);//5.将FutureTask的对象作为参数传递到Thread类的构造器中创建Thread对象并调用start()Thread thread new Thread(futureTask);thread.start();//6.获取Callable中call方法的返回值//get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。try {Object o futureTask.get();System.out.println(总和为 o);} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}
}如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大
call()可以有返回值的。call()可以抛出异常被外面的操作捕获获取异常的信息Callable是支持泛型的
新增方式二使用线程池
背景 经常创建和销毁、使用量特别大的资源比如并发情况下的线程对性能影响很大
思路 提前创建好多个线程放入线程池中使用时直接获取使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具
好处
提高响应速度减少了创建新线程的时间降低资源消耗重复利用线程池中线程不需要每次都创建便于线程管理 corePoolSize核心池的大小maximumPoolSize最大线程数keepAliveTime线程没有任务时最多保持多长时间后会终止…
线程池相关API
JDK 5.0起提供了线程池相关API ExecutorService和Executors
ExecutorService真正的线程池接口。常见子类ThreadPoolExecutor
void execute(Runnable command) 执行任务/命令没有返回值一般用来执行 RunnableT FutureT submit(CallableT task)执行任务有返回值一般又来执行 Callablevoid shutdown()关闭连接池
Executors工具类、线程池的工厂类用于创建并返回不同类型的线程池
Executors.newCachedThreadPool()创建一个可根据需要创建新线程的线程池Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池Executors.newSingleThreadExecutor() 创建一个只有一个线程的线程池Executors.newScheduledThreadPool(n)创建一个线程池它可安排在给定延迟后运 行命令或者定期地执行
使用实例
public static void main(String[] args) {//1. 创建线程池ExecutorService service Executors.newFixedThreadPool(10);//2. 放入线程并启动service.execute(new OddNum());//Runnable线程启动FutureTask futureTask new FutureTask(new EvenNum());service.submit(futureTask);//Callable线程启动//3. 关闭线程池service.shutdown();
}线程管理
由于我们接收线程池对象时是使用多态方式接收的我们可以查看newFixedThreadPool源码看到它返回的是ThreadPoolExecutor若想使用线程管理就必须先进行强转。
/*** Creates a thread pool that reuses a fixed number of threads* operating off a shared unbounded queue. At any point, at most* {code nThreads} threads will be active processing tasks.* If additional tasks are submitted when all threads are active,* they will wait in the queue until a thread is available.* If any thread terminates due to a failure during execution* prior to shutdown, a new one will take its place if needed to* execute subsequent tasks. The threads in the pool will exist* until it is explicitly {link ExecutorService#shutdown shutdown}.** param nThreads the number of threads in the pool* return the newly created thread pool* throws IllegalArgumentException if {code nThreads 0}*/
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueueRunnable());
}转换为ThreadPoolExecutor再进行管理
ThreadPoolExecutor service1 (ThreadPoolExecutor) service;
service1.setCorePoolSize(3);//核心池的大小