17_多线程

多线程

  • 理解线程的概念
  • 掌握线程的创建和启动
  • 了解线程的状态
  • 掌握线程调度的常用方法
  • 掌握线程的同步
  • 线程通信
  • 线程池

1.引入

1.什么是程序?(软件)
用某种语言编写的一组指令的集合
注意:程序本身不占用内存空间,编写好的程序静静躺在磁盘里面,不会占用内存空间。
程序运行起来,会产生一个进程,进程是会占用内存空间的。

2.什么是进程?
操作系统上进程的定义:
    a.进程是正在运行的程序的实例
    b.进程是一个程序及其数据在处理机上顺序执行时所发生的活动。
    c.进程是具有独立功能单位的程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。
    
进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。
它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体。
它不只是程序的代码本身,还包括当前的活动,通过程序计数器的值和处理寄存器的内容来表示。

3.什么是线程?
	a.由线程ID、程序计数器、寄存器集合和堆栈组成。
	线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源。
	b.线程是程序执行流中最小单位。

4.什么是多线程?
是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程。
-------------------------------------------------------------
迅雷下载内容的时候
一边下载音乐,一边下载电影....
单线程思想:自上而下...
多线程的思想: 同时执行....

并行:多个任务同时进行,不存在时间间隔。
并发:多个任务在同一个时间间隔内执行。

电脑
双核 双线程 
i5  四核四线程 执行任务的时候可以通知执行4个任务,分别有不同的物理核心去处理。

i7  四核八线程  
超线程技术 讲一个物理核心 分成2个逻辑核心 (通过增加 5% 晶体面积  执行增加大概15%-30% 效率)


1个进程至少由1个线程组成
main方法就是一个主线程 ,只有一个main方法的无其他线程的程序 单线程)

1个进程也可以由多个线程组成(除了main方法以外,还有其他的线程(子线程),多线程)
1.1 理解多线程概念
通过分析代码理解多线程的意义:
//1.单线程
public static void main(String[] args) {
    System.out.println("程序开始........");
    m1();
    m2();
    System.out.println("程序结束........");
}
public static void m1() {
    System.out.println("m1被执行.....");
}

public static void m2() {
    System.out.println("m2被执行......");
}

单线程执行图解:

thread02

多线程程序执行图解:

thread03

通过代码了解一下线程

main方法本身就是一个线程
//1.获取当前正在执行的线程对象
Thread th=Thread.currentThread();
//2.获取线程的名字: 主线程默认名字为 main 
// 子线程的名字:Thread-0 第一个子线程  
//Thread-1 第二个子线
System.out.println("线程名字:"+th.getName());
//3. 设置线程的名字
th.setName("主线程");
System.out.println(th.getName());     

2.常用的线程的创建和启动(子线程)

thread01

Java中创建线程的两种方式
继承java.lang.Thread类
实现java.lang.Runnable接口
     
分别使用继承 Thread  以及 实现 Runnable 接口的方式实现多线程程序
	都需要重写run方法(需要执行的任务)
	都是通过.start()开启线程。
    
注意:
调用start()方法与调用run()有何区别?
a.调用run()方法 ,只是简单的方法调用,而且此方法由主线程(main)执行(单线程) 
b.调用strat()方法开启一个子线程(多线程),
当main 线程启动一个子线程Thread-0, 主线程不会阻塞, 会继续执行。
主线程和子线程是交替执行。
2.1 比较2种创建线程的方式:
<1>.继承Thread类
编写简单,可直接操作线程
适用于单继承

<2>.实现Runnable接口
因为Java类是单继承,当一个子类已经继承了父类还需要使用多线程,实现此接口就行。
便于共享资源

多线程实现网络购票,用户提交购票信息后
第一步:网站修改网站车票数据
第二步:显示出票反馈信息给用户

总的票数 是共享的资源  100 
多个窗口售卖   每个窗口对应一个线程
多个窗口卖票的问题

