eclipce的输出语句简写是syso,idea是sout
idea中的module相当于eclipce中的project
idea中的project相当于eclipce中的workspace
若想删掉module,则先remove,再delete
ctrl+alt+T:可以直接surround with,然后选择synchronized或try-catch
CTRL+P可以提示构造器的参数是什么类型的
程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即一段静态的代码,静态对象
进程(process)是程序的一次执行过程,或是正在执行的一个程序,是一个动态的过程:有它自身的产生、存在和消亡过程。----生命周期
->如运行中的qq。
->可以看出,程序是静态的,进程是动态的
->进程作为资源分配单位,系统在运行时会为每个进程分配不同的内存区域
线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径
->若一个进程同一时间并行执行多个线程,就是支持多线程的,如360软件同时可以进行查杀病毒和清理垃圾
->线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器,线程切换的开销小
->一个进程中的多个线程共享相同的内存单元/内存地址空间。他们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简单、高效。但多个线程操作共享的系统资源可能会带来安全隐患。
单核cpu:是一种假的多线程,因为在一段时间内只能执行一个线程的任务。但是由于cpu执行时间特别短,所以感觉不出来
多核cpu可以更好的发挥多线程的效率
一个java应用程序java.exe,其实至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程
并行:多个CPU同时执行多个任务。比如多个人同时做不同的事
并发:一个CPU(采用时间片)同时执行多个任务。比如:多个人做同一件事,秒杀
多线程有优点:
1.提高应用程序的响应。对图形化界面更有意义,可增强用户体验
2.提高计算机系统CPU的利用率
3.改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改
何时需要多线程
1.程序需要同时执行两个或多个任务
2.程序需要实现一些必须等待的任务时,如文件读写、用户输入、网络操作弄成不同的线程
3.需要一些后台运行程序时
//例子:遍历10000以内所有的偶数
class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<10000;i++){
if(i%2==0){
System.out.println(i);
}
}
}
}
public class ThreadTest416 {
public static void main(String[] args) {
MyThread t1 = new MyThread();//t1对象的创建仍是主线程做的
t1.start();//调完start()之后,分线程才开始执行
for(int i=0;i<10000;i++){
//这是主线程的遍历,由于主线程和分线程是分开执行的,因此不确定"i"和"i主线程**************"谁先输出
//有可能是输出一部分"i"再输出一部分"i主线程**************",然后再输出"i"......这样交叉的输出
if(i%2==0){
System.out.println(i+"主线程****************");
}
}
}
}
必须调用的t1.start(),不能调用t1.run(),因为这样不会创建一个新的线程,而只是简单执行run()方法
默认给分线程起的名字是Thread-0,Thread-1……,可以通过Thread.currentThread().getName()来获取当前线程的名字
若想再起一个遍历10000以内的偶数的线程,不能通过已经start()的线程去再起一个,意思是不能再调用一次t1.start()来再起一个线程。否则会报IllegalThreadStateException异常
这时候可以通过再new一个对象来调用start():MyThread t2=new MyThread(); t2.start();
即要想起多个线程,需要new多个对象
因为一个对象只能调用一次start(),因此经常使用匿名子类的匿名对象来起一个线程:
new Thread(){
@Override
public void run() {
System.out.println("第三个线程");
}
}.start();
class HThread extends Thread{
@Override
public void run() {
for(int i=0;i<100;i++){
if(i%2==0){
try {
sleep(1000);//每次遇到sleep,就会等一秒。意思就是每一秒执行一次打印i
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
// if(i%20==0){
// //当i=0,20,40,60,80时主要释放CPU执行权,这时很可能CPU会执行主线程,但是还有可能是虽然分线程释放了CPU执行权,
// //但是仍被分线程抢占了CPU资源,因此接下来仍然是执行分线程
// this.yield();
// }
}
}
public HThread(String name){
super(name);
}
}
public class ThreadMethod419 {
public static void main(String[] args) throws InterruptedException {
HThread h1 = new HThread("Thread------1");
// h1.setName("第一分线程");
h1.start();
//给主线程命名
Thread.currentThread().setName("主线程");
for(int i=0;i<100;i++){
if(i%2==0){
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
if(i==20){
//当i=20时,主线程停止,让分线程先执行完主线程再开始执行
//在i=20之前主线程和分线程也会执行一些,就是说i=20时主线程是i=20,分线程i≠0,可能等于其他值
try{
h1.join();
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
System.out.println(h1.isAlive());
}
}
线程的调度
一般调度是按照时间片来调度:
但是也可以通过抢占式来进行调度:优先级高的线程抢占CPU
Java调度方法:
->同优先级线程按照先进先出(先到先服务),使用时间片策略
->对高优先级,使用优先调度的抢占式策略
线程的优先级等级:
->MAX_PRIORITY:10 最大优先级
->MIN_PRIORITY:1 最小优先级
->NORM_PRIORITY:5 通常的优先级
->getPriority():返回线程的优先级
->setPriority(int p):改变线程的优先级
如
h1.setPriority(Thread.MAX_PRIORITY);
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
说明:
线程创建时继承父线程的优先级
低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用
class Window extends Thread{
private static int ticket;//票必须是静态的,这样三个窗口的对象共享这100张票
@Override
public void run() {
for(ticket=0;ticket<100;ticket++){
System.out.println(getName()+"\t"+ticket);
}
}
public Window(String name){
super(name);
}
}
public class WindowTest421 {
public static void main(String[] args) {
Window w1 = new Window("第1个窗口");
Window w2 = new Window("第2个窗口");
Window w3 = new Window("第3个窗口");
w1.start();
w2.start();
w3.start();
}
}
但是线程不安全:可能会出现三个窗口同时把票1卖了的情况
class MThread implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public class ThreadTest422 {
public static void main(String[] args) {
MThread m1 = new MThread();
Thread t1 = new Thread(m1);
t1.start();//此线程仍然是一个Thread类,调用start():①启动线程 ②调用当前线程的run(),即调用Runnable类型的target的run()
//再启动一个线程,遍历100以内的偶数
Thread t2 = new Thread(m1);
t2.start();
}
}
class MaiPiao implements Runnable{
private int ticket=1;//不用再加static,见下面的main方法中解释
@Override
public void run() {
while(ticket<=100){
System.out.println(Thread.currentThread().getName()+"卖票,票号:"+ticket);
ticket++;
}
}
}
public class WindowTest423 {
public static void main(String[] args) {
MaiPiao m1 = new MaiPiao();
//由于只需造一个实现类的对象,然后分别传入到三个不同的Thread中,因此t1,t2,t3共享同一个m1对象
//即这三者共享同一个变量ticket
Thread t1 = new Thread(m1);
Thread t2 = new Thread(m1);
Thread t3 = new Thread(m1);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
虽然不用加static了,但是仍会出现线程不安全问题
开发中优先选择方式2(实现Runnable接口),原因是:
1.实现的方式没有类的单继承性的局限性
2.实现的方式更适合处理多个线程有共享数据的情况
由于
public class Thread implements Runnable{}
即Thread类也实现了Runnable接口
因此两种方式的相同点:
->两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。
->目前这两种方式要想启动线程,都是要调用Thread类中的start()
Java中的线程分为两类:一种是守护线程,一种是用户线程
守护线程是用来服务用户线程的,当用户线程的生命周期结束时守护线程也会结束
如main是用户线程,gc()是守护线程
JDK中用Thread.State类(这是Thread类中的一个内部类)定义了线程的几种状态
在Java语言中,一个完整的生命周期中通常要经历如下的五种状态:
synchronized(同步监视器){
//需要同步的代码
}
说明:1.操作共享数据的代码,即为需要被同步的代码–>不能把代码包含多了,也不能少了(否则会出现一个窗口把票都卖完,其他窗口没有票的情况)
2.共享数据:多个线程共同操作的变量,如ticket
3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁
要求:多个线程必须共用同一把锁
补充:在实现Runnable接口创建多线程的方式中,可以考虑使用this充当锁。
在继承Tread类创建多线程的方式中,慎用this充当锁;可以考虑使用当前类充当锁(MaiPiao.class)
方式二:同步方法
如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的
声明同步方法:
权限修饰符 synchronized 返回值类型 方法名(形参列表){}
总结:1.同步方法仍然涉及到同步监视器,只是不需要我们显式的声明
2.非静态的同步方法,同步监视器是this;静态的同步方法,同步监视器是当前类本身(类.class)
class MaiPiao implements Runnable{
private int ticket=1;
Object o=new Object();//锁必须声明在这,不能拿用匿名对象放到synchronized中(synchronized(new Object))
//因为锁必须只有一把,多个线程必须共用一把锁,因此锁应该声明在共用的区域,上述不合适的方法会导致多把锁,线程不安全
//如果通过同步代码块之后仍然会出现错票和重票,那么大概率是锁的问题,即锁不止一把
@Override
public void run() {
// synchronized(o){//方式一:同步代码块
while(true){//一般循环都放同步机制的外面,判断尽量在同步机制外面,即把while(ticket<=100){}换成while(true){if(ticket<=0){} else break;},这样判断语句就在同步机制以内了
synchronized(this){//最方便的一把锁就是this,这样不用再new一个对象了,而this就是MaiPiao这个类new出的对象,因为只有一个(m1)
if(ticket<=100) {
//所以可以用来作为锁
System.out.println(Thread.currentThread().getName() + "卖票,票号:" + ticket);
ticket++;
}else break;
}
}
}
}
public class MaiPiao430 {
public static void main(String[] args) {
MaiPiao m1 = new MaiPiao();
Thread t1 = new Thread(m1);
Thread t2 = new Thread(m1);
Thread t3 = new Thread(m1);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class Window extends Thread{
private static int ticket=1;
private static Object o = new Object();//锁要求是一把,所以是static
@Override
public void run() {
while (true){
synchronized(o) {//这种方式锁不能用this,因为有三个对象,不唯一。可以用Window.class来作为锁,Window.class是Window类
//也可以看成一个对象,在反射那一部分会了解到
if(ticket<=100) {
System.out.println(getName() + "\t" + ticket);
ticket++;
}else break;
}
}
}
public Window(String name){
super(name);
}
}
public class MaiPiao432 {
public static void main(String[] args) {
Window w1 = new Window("第1个窗口");
Window w2 = new Window("第2个窗口");
Window w3 = new Window("第3个窗口");
w1.start();
w2.start();
w3.start();
}
}
class MaiPiao3 implements Runnable{
private int ticket=1;
@Override
public void run() {
while(true){
show();
}
}
private synchronized void show(){//默认同步监视器是this
if (ticket<=100) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "卖票,票号:" + ticket);
ticket++;
}
}
}
class MaiPiao4 extends Thread{
private static int ticket=1;
private static Object o = new Object();
@Override
public void run() {
while (true){
show();
}
}
private static synchronized void show(){//由于同步方法中默认的锁是this,所以处理继承Thread类的方式的线程安全问题时,肯定不对
//因为MaiPiao4的对象有三个,this不能作为锁
//所以需要保证同步方法是静态的,此时默认的锁不是this,而是MaiPiao4.class
if(ticket<=100) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "\t" + ticket);
ticket++;
}
}
public MaiPiao4(String name){
super(name);
}
}
class Bank{
private Bank(){}
private static Bank b=null;
//方式一:1
// public static synchronized Bank getInstance(){//加上synchronized,直接把此方法设置成同步方法,锁是Bank.class
// if(b==null){
// b=new Bank();
// }
// return b;
// }
//方式一:2
// public static Bank getInstance(){
// synchronized (Bank.class) {
// if(b==null){
// b=new Bank();
// }
// return b;
// }
// }
//以上是方式一:效率较差
//以下是方式二:效率较高,以后写懒汉式的单例模式直接写这个
public static Bank getInstance(){
if(b==null){//因为如果线程1已经new一个对象了,那之后的线程就不用在等这个锁了,直接执行return语句
synchronized (Bank.class) {
if(b==null){
b=new Bank();
}
}
}
return b;
}
}
->不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
->出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
举例:
/**
* @description 演示线程的死锁问题
*
* 以下代码会出现死锁:线程1拿到锁s1之后睡眠100ms,这时线程2拿到锁s2之后睡眠100ms。等他们醒的时候线程1等着拿锁s2,线程2等着拿锁s1
* 而这两把锁在对方的手中,这时就僵持住了,因此出现死锁,两个线程都在阻塞,不能进入死亡状态
*
*/
public class SiSuo436 {
public static void main(String[] args) {
StringBuffer s1=new StringBuffer();
StringBuffer s2=new StringBuffer();
//匿名子类对象继承Thread类来新建一个线程
new Thread(){
@Override
public void run() {
synchronized (s1){
s1.append("a");
s2.append("1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (s2){
s1.append("b");
s2.append("2");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
//匿名实现Runnable类对象传入到匿名Thread子类对象中来新建一个线程
new Thread(new Runnable() {
@Override
public void run() {
synchronized (s2){
s1.append("c");
s2.append("3");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (s1){
s1.append("d");
s2.append("4");
System.out.println(s1);
System.out.println(s2);
}
}
}
}).start();
}
}
我们使用同步时要避免出现死锁
如:1.专门的算法
2.尽量减少同步资源的定义
3.尽量避免嵌套同步
从JDK5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象
ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁,释放锁
演示:
class Window2 implements Runnable{
private int ticket=100;
//1.实例化Lock
//private ReentrantLock lock = new ReentrantLock(true); true意味着是公平分配时间片的,即线程1执行一次,线程2执行一次,线程3执行一次,接着再执行一次线程1...
private ReentrantLock lock = new ReentrantLock();//无参默认是false,意味着线程1,2,3都是抢着时间片的,不是公平的
//如果是继承Thread类的创建多线程,则是private static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){//一般循环都放同步机制的外面,判断尽量在同步机制外面,即把while(ticket>0){}换成while(true){if(ticket<0) break;},这样判断语句就在同步机制以内了
try{
//2.调用锁定的方法:lock()方法
lock.lock();//lock()方法后面的代码被锁定了,和在synchronized代码块中的代码类似
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+":"+ticket);
ticket--;
}else{
break;
}
}
finally {//开锁放到try中,解锁放到finally中,意思是不管开锁之后的代码是否出现异常,一定要保证必须解锁.所以一旦用Lock的方式,必须用try-finally
//3.调用解锁方法:unlock()
lock.unlock();
}
}
}
}
public class LockTest437 {
public static void main(String[] args) {
Window2 w = new Window2();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.start();
t2.start();
t3.start();
}
}
同:二者都是用来解决线程安全问题的
不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
Lock需要手动的启动同步(lock()),同时结束同步也需要手动释放(unlock())
使用Lock()锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
三种:Lock,同步代码块,同步方法
Lock>同步代码块>同步方法(这三者的灵活性是依次递减的)
实际上用谁都可以
线程通信涉及到的三个方法:
注意点:
/**
* @description 线程通信的例子:使用两个线程打印1-100.线程1,线程2交替打印
*
* 以下代码解释:num=1时,线程1进入synchronized代码块,然后忽略notify()方法,然后执行到wait()方法,进而阻塞,并释放锁;这时线程2拿到锁
* 进入synchronized代码块,执行到notify(),把正在阻塞的线程1唤醒,然后线程2执行到wait()又阻塞,继续等待线程1执行到notify()来唤醒....这样循环
*
*/
class Number implements Runnable{
private int num=1;
@Override
public void run() {
while (true){
synchronized (this) {
notify();//专门唤醒另一个线程的wait()阻塞
if(num<=100){
try {
Thread.sleep(10);//sleep()阻塞的时候不会释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+num++);
try {
wait();//wait阻塞的时候可以把锁释放了
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}else break;
}
}
}
}
public class CommunicationTest439 {
public static void main(String[] args) {
Number n = new Number();
new Thread(n).start();
new Thread(n).start();
}
}
相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态
不同点:
1.两个方法声明的位置不同:Thread类中声明的sleep(),Object()类中声明wait()
2.调用的要求不同:sleep()可以在任何需要的场景下调用。Wait()必须使用在同步代码块或同步方法中
3.关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,而wait()会释放锁
/**
* @author JiaMing
* @create 08-12 21:54
* @description 经典例题:生产者/消费者问题
*
* 生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图
* 生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品
* 了再通知消费者来取走产品。
*
* 分析:
* 1.是否是多线程问题?是,生产者线程,消费者线程
* 2.是否有共享数据?有,产品(或店员)
* 3.如何处理线程安全问题?同步机制,三种方法
* 4.是否涉及到线程的通信?是
*/
class Clerk{
private int productCount=0;
//以下两个方法都在操作productCount,所以会出现线程安全问题,因此都加上synchronized使其变成同步方法。
//需要注意的是这两个方法必须都设成非静态方法,这样锁默认是this,因为clerk对象只new了一个
//生产产品
public synchronized void produceProduct() {
if(productCount<20){
productCount++;
System.out.println(Thread.currentThread().getName()+":开始生产第"+productCount+"个产品");
notify();
}else {//等待
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//消费产品
public synchronized void consumeProduct() {
if(productCount>0){
System.out.println(Thread.currentThread().getName()+"开始消费第"+productCount+"个产品");
productCount--;
notify();
}else {//等待
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer extends Thread{//生产者
private Clerk clerk;
public Producer(Clerk clerk){
this.clerk=clerk;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始生产产品......");
while (true){
try {
sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
clerk.produceProduct();
}
}
}
class Consumer extends Thread{//消费者
private Clerk clerk;
public Consumer(Clerk clerk){
this.clerk=clerk;
}
@Override
public void run() {
System.out.println(getName()+"开始消费产品......");
while (true){
try {
sleep(20);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
clerk.consumeProduct();
}
}
}
public class ProductTest441 {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer producer = new Producer(clerk);
Consumer consumer = new Consumer(clerk);
producer.setName("生产者");
consumer.setName("消费者");
producer.start();
consumer.start();
}
}
如何理解实现Callable接口的方式比实现Runnable接口的方式更强大?
class NumberThread implements Callable{//1.
@Override
public Object call() throws Exception {//2.
int sum=0;
for (int i = 1; i <= 100; i++) {
if(i%2==0){
sum+=i;
}
}
return sum;
}
}
public class ThreadNew442 {
public static void main(String[] args) {
NumberThread n = new NumberThread();//3.
FutureTask f = new FutureTask(n);//4.
new Thread(f).start();//5.
try {
//get()方法的返回值即为FutureTask构造器参数Callable实现类重写call()方法的返回值
Object sum = f.get();
System.out.println(sum);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
}
背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具
好处:
->提高响应速度(减少了创建新线程的时间)
->降低资源消耗(重复利用线程池中线程,不需要每次都创建)
->便于线程管理
JDK5.0提供了线程池相关的API:ExecutorService和Executors
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
->void execute(Runnable command):执行任务,没有返回值,一般用来执行Runnable
->Futuresubmit(Callable task):执行任务,有返回值,一般用来执行Callable
->void shutdown():关闭线程池
Executors:工具栏、线程池的工厂类,用于创建并返回不同类型的线程池
->Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
->Executors.newFixedThreadPool(n):创建一个可重用固定线程数的线程池
->Executors.newSingleThreadExecutor(n):创建一个只有一个线程的线程池
->Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或定期地执行
class NumberThread1 implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if(i%2==0){
System.out.println(Thread.currentThread().getName()+":"+i);//输出:pool-1-thread-1:i
}
}
}
}
public class ThreadPool444 {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
//线程池中取一个线程来执行Runnable接口的run(),执行完再放回池中
service.execute(new NumberThread1());//适合使用于Runnable
// service.submit(Callable callable);//适合使用于Callable
service.shutdown();//关闭线程池
}
}
可以设置corePoolSize(核心池的大小)、maximumPoolSize(最大线程数)、keepAliveTime(线程没有任务时最多保持多长时间后会终止)
如
//设置线程池的属性
ThreadPoolExecutorservice1=(ThreadPoolExecutor)service;//service是ExecutorService接口的类型,肯定不会有属性
//所以要想设置属性,肯定得强转成实现类,然后调属性
service1.setCorePoolSize(15);