程序是含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中,也就是说程序是静态的代码。
进程是程序的一次执行过程,是系统运行的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。
线程,与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间做切换工作时,负担要比进程小得多,线程也被称为轻量级进程。
使用的是并发,因为电脑上只有一个CP,但是电脑上却可以同时运行多个程序,这是一种假象,因为CPU的计算速度极快,10^-9每秒就会计算一次,而人能感觉到的时间流失是秒,所以电脑给我们一种感觉好像电脑上程序在同时执行。
Thread就是一个线程类,里面定义所有和线程相关的功能,只有是这个类才能开启的任务,才能被CPU单独去执行。
Java中线程执行的时候是采用的并发。学习的过程中不要纠结于多个线程执行的结果,因为是随机在执行,抢占式的在使用CPU资源。
说明 | 方法名 |
在线程开启后,此方法将被调用执行 | void run() |
在线程开始执行,java虚拟机会调用run()方法 | void start() |
package com.why.demo02;
// 继承Thread类,MyThread就具备所有和线程相关的功能
public class MyThread extends Thread{
// 重写run方法, 条件自己的任务
@Override
public void run() {
// 添加一个任务打印一百次:MyThread线程任务 ----
for (int i = 1; i < 101; i++) {
System.out.println("MyThread线程任务 ---- " + i);
}
}
}
package com.ujiuye.demo02;
public class UseMyThread {
public static void main(String[] args) {
// System.out.println( 1 / 0);
// 创建一个MyThread对象
MyThread mt = new MyThread();
// 调用start方法开启线程
mt.start();
// mt.run(); 千万不要调用run方法,因为并没有开启线程,只是一个对象调用一次方法而已.
// 这个循环属于main方法所在的线程,就是换一个叫做"main"线程,这线程就是主线程
for (int i = 0; i < 100; i++) {
System.out.println("主线程中的任务---" + i);
}
}
}
继承开启多线程的简化版:匿名内部类的方式
Thread 变量名 = new Thread() {重写run方法;}变量名.start();new Thread() {重写run方法;}.start();
public class UseNoNameClassImpThread {
public static void main(String[] args) {
// 匿名内部方式继承Thread类开启多线程
Thread t = new Thread(){
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("线程1-----"+i);
}
}
};
t.start();
new Thread(){
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("线程2-----"+i);
}
}
}.start();
}
}
Runnable是一个接口。仅仅用来描述线程要执行的任务。它的实现类就是线程执行的任务,实现类不是线程对象。
方法名 | 说明 |
Thread(Runnable target) | 分配一个新的Thread对象 |
Thread(Runnable target,String name) | 分配一个新的Thread对象 |
解释:
Thread的构造方法创建对象的时候传入了Runnable接口的对象 ,Runnable接口对象重写run方法相当于指定线程任务,创建线程的时候绑定了该线程对象要干的任务。
package com.offcn.demo2;
// 1.定义一个MyRunnable类实现Runnable接口
public class MyRunnable implements Runnable{
// 2.重写接口中的run方法.定义线程任务内容
@Override
public void run() {
for (int i = 1; i < 101; i++) {
System.out.println("任务对象----"+i);
}
}
}
public class UseMyRunnable {
public static void main(String[] args) {
// 3.创建任务类对象
MyRunnable m1 = new MyRunnable();
// 4.创建线程对象,把任务对象绑定到线程中
Thread t1 = new Thread(m1);
t1.start();
for (int i = 0; i < 100; i++) {
System.out.println("主线程------"+i);
}
}
}
匿名内部类实现Runnable接口,开启多线程
Runnable r = new Runnable() {重写run方法};Thread t = new Thread(r);Thread t2 = new Thread(new Runnable() {重写run方法});
public class NoNameRunnableObject {
public static void main(String[] args) {
// 匿名内部类为Runnable提供实现类
Runnable r = new Runnable() {
@Override
public void run() {
for (int i = 1; i < 20; i++) {
System.out.println("任务1-----"+i);
}
}
};
// 任务对象绑定到线程对象中,相同的任务可以被多个线程对象执行.
// 解开了之前继承实现多线程下任务和线程之间的耦合性.
Thread t1 = new Thread(r );
Thread t2 = new Thread(r);
t1.start();
t2.start();
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i < 30; i++) {
System.out.println("任务2------我爱学习java");
}
}
});
t3.start();
}
}
这种实现多线程方式类型有三个:Callable接口,FutureTask类,Thread类。
方法名 | 说明 |
V call() |
计算结果,如果无法计算结果,则抛出一个异常,线程要执行任务就写在call方法中。此方法的返回值就是线程任务执行结束后给调用者的反馈。
|
FutureTask(Callable callable) |
创建一个 FutureTask对象,一旦运行就执行给定的 Callable接口你重写的call的内容
|
V get() |
如有必要,等待计算完成,然后获取其结果,获取的就是call方法的返回值,这个get方法属于延迟方法,因为调用完之后不会立即执行,而是等到Callable接口实现call执行完毕之后才会执行了。获取就是call方法的返回值
|
FutureTask实现了RunnableFuture接口,而RunnableFuture是Runnable接口子接口,所以FutureTask对象就可以看做一个Runnable接口实现类对象使用,可以通过Thread类的Thread(Runnable target)构造方法,把Ruture Task对象绑定到线程对象中。
定义一个类MyCallable实现Callable接口
//实现Callable接口
public class MyCallable implements Callable {
@Override
public String call() throws Exception {
// 线程任务
for (int i = 1; i < 100; i++) {
System.out.println("女神 咱俩交往吧, I love you!!!");
}
// 女生给你的反馈
return "一起去如家";
}
}
public class UseMyCallable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 3.MyCallable对象
MyCallable mc = new MyCallable();
// 4.创建一个FutureTask对象, 把MyCallable绑定到未来任务对象中
FutureTask task = new FutureTask<>(mc);
// 5.把未来任务对象绑定到Thread类中
Thread t = new Thread(task);
t.start();
for (int i = 0; i < 100; i++) {
System.out.println("主线程-女生不要答应");
}
// 一定要在线程开启之后,在调用get方法,否则线程就没有开启的机会.
String result = task.get();
System.out.println("女神的反馈:"+result);
}
}
1、继承的方式:适合于这个任务只想被一个线程的对象执行的情况
2、实现Runnable接口方式:适合于一个任务想被多个线程执行的情况
3、实现Callable接口方式:也适合一个任务想被多个线程执行的情况,你还想得倒任务的执行结果
方法名 | 说明 |
void setName(String name) | 将此线程的名称更改为等于参数name,除了可以使用setName设置线程名称,也可以是Thread类提供构造方法执行线程名称.Thread(String name), Thread(Runnable r, String name) |
String getName() | 返回此线程的名称 |
static Thread currentThread() | 返回对当前正在执行的线程对象的引用 |
public class UseThreadName {
public static void main(String[] args) {
// 线程如果没有指定名称,也有默认名称: Thread-数字 数字从0开始
Thread t1 = new Thread();
System.out.println(t1.getName());// Thread-0
Thread t2 = new Thread();
t2.setName("线程2"); // 修改线程名称
System.out.println(t2.getName());
// 通过构造方法执行线程名称: Thread(Stirng name)
Thread t3 = new Thread("线程1");
System.out.println(t3.getName());
// 通过构造方法执行线程名称: Thread(Runnable r, String name)
Thread t4 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("任务");
}
}, "线程3");
System.out.println(t4.getName());
// 获取main方法所在线程对象
Thread t = Thread.currentThread();
System.out.println(t.getName());
// Exception in thread "main" 在一个叫main线程中出现了异常
// System.out.println(1 / 0 );
}
}
方法名 | 说明 |
static void sleep(long millis) | 是一个静态方法,而且方法上还有编译时异常的声明,使用时一定要处理异常。作用使当前正在执行的线程停留(暂停执行)指定的毫秒数,1s = 1000ms,过了指定的时间还会醒,可以继续执行任务。 |
public class UseThreadSleepMethod {
public static void main(String[] args) {
// 匿名内部类体现Thread子类
Thread t1 = new Thread("线程1"){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000); // 只能try..catch处理异常,因为Thread中
run没有异常的声明,子类重写也不能声明异常
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+"----"+i);
}
}
};
t1.start();
// 匿名内部类提供Runnable的实现类
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "----"+i);
}
}
}, "线程2");
t2.start();
}
}
方法名 | 说明 |
final int getPriority() | 返回此线程的优先级 |
final void setPriority(int newPriority) | 更改此线程的优先级是5;线程优先级的范围是:1-10,数字越大优先级越高。以后设置优先级也不会直接写数字,而是使用Thread类中提供三个常量值设置优先级属性。 |
public class UseThreadPriority {
public static void main(String[] args) {
Thread t1 = new Thread("线程1") {
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
System.out.println(getName() + "----" + i);
}
}
};
Thread t2 = new Thread("线程2") {
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
System.out.println(getName() + "----" + i);
}
}
};
// 获取线程对象默认优先级
System.out.println(t1.getPriority()); // 5
System.out.println(t2.getPriority()); // 5
/* // 练习:获取主线程优先级
Thread t = Thread.currentThread();
System.out.println(t.getName() + "----" + t.getPriority()); // main----5*/
// 设置线程优先级 数字必须是[1, 10] 超出范围就会报错
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
}
}
方法名 | 说明 |
static void yield()
|
线程让步,暂停当前正在执行的线程任务,把执行机会让给优先级相同或更高的线程,若队列中没有同优先级的线程,忽略此方法; 当前线程让出CPU资源后,依然有获取CPU的机会,并不是让步之后就不能获取到CPU资源了。
|
public class YieldMethod {
public static void main(String[] args) {
Thread t1 = new Thread("任务1") {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
// 任务1碰到偶数就礼让
if(i % 2 == 0) {
Thread.yield();
}
System.out.println(getName()+"------" + i);
}
}
};
Thread t2 = new Thread("任务2") {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(getName()+"------" + i);
}
}
};
// t1和t2的优先级一样,都是5,能触发yield方法.
t1.start();
t2.start();
}
}
基本都是t2先执行完,t1才会执行完。因为t1经常让步。
线程中断方法不是把任务执行中断掉,而是终端的线程状态。
方法名 | 说明 |
public void interrupt()
|
中断这个线程状态,不是中断任务。
|
public class UseThreadInterrupt {
public static void main(String[] args) {
Thread t1 = new Thread("why"){
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 1; i <= 5; i++) {
System.out.println(getName()+"i");
}
}
};
t1.start();
t1.interrupt(); // 看到程序执行的之后,就不回你再停留5秒才走循环体. 线程状态被中断会异常,但是咱们使用try...catch处理异常,程序出了异常还能继续执行.
}
}
方法名 | 说明 |
public final void
setDaemon(boolean
on)
|
将此线程标记为daemon线程或用户线程。当运行的唯一线程都是守护进程线程时,Java虚拟机将退出。
|
boolean isDaemon()
|
判断一个线程是否为守护线程,是守护线程返回true,否则返回false.
|
public class UseThreadDaemon {
public static void main(String[] args) {
Thread t = Thread.currentThread();
boolean daemon = t.isDaemon();
System.out.println(daemon ? "主线是守护线程" : "主线程不是守护线程");
// 自己创建的线程对象,默认都不是守护线程.
Thread t1 = new Thread("大王"){
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("我是大王我怕谁???");
}
}
};
System.out.println(t1.isDaemon());
Thread t2 = new Thread("奴隶"){
@Override
public void run() {
while (true){
System.out.println("我是守护者,要为大王服务....");
}
}
};
t2.setDaemon(true); // t2就变为守护线程了,就具备守护线程特点, 非守护线程任务结束了,
// 那么这个守护线程过一会也会挂掉.
System.out.println(t2.isDaemon());
t1.start();
t2.start();
}
}
案例需求:某电影院目前正在上映国产大片:一路向西,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票实现步骤:- 定义一个类SellTicket实现Runnable接口,里面定义一个成员变量:private int tickets = 100;- 在SellTicket类中重写run()方法实现卖票,代码步骤如下- 判断票数大于0,就卖票,并告知是哪个窗口卖的- 卖了票之后,总票数要减1- 票卖没了,线程停止- 定义一个测试类SellTicketDemo,里面有main方法,代码步骤如下- 创建SellTicket类的对象- 创建三个Thread类的对象,把SellTicket对象作为构造方法的参数,并给出对应的窗口名称- 启动线程
public class SellTicket implements Runnable {
// 定义变量模拟要卖的100张票
private int ticket = 100;
@Override
public void run() {
while (ticket > 0) {
// 卖出一张票数-1
ticket--;
System.out.println(Thread.currentThread().getName()+"---卖出的是第"+(ticket+1)+"张, 剩余"+ticket+"张");
}
}
}
// 线程安全问题演示
public class ThreadSafeProblem {
public static void main(String[] args) {
// 卖票任务
SellTicket st = new SellTicket();
// 创建3个线程对象,模拟三个窗口
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");
t1.start();
t2.start();
t3.start();
}
}
买票出现的问题:
问题产生的原因:线程执行的随机性导致的,可能再卖票过程中丢失cpu的执行权,导致出现问题。
原子操作
处理方案:
synchronized(锁对象) {原子操作的代码;【有可能出现线程安全问题的代码】}
一段代码一旦使用同步代码块之后,线程只要进入同步代码块,就要先获取锁对象,只有拿到锁对象才能进入同步代码块执行任务,在这个线程执行任务期间,CPU可能会把资源切换到其他线程,即使其他线程也要进入这个同步代码块,因为锁对象已经被之前的线程拿走了,它没有锁对象,依然进不来,只能等之前线程执行完,把锁对象归还,其他线程才有机会拿到锁对象,执行里面任务。
优点:解决了多线程的数据安全问题
缺点:当线程很多时,因为每个线程都会去判断同步上的锁。这是很耗费资源的,无形中会降低程序的运行效率。
public class SellTicket implements Runnable {
// 定义变量模拟要卖的100张票
private int ticket = 100;
private StringBuilder sb = new StringBuilder();
@Override
public void run() {
while (ticket > 0) { // "a"首先是String对象. 且常量字符串只会在方法区常量池中存储一份.
// synchronized (new String("a")) { // 这种锁是锁不住的,每次获取锁对象都new一个新的对象.无法同步代码中原子性操作.
// synchronized (this) { // 外面只创建一个SellTicket对象, 且三个线程绑定的都是这个SellTicket对象. 因此this就只代表外界创建这个st对象. 此时依然能保证锁对象的唯一.
// synchronized (sb) { sb是SellTicket的一个对象属性,SellTicket在外界只创建了一个st对象,这个st对象sb属性也就只有一个.所以能保证锁对象的唯一.
synchronized (SellTicket.class) { // 类名.class就是获取这个类在方法区中加载字节文件之后形成字节码文件对象.一个类字节文件只会在方法区中加载一次.这个类字节文件对象只有一个.它也能充当锁对象
if (ticket > 0 ){ // 防止当ticket等于1的时候, 三条线程执行时都满足循环判断
// ticket>0成立,进入循环体. 执行同步代码快操作时出现卖负票的情况
// 卖出一张票数-1
ticket--;
System.out.println(Thread.currentThread().getName() + "---卖出的是第" +(ticket + 1) + "张, 剩余" + ticket + "张");
}
}
}
}
}
// 线程安全问题演示
public class ThreadSafeProblem {
public static void main(String[] args) {
// 卖票任务
SellTicket st = new SellTicket();
// 创建3个线程对象,模拟三个窗口
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");
t1.start();
t2.start();
t3.start();
}
}
修饰符 synchronized 返回值类型 方法名 ( 方法参数 ) {方法体;}
修饰符 返回值类型 方法名 ( 方法参数 ) {synchronized ( this ) {方法体;}}
因为同步成员方法默认的锁对象就是:this
public class SellTicket implements Runnable {
// 定义变量模拟要卖的100张票
private int ticket = 100;
@Override
public void run() {
while (ticket > 0) {
sell();
}
}
// 把卖票的过程抽取出来定义到一个成员方法中
// 咱们写的一个成员方法sell,这个方法内部所有的代码都使用同步代码块给包裹了,并且使用的锁对象是this的时候
// 这种写法可以使用同步成员方法简化
/* public void sell() {
synchronized (this) { // this-->st
if (ticket > 0 ){
ticket--;
System.out.println(Thread.currentThread().getName() + "---卖出的是第" +
(ticket + 1) + "张, 剩余" + ticket + "张");
}
}
}*/
// 这就是同步方法. 同步成员方法的锁对象就是this. 简化的就是上面sell中的代码
public synchronized void sell() {
if (ticket > 0 ){
ticket--;
System.out.println(Thread.currentThread().getName() + "---卖出的是第" + (ticket + 1) + "张, 剩余" + ticket + "张");
}
}
}
public class ThreadSafeProblem {
public static void main(String[] args) {
// 卖票任务
SellTicket st = new SellTicket();
// 创建3个线程对象,模拟三个窗口
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");
t1.start();
t2.start();
t3.start();
}
}
修饰符 static synchronized 返回值类型 方法名 ( 方法参数 ) {方法体;}
修饰符 static 返回值类型 方法名 ( 方法参数 ) {synchronized ( 类名 . class ) {方法体;}}
因为同步静态方法默认值的锁对象就是:当前类的类对象,类名.class
public class SellTicket implements Runnable {
// 定义变量模拟要卖的100张票
private static int ticket = 100;
@Override
public void run() {
while (ticket > 0) {
sell();
}
}
// 定义一个静态方法sell,里面所有的代码都使用同步代码块包裹.并且使用的锁对象是当前类字节码文件对象(当前类的类对象)
/* public static void sell() {
synchronized (SellTicket.class) {
if (ticket > 0 ){
ticket--;
System.out.println(Thread.currentThread().getName() + "---卖出的是第" +
(ticket + 1) + "张, 剩余" + ticket + "张");
}
}
}*/
// 同步静态方法. 默认使用锁对象就是当前类的类对象(SellTicket.class),简化的就是上面sell方法的写法
public static synchronized void sell() {
if (ticket > 0) {
ticket--;
System.out.println(Thread.currentThread().getName() + "---卖出的是第" + (ticket + 1) + "张, 剩余" + ticket + "张");
}
}
}
public class UseSellTicket {
public static void main(String[] args) {
// 创建卖票的任务
SellTicket st = new SellTicket();
// 把任务绑定到三个卖票窗口上, 也就三个窗口共同完成一个任务
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");
t1.start();
t2.start();
t3.start();
}
}
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock,Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化。
ReentrantLock构造方法
方法名 | 说明 |
ReentrantLock() | 创建一个ReentrantLock的实例 |
加锁解锁方法
方法名 | 说明 |
void lock() | 获得锁 |
void unlock() | 释放锁 |
代码示例
public class SellTicket implements Runnable {
// 定义变量模拟要卖的100张票
private int ticket = 100;
// 创建一个锁对象属性
private ReentrantLock l = new ReentrantLock();
@Override
public void run() {
while (ticket > 0) {
l.lock(); // 获取锁对象
if (ticket > 0) {
ticket--;
System.out.println(Thread.currentThread().getName() + "---卖出的是第" +(ticket + 1) + "张, 剩余" + ticket + "张");
}
l.unlock();
}
}
}
// 线程安全问题演示
public class ThreadSafe {
public static void main(String[] args) {
// 卖票任务
SellTicket st = new SellTicket();
// 创建3个线程对象,模拟三个窗口
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");
t1.start();
t2.start();
t3.start();
}
}
无论使用哪种解决方案,原理都是要线程同步,要报原子性操作代码块的完整性,不能中途被打断,加锁同步。
加锁原则:保证锁唯一
线程死锁是指由于两个或者多个线程相互持有对方所需要的锁资源,导致这些线程处于等待状态,无法继续执行。
public class DeadLock {
// 两个锁对象
private static final String l1 = "A";
private static final String l2 = "B";
public static void main(String[] args) {
Thread t1 = new Thread("线程1") {
@Override
public void run() {
synchronized (l1) {
try {
Thread.sleep(20); // 休眠是为了放大死锁现象出现的概率,让t1和t2更容易交替执行
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (l2) {
System.out.println("任务1");
}
}
}
};
Thread t2 = new Thread("线程2") {
@Override
public void run() {
synchronized (l2) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (l1) {
System.out.println("任务2");
}
}
}
};
t1.start();
t2.start();
}
}
线程生命周期,线程对象从生到死一个过程,当线程被创建并启动以后,他既不是一启动就进入了执行状态,也不是一直处于执行状态。线程对象在不同的时期有不同的状态。
通过java代码的方式。获取实际中线程在程序中会出现的各种状态。在Thread类中提供有一个方法:getState()。此方法就可以返回线程对象状态。
线程状态 | 具体含义 |
NEW |
一个尚未启动的线程的状态。也称之为初始状态、开始状态。
线程刚被创建,但是并未启动。还没调用start方法。MyThread t = new MyThread()只有线程对象,没有启动线程。
|
RUNNABLE |
当我们调用线程对象的start方法,CPU执行线程中任务的时候。那么此时线程对象进入了RUNNABLE状态。
|
BLOCKED |
当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入BLOCKED状态;当该线程持有锁时,该线程将变成Runnable状态。
|
WAITING |
一个正在等待的线程的状态。也称之为等待状态。造成线程等待的原因有两种,分别是调用Object.wait()、join()方法。处于等待状态的线程,正在等待其他线程去执行一个特定的操作。例如:因为wait()而等待的线程正在等待另一个线程去调用notify()或notifyAll();一个因为join()而等待的线程正在等待另一个线程结束。
|
TIMED_WAITING |
一个在限定时间内等待的线程的状态。也称之为限时等待状态。造成线程限时等待状态的原因有三种,分别是:
Thread.sleep(long),Object.wait(long)、join(long)。
|
TERMINSTED |
一个完全运行完成的线程的状态。也称之为终止状态、结束状态
|
submit(Runnable r):提交线程任务到线程池执行
submit(Callable c):提交线程任务到线程池执行线程池一般是不会关闭了,因为要复用这个线程池管理线程对象,线程池不关闭程序不会停止.
shutdown():关闭线程池
shutdownNow():关闭线程池
newCachedThreadPool()获取线程池
public static void reuseThread() {
// Executors是java提供一个工具类,通过该类就可以获取java中给我们准备一些
// 不同作用和特点线程池对象.
// 获取一个能缓存线程对象的线程池.
ExecutorService pool = Executors.newCachedThreadPool();
for (int i = 1; i <= 100; i++) {
// Runnable的run方法中的内容将来要在别的线程中执行.变量i是在主线程中定义的.变量的使用是不能夸线程的.
// 之所不让跨线程使用的原因.多个线程操作同一个数据有安全隐患.
final int j = i;
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"----任务"+j);
}
};
pool.submit(r);
}
}
public static void simpleThreadPool1() {
// 获取一个能缓存线程对象的线程池.
ExecutorService pool = Executors.newCachedThreadPool();
// 准备三个任务
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"----任务1"); // pool-1-thread-1----任务1
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"----任务2"); // pool-1-thread-2----任务2
}
};
Runnable r3 = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"----任务3"); // pool-1-thread-3----任务3
}
};
// 任务提交到线程池
pool.submit(r1);
pool.submit(r2);
pool.submit(r3);
// 线程池不关闭程序不会停止.
}
newFixedThreadPool(int Thread)获取线程池
public static void useNewFixedThreadPool() {
// Executors是java提供一个工具类,通过该类就可以获取java中给我们准备一些
// 不同作用和特点线程池对象.
// 获取一个能缓存线程对象的线程池.
ExecutorService pool = Executors.newFixedThreadPool(3);
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"----任务1");
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"----任务2");
}
};
Runnable r3 = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"----任务3");
}
};
Runnable r4 = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"----任务4");
}
};
pool.submit(r1);
pool.submit(r2);
pool.submit(r3);
pool.submit(r4);
}
newSingleThreadExecutor()获取线程池
public static void useNewSingleThreadExector() {
// 获取只有一个线程对象线程池
ExecutorService pool = Executors.newSingleThreadExecutor();
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"----任务1");
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"----任务2");
}
};
pool.submit(r1);
pool.submit(r2);
}