Ticket1 t1=new Ticket1();
Ticket1 t2=new Ticket1();
Ticket1 t3=new Ticket1();
Ticket1 t4=new Ticket1();

class Ticket1  extends Thread{
	private int ticketCount=10;
	//run方法去卖票
} 
同样适用实现runnable接口的方式实现  开启多个线程

3.守护线程

守护线程与普通线程的唯一区别是:当JVM中所有的线程都是守护线程的时候,JVM就可以退出了;如果还有一个或以上的非守护线程则不会退出。
所以setDeamon(true)的唯一意义就是告诉JVM不需要等待它退出。

4.了解线程的状态

线程状态
与人有生老病死一样,线程也同样要经历新建、就绪、运行(活动)、阻塞和 等待队列,死亡六种不同的状态。
这五种状态都可以通过Thread类中的方法进行控制。

<1>.新建状态 (New Thread)Java 语言中使用 new 操作符创建一个线程后,该线程仅仅是一个空对象,
它具备了线程的一些特征,但此时系统没有为其分配资源,这时的线程处于创建状态。

<2>.就绪状态 (Runnable)
使用 start() 方法启动一个线程后,
系统为该线程分配了除 CPU 外的所需资源,使该线程处于就绪状态。

<3>.运行状态 (Running)
Java 运行系统通过调度选中一个处于就绪状态的线程,
使其占有 CPU 并转为运行状态。此时,系统真正执行线程的 run() 方法。

<4>.阻塞状态 (Blocked)一个正在运行的线程因某些原因不能继续运行时,就进入阻塞状态。

<5>.死亡状态 (Dead)
线程在 run() 方法执行结束后进入死亡状态。

<6>.等待队列(Waiting)
正在等待另一个线程执行特定动作的线程处于此状态    

thread04

5.线程调度的常用方法

线程调度指按照特定机制为多个线程分配CPU的使用权
    
<1>.
getPriority(int  newPriority) 获取线程的优先级
setPriority(int  newPriority) 更改线程的优先级
注意:线程优先级由1~10表示,1最低,默认优先级为5
优先级高的线程获得CPU资源的概率较大(但不能保证一定会分配资源)
cpu会尽可能的优先分配资源给优先级相对较高的线程

线程1   1  优先级最低
线程2   10  优先级最高     
    10代表:此线程获取CPU优先分配资源的概率更大一点  不能保证一定让此线程获取资源
    
<2>(*).static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠  定时
       
<3>.boolean isAlive() 测试线程是否处于活动状态
    
<4>. stop() 强制终止线程
    
<5>. interrupt() 中断线程
    中断线程,但没有真正结束线程。所以一般用于中断正在休眠线程。
    
案例1:
需求说明
某科室一天需看普通号50个 , 特需号10个
特需号看病时间是普通号的2(sleep)
开始时普通号和特需号并行叫号,叫到特需号的概率比普通号高  setPriority(1-10)
当普通号叫完第10号时,要求先看完全部特需号,再看普通号   join( ms)
使用多线程模拟这一过程
Special 特需号
General 普通号
    
public class Test {
	public static void main(String[] args) {
		Special special=new Special();
		General general=new General();
		//1.叫号的时候优先叫特需号的  (1-10   1:最低   10:最高   5:默认优先级) 值越大获得时间片段的概率就越大。
		special.setPriority(10);
		general.setPriority(1);
		
		special.start();
		general.start();
	}
}
//1.特需号 10个
class Special extends Thread{
	@Override
	public void run() {
		for(int i=1;i<=10;i++) {
			//1.模拟看一个特需号病人的时间 
			try {
				Thread.sleep(400);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("---------------正在看第"+i+"个特殊号病人----------------");
		}
	}
}
//2.普通号 50个
class General extends Thread{
	@Override
	public void run() {
		for(int i=1;i<=50;i++) {
			//1.模拟普通号看病的时间
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("正在看第"+i+"个普通号病人");
			if(i==10) {
				//2.看到第10个病人的时候,先看完特需号,再看普通号 (线程进入阻塞状态)
				try {
                    //进入阻塞,在指定时间范围内 进入阻塞状态 ,没有设置时间就是永久阻塞
					this.join(3000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
5.1 yield()和join()方法
<3>(*).void join( long millis)  线程的插队
一旦插队成功,则肯定先执行完插入的线程所以的任务。  
让出CPU资源,给其他线程使用
注意:使当前线程暂停执行,等待其他线程结束后再继续执行本线程

<4>.static void yield() 暂停当前正在执行的线程对象,并执行其他线程
注意:暂停当前线程,允许其他具有相同优先级的线程获得运行机会
该线程处于就绪状态,不转为阻塞状态。
特别注意:只是提供一种可能,但是不能保证一定会实现礼让

class T3 implements Runnable {
    private int count = 0;
    @Override
    public void run() {
        while (true) {
            System.out.println("hello " + (++count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (count == 10) {
                break;
            }
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread t3 = new Thread(new T3());//创建子线程
        for (int i = 1; i <= 10; i++) {
            System.out.println("hi " + i);
            if(i == 5) {//说明主线程输出了5 次hi
                t3.start();//启动子线程输出hello
                t3.join();//立即将t3 子线程,插入到main 线程,让t3 先执行
            }
            Thread.sleep(1000);//输出一次hi, 让main 线程也休眠1s
        }
    }
}
5.2 start()方法
1. 当一个类继承了Thread 类, 该类就可以当做线程使用
2. 我们会重写run 方法,写上自己的业务代码
3.Thread 类实现了Runnable 接口的run 方法   

class Cat extends Thread {
    int times = 0;
    @Override
    //重写run 方法,写上自己的业务逻辑
    public void run() {
        while (true) {
            //该线程每隔1 秒。在控制台输出“喵喵, 我是小猫咪”

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //当times 到80, 退出while, 这时线程也就退出..
            if (times == 80) {
                break;
            }
        }
    }
}  

public static void main(String[] args) throws InterruptedException {
        //创建Cat 对象,可以当做线程使用
        Cat cat = new Cat();
        //名字main
        System.out.println("主线程继续执行" + Thread.currentThread().getName());
        for(int i = 0; i < 60; i++) {
            System.out.println("主线程i=" + i);
            //让主线程休眠
            Thread.sleep(1000);
        }
    }

thread08

(1)
public synchronized void start() {
	start0();
}

(2)
//start0() 是本地方法,是JVM 调用, 底层是c/c++实现
//真正实现多线程的效果, 是start0(), 而不是run
private native void start0();

(3)
cat.start();//启动线程-> 最终会执行cat 的run 方法

start()方法调用start0()方法,该线程并不一定立马执行,只是将线程变成了可运行状态。
具体什么时候执行,取决于CPU,由CPU统一调度。

6.线程同步案例分析

6.1线程同步的基本概念:
java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),将会导致数据不准确,相互之间产生冲突,
因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。

同步

thread05

异步:

thread06

同步,线程安全,执行效率较低。
异步,线程不安全,执行效率更高。
6.2使用同步方法实现同步 (重点)

即有synchronized关键字修饰的方法。由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。

 public synchronized void save(){}
 注: synchronized关键字也可以修饰静态方法,
 此时如果调用该静态方法,将会锁住整个类。。。
 
  public void save(){
  	 synchronized(Object obj){
 		//执行代码逻辑   
 	}
  }
 
6.3 volatile关键字

保证内存可见性
基本概念:
可见性是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。
也就是一个线程修改的结果,另一个线程马上就能看到。

6.3.1原理
 Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。
 当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。
 volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。
  在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比sychronized关键字更轻量级的同步机制。
  
代码实现:
private volatile static boolean flag;
	public static void main(String[] args) throws InterruptedException {
		new Thread(()->{
			while(true) {
				if(flag==!flag) {
					System.out.println("拜拜...");
					System.exit(0);
				}
			}
		}).start();
		
		Thread.sleep(100);
		
		new Thread(()->{
			while(true) {
				flag=!flag;
				System.out.println("made in china!!!");
			}
		}).start();
	}

thread07

6.4使用重入锁(Lock 接口)实现线程同步
在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。
ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized的作用相同都是实现代码同步,扩展了其能力。
ReenreantLock类的常用方法有: 
 new ReentrantLock() : 创建一个ReentrantLock实例         
 lock() : 获得锁        
 unlock() : 释放锁
6.4.1 synchronized 与lock 之间区别:
1.Lock是一个接口,而synchronized是Java中的关键字,synchronized在代码执行时出现异常,JVM会自动释放锁定。
  但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将 unLock()放到finally{} 中;

2.synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;
  而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

3.Lock可以让等待锁的线程响应中断,线程可以中断去干别的事务,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断。

4.通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

5.Lock可以提高多个线程进行读操作的效率。
  在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。
 
6.5案例分析:
1.使用线程同步解决单例模式之懒汉式线程不安全的为题

Lock的实现:

public class LazyTest02 {

	public static void main(String[] args) {
		new Thread(()-> {
			System.out.println(LazyDemo02.getInstance());
		}).start();
		new Thread(()-> {
			System.out.println(LazyDemo02.getInstance());
		}).start();
	}
}
class LazyDemo02{
	//1.定义LazyDemo类型的变量
	private static volatile LazyDemo02 ls;
	//2.构造方法私有化
	private LazyDemo02() {}
	//创建锁
	private static Lock lock=new ReentrantLock();
	//3.定义静态的方法得到实例
	public static  LazyDemo02 getInstance() {
		lock.lock();//获得锁
		if (ls == null) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			ls = new LazyDemo02();
		}
		lock.unlock();//释放锁
		return ls;
	}
}
synchronized 的代码实现:
public class LazyTest {
	public static void main(String[] args) {
		//1.使用线程1 创建对象
		new Thread() {
			public void run() {
				System.out.println(LazyDemo.getInstance());
			}
		}.start();
		//2.使用线程2创建对象
		new Thread() {
			public void run() {
				System.out.println(LazyDemo.getInstance());
			}
		}.start();
		//3.使用线程3创建对象
		new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println(LazyDemo.getInstance());
			}
		}).start();
		
		//4. 使用线程4创建对象
		new Thread(()->{
			System.out.println(LazyDemo.getInstance());
		}).start();
	}
}
class LazyDemo{
	//1.定义LazyDemo类型的变量
	private static volatile LazyDemo ls;
	//2.构造方法私有化
	private LazyDemo() {}
	//3.定义静态的方法得到实例
	public static synchronized LazyDemo getInstance() {
		if (ls == null) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			ls = new LazyDemo();
		}
		return ls;
	}
}


2.某银行卡账号上有500元现金。一个人拿着存折去取钱,同时另一个人拿着卡去ATM上取钱,各自取钱400元。
要求取钱过程中不能出现资源竞争:比如400元被取出两次、银行卡的账目不能小于0等。

线程1   模拟ATM取钱
线程2  模拟银行柜台取钱
    
建立Bankclass Bank{
    private int money=500;//余额
     public int getMoney(int money){//取得钱
         if(this.money<money){
             syso("余额不足,取钱失败...")
             return -1;
         }else{
             //模拟取钱的时间
             
             this.money-=money;
             syso...("本次取钱金额为"+money+",剩余的余额为:"+this.money);
         }
         return money;//取出来的钱
     }
}   

class BankThread{
    private Bank bank;
}2个线程调用同一个对象中的getMoney()方法
package com.itwn.thread06;

public class BankTest {

	public static void main(String[] args) {
		Bank bank=new Bank();
		new BankThread(bank,"ATM").start();
		new BankThread(bank,"银行").start();
	}

}
class BankThread extends Thread{
	private Bank bank;
	public BankThread(Bank bank,String name) {
		super(name);
		this.bank=bank;
	}
	@Override
	public void run() {
		System.out.println(this.getName()+"上取款金额为:"+bank.getMoney(400));
	}
}
class Bank{
	private volatile int money=500;
	public synchronized int getMoney(int money) {
		if(money>this.money) {
			System.out.println(Thread.currentThread().getName()+"上余额不足,取钱失败...");
			return -1;
		}else {
			//1.模拟取钱的过程
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			this.money-=money;
			System.out.println(Thread.currentThread().getName()+"取钱成功,剩余余额:"+this.money);
		}
		return money;//取钱数
	}
}

7.线程通信

线程通信是建立在线程同步的基础之上的...

1、借助于Object类的wait()notify()notifyAll()实现通信
线程执行wait()后,就放弃了运行资格,处于冻结状态
    
线程运行时,内存中会建立一个线程池,冻结状态的线程都存在于线程池中,notify()执行时唤醒的也是线程池中的线程,线程池中有多个线程时唤醒第一个被冻结的线程。
    
notifyall(), 唤醒线程池中所有线程。

注:(1wait(),notify(),notifyall()都用在同步里面,因为这3个函数是对持有锁的线程进行操作,而只有同步才有锁,所以要使用在同步中;
   (2wait(),notify(),notifyall()在使用时必须标识它们所操作的线程持有的锁,因为等待和唤醒必须是同一锁下的线程;而锁可以是任意对象,所以这3个方法都是Object类中的方法。
7.1线程通信入门案例
交替打印数字以及字母
1 2 A 3 4 B 5 6 C ... 55 56 Z
    
    <1>.线程1 打印数字  当数字是偶数的时候 线程1 wait()挂起 释放锁,唤醒打印字母的线程  获取的锁
    <2>.线程2 打印字母 每打印一个字母 线程 wait()挂起 释放锁,唤醒 打印数字的线程 获得锁

public class ThreadTest {
	public static void main(String[] args) {
		Object obj=new Object();
		new Thread(()-> {
			synchronized (obj){//构建了一个同步代码块
				for(int i=1;i<=52;i++) {
					//1.模拟打印数字的时间
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(i);
					if(i%2==0) {
						try {
							obj.notify();//唤醒 打印字母的线程 
							obj.wait();//挂起打印数字的线程 释放锁
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
			}
		}).start();
		
		new Thread(){
			@Override
			public void run() {
				synchronized (obj) {//构建一个同步代码块
					for(char c='A';c<='Z';c++) {
						try {
							Thread.sleep(500);
						} catch (InterruptedException e1) {
							e1.printStackTrace();
						}
						System.out.println(c);
						try {
							obj.notify();//唤醒打印数字的线程
							obj.wait();//挂起打印字母的线程 释放锁
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
			}
		}.start();
		
	}
}
7.2线程通信实现生产者消费者模式
<1>.假设仓库的容量为5,利用线程通信设计实现生产者与消费者模式?
public class ProductTest {

	public static void main(String[] args) {
		WareHouse house=new WareHouse();
		//1.生产者的线程
		new Thread() {
			public void run() {
				house.createProduct();
			}
		}.start();
		
		//2.消费者的线程
		new Thread() {
			public void run() {
				house.removeAllProduct();
			}
		}.start();
	}

}
class WareHouse{
	private LinkedList<Product> list=new LinkedList<>();
	//1.生产产品的方法
	public void addProduct(Product product) {
		if(list.size()==5) {//仓库满了,提醒消费者线程进行消费
			try {
				this.notify();//唤醒消费者线程进行消费
				this.wait();//挂起生产者线程 
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		list.add(product);
		System.out.println("生产了:"+product);
	}
	
	//2.消费产品的方法
	public void removeProduct() {
		if(list.size()==0) {//仓库空了,无法进行消费了...
			try {
				this.notify();//唤醒生产者线程,生产商品
				this.wait();//挂起消费者线程
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		Product product=list.removeLast();
		System.out.println("消费了"+product);
	}
	
	//生产了20件商品 (生产者线程 完成)
	public synchronized void createProduct() {
		for(int i=1;i<=20;i++) {
			Product product=new Product(i);
			addProduct(product);
		}
		//最后一次因为 生产者线程已经结束,需要手动唤醒消费者线程
		this.notify();
	}
	//消费了20件商品 (消费者线程 完成)
	public synchronized void removeAllProduct() {
		for(int i=1;i<=20;i++) {
			removeProduct();
		}
		
	}
}

class Product{
	private int id;
	public Product(int id) {
		this.id=id;
	}
	@Override
	public String toString() {
		return "产品"+id;
	}
}     

8 .线程池

8.1线程池的概念:

线程池就是首先创建一些线程,它们的集合称为线程池。
使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。

8.2 线程池工作机制

在线程池的编程模式下,任务是提交给整个线程池,而不是直接提交给某个线程,线程池在拿到任务后,就在内部寻找是否有空闲的线程,如果有,则将任务交给某个空闲的线程。
一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务。

8.3 总结

多线程运行时间,系统不断的启动和关闭新线程,成本非常高,会过渡消耗系统资源,以及过渡切换线程的危险,从而可能导致系统资源的崩溃。这时,线程池就是最好的选择了。

8.4 线程池的使用
线程池的返回值ExecutorServiceExecutorServiceJava提供的用于管理线程池的类。该类的两个作用:控制线程数量和重用线程

具体的4种常用的线程池实现如下:(返回值都是ExecutorService1.Executors.newCacheThreadPool():
可缓存线程池,先查看池中有没有以前建立的线程,如果有,就直接使用。如果没有,就建一个新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步型任务。
注意:当执行当前任务时上一个任务已经完成,会复用执行上一个任务的线程,而不用每次新建线程

2.Executors.newFixedThreadPool(int n):创建一个可重用固定个数的线程池,以共享的无界队列方式来运行这些线程。
注意:定长线程池的大小最好根据系统资源进行设置。

3.Executors.newScheduledThreadPool(int n):创建一个定长线程池,支持定时及周期性任务执行

4.Executors.newSingleThreadExecutor():创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

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

public class Test {

	public static void main(String[] args) {
		method01();
        method02();
        method03();
        method04();
        
	}
    
    public static void method01() {
        //1.可缓存的线程池 (长度不收限制)
		ExecutorService es01=Executors.newCachedThreadPool();
		while(true) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			//1.开启线程(先从线程池中找空闲的线程,如果没有创建一个新的线程,如果有就会用空闲的线程)
			es01.execute(()->{
				for(int i=1;i<=10;i++) {
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+":"+i);
				}
			});
		}		
    }
    
    public static void method02() {
        2.定长的线程池 长度为5 线程池中最多只有5个线程
		ExecutorService es02=Executors.newFixedThreadPool(5);
		while(true) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			es02.execute(()->{
				for(char c='a';c<='z';c++) {
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+":"+c);
				}
			});
		}
    }
    
    public static void method03() {
        3. 加延时的线程池 ,定长
		ScheduledExecutorService es03=Executors.newScheduledThreadPool(5);
		*//**
		 * th1:线程需要完成的任务 Runnable接口的实现类
		 * th2: 设置延时时间 (3秒后开启线程)
		 * th3: 定时时间 (每隔5秒钟刷新一次任务)
		 * th4:设置时间单位
		 * *//*
		es03.scheduleAtFixedRate(()->{
			//线程需要完成的任务
			for(int i=1;i<=10;i++) {
				System.out.println(Thread.currentThread().getName()+":"+i);
			}
		}, 3,5,TimeUnit.SECONDS);
    }
    
    public static void method04() {
        //4. 单例线程池 (线程池中只有一个线程)
		ExecutorService es04=Executors.newSingleThreadExecutor();
		while(true) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			es04.execute(()->{
				for(int i=1;i<=10;i++) {
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+":"+i);
				}
			});
		}
    }
}       
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值