进程
指一个内存中运行的应用程序,一个进程独占一个独立的空间。
线程
是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程。
线程实际上是在进程基础上的进一步划分,一个进程执行后,里面的若干执行路径又可以划分成若干个线程
线程调度
分时调度
所有线程轮流使用cpu, 平均分配每个线程占用cpu的时间。
抢占式调度
优先让优先级高的线程使用cpu,如果优先级相同就随机分配,java用的是抢占式调度。
CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻,
只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是 在同一时
刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的 使
用率更高。
同步与异步
同步:排队执行,效率低但安全
异步:同时执行,效率高但不安全
并发与并行
并发:多个事情在同一时间段执行
并行:多个事情在同一时刻执行
继承Thread
- 有两种方法可以创建新的执行线程。 一种是将类声明为
Thread
的子类。 此子类应覆盖类Thread
的run
方法。 然后可以分配和启动子类的实例。 - 当在某个线程中运行的代码创建一个新的
Thread
对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护进程线程。 - 每个线程都有自己的栈空间,共享一个堆空间。由一个线程所调用的方法,这个方法会在这个线程里执行。
字段
变量和类型 | 字段 | 描述 |
---|---|---|
static int |
[MAX_PRIORITY](#MAX_PRIORITY) |
线程可以拥有的最大优先级。 |
static int |
[MIN_PRIORITY](#MIN_PRIORITY) |
线程可以拥有的最低优先级。 |
static int |
[NORM_PRIORITY](#NORM_PRIORITY) |
分配给线程的默认优先级。 |
方法摘要
变量和类型 | 方法 | 描述 |
---|---|---|
static int |
[activeCount](#activeCount())() |
返回当前线程thread group及其子组中活动线程数的估计值。 |
void |
[checkAccess](#checkAccess())() |
确定当前运行的线程是否具有修改此线程的权限。 |
protected [Object](Object.html) |
[clone](#clone())() |
抛出CloneNotSupportedException,因为无法有意义地克隆线程。 |
int |
[countStackFrames](#countStackFrames())() |
不推荐使用,要删除:此API元素将在以后的版本中删除。 此调用的定义取决于 suspend() ) ,已弃用。 |
static [Thread](Thread.html) |
[currentThread](#currentThread())() |
返回对当前正在执行的线程对象的引用。 |
static void |
[dumpStack](#dumpStack())() |
将当前线程的堆栈跟踪打印到标准错误流。 |
static int |
[enumerate](#enumerate(java.lang.Thread%5B%5D))([Thread](Thread.html)[] tarray) |
将当前线程的线程组及其子组中的每个活动线程复制到指定的数组中。 |
static [Map](../util/Map.html)<[Thread](Thread.html),[StackTraceElement](StackTraceElement.html)[]> |
[getAllStackTraces](#getAllStackTraces())() |
返回所有活动线程的堆栈跟踪映射。 |
[ClassLoader](ClassLoader.html) |
[getContextClassLoader](#getContextClassLoader())() |
返回此线程的上下文 ClassLoader 。 |
static [Thread.UncaughtExceptionHandler](Thread.UncaughtExceptionHandler.html) |
[getDefaultUncaughtExceptionHandler](#getDefaultUncaughtExceptionHandler())() |
返回由于未捕获的异常而导致线程突然终止时调用的默认处理程序。 |
long |
[getId](#getId())() |
返回此Thread的标识符。 |
[String](String.html) |
[getName](#getName())() |
返回此线程的名称。 |
int |
[getPriority](#getPriority())() |
返回此线程的优先级。 |
[StackTraceElement](StackTraceElement.html)[] |
[getStackTrace](#getStackTrace())() |
返回表示此线程的堆栈转储的堆栈跟踪元素数组。 |
[Thread.State](Thread.State.html) |
[getState](#getState())() |
返回此线程的状态。 |
[ThreadGroup](ThreadGroup.html) |
[getThreadGroup](#getThreadGroup())() |
返回此线程所属的线程组。 |
[Thread.UncaughtExceptionHandler](Thread.UncaughtExceptionHandler.html) |
[getUncaughtExceptionHandler](#getUncaughtExceptionHandler())() |
返回此线程由于未捕获的异常而突然终止时调用的处理程序。 |
static boolean |
[holdsLock](#holdsLock(java.lang.Object))([Object](Object.html) obj) |
当且仅当当前线程在指定对象上保存监视器锁时,返回 true 。 |
void |
[interrupt](#interrupt())() |
中断此线程。 |
static boolean |
[interrupted](#interrupted())() |
测试当前线程是否已被中断。 |
boolean |
[isAlive](#isAlive())() |
测试此线程是否存活。 |
boolean |
[isDaemon](#isDaemon())() |
测试此线程是否为守护程序线程。 |
boolean |
[isInterrupted](#isInterrupted())() |
测试此线程是否已被中断。 |
void |
[join](#join())() |
等待这个线程死亡。 |
void |
[join](#join(long))(long millis) |
此线程最多等待 millis 毫秒。 |
void |
[join](#join(long,int))(long millis, int nanos) |
此线程最多等待 millis 毫秒加上 nanos 纳秒。 |
static void |
[onSpinWait](#onSpinWait())() |
表示调用者暂时无法进展,直到其他活动发生一个或多个操作为止。 |
void |
[resume](#resume())() |
已过时。 此方法仅适用于 suspend() ) ,由于它易于死锁,因此已被弃用。 |
void |
[run](#run())() |
如果此线程是使用单独的Runnable 运行对象构造的,则调用该Runnable 对象的run 方法; 否则,此方法不执行任何操作并返回。 |
void |
[setContextClassLoader](#setContextClassLoader(java.lang.ClassLoader))([ClassLoader](ClassLoader.html) cl) |
为此Thread设置上下文ClassLoader。 |
void |
[setDaemon](#setDaemon(boolean))(boolean on) |
将此线程标记为 daemon)线程或用户线程。 |
static void |
[setDefaultUncaughtExceptionHandler](#setDefaultUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler))([Thread.UncaughtExceptionHandler](Thread.UncaughtExceptionHandler.html) eh) |
设置当线程由于未捕获的异常而突然终止时调用的默认处理程序,并且没有为该线程定义其他处理程序。 |
void |
[setName](#setName(java.lang.String))([String](String.html) name) |
将此线程的名称更改为等于参数 name 。 |
void |
[setPriority](#setPriority(int))(int newPriority) |
更改此线程的优先级。 |
void |
[setUncaughtExceptionHandler](#setUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler))([Thread.UncaughtExceptionHandler](Thread.UncaughtExceptionHandler.html) eh) |
设置当此线程由于未捕获的异常而突然终止时调用的处理程序。 |
static void |
[sleep](#sleep(long))(long millis) |
导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序的精度和准确性。 |
static void |
[sleep](#sleep(long,int))(long millis, int nanos) |
导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数加上指定的纳秒数,具体取决于系统定时器和调度程序的精度和准确性。 |
void |
[start](#start())() |
导致此线程开始执行; Java虚拟机调用此线程的run 方法。 |
[String](String.html) |
[toString](#toString())() |
返回此线程的字符串表示形式,包括线程的名称,优先级和线程组。 |
static void |
[yield](#yield())() |
向调度程序提示当前线程是否愿意产生其当前使用的处理器。 |
public class Demo1 {
/**
* 多线程技术
* @param args
*/
public static void main(String[] args) {
MyTherd m = new MyTherd();
m.start();
for(int i = 0; i < 10 ;i++){
System.out.println("B");
}
}
}
public class MyTherd extends Thread {
/**
* run方法就是线程要执行的任务方法
*
*/
@Override
public void run() {
//这里的代码,就是一个新的执行路劲
//执行路劲的调用方式,通过therd对象的start()来启动rw
for(int i = 0; i < 10 ;i++){
System.out.println("A");
}
}
实现Runnable
实现
Runnable
通过实例化Thread
实例并将其自身作为目标传递而无需子类化Thread
。 在大多数情况下,如果您只打算覆盖run()
方法而不使用其他Thread
方法,则应使用Runnable
接口。** * 用于给线程执行的任务 */ public class Myrunnable implements Runnable { @Override public void run() { //线程的任务 for(int i = 0; i < 10;i++){ System.out.println("A"); } } }
``` ublic class Demo2 { public static void main(String[] args) {
//创建一个任务对象 Myrunnable r = new Myrunnable(); //创建一个线程,并为其分配任务m Thread t = new Thread(r); //执行线程 t.start(); for(int i = 0; i < 10;i++){ System.out.println("B"); }
} }
<a name="o0Cdi"></a>
### 实现Runnable接口比继承Therd有如下优势:
1.通过创建任务,通过给线程分配的方式来实现多线程,更适合多个线程同时执行相同任务的情况。<br />2.可避免单继承带来的局限性<br />3.任务与线程本身是分开的,提高了程序的健壮性<br />4.线程池计数接受Runnable
<a name="0dpnn"></a>
## 设置和获取线程名称
| `static [Thread](Thread.html)` | `[currentThread](#currentThread())()` | 返回对当前正在执行的线程对象的引用。 |
| --- | --- | --- |
| `[String](String.html)` | `[getName](#getName())()` | 返回此线程的名称。 |
| --- | --- | --- |
public class Demo3 { public static void main(String[] args) { System.out.println(Thread.currentThread().getName()); new Thread(new MyRunnable(),”1号”).start();//设置名称 Thread thread = new Thread(new MyRunnable()); thread.setName(“2号”); thread.start(); new Thread(new MyRunnable()).start(); //结果: // main // 1号 // 2号 // Thread-1 } static class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());//返回当前线程的名称
}
}
}
<a name="QsKE3"></a>
## 线程休眠
| `static void` | `[sleep](#sleep(long))(long millis)` | 导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序的精度和准确性。 |
| --- | --- | --- |
| `static void` | `[sleep](#sleep(long,int))(long millis, int nanos)` | 导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数加上指定的纳秒数,具体取决于系统定时器和调度程序的精度和准确性。 |
public class Democratic { public static void main(String[] args) throws InterruptedException { for(int i = 0; i < 10; i++){ System.out.println(i); Thread.sleep(1000); //结果:每隔1s输出一个i } } }
<a name="feUuk"></a>
## 线程阻塞
表示线程当前暂停执行的状态
<a name="XFF5o"></a>
## 线程的中断
一个线程是否中断,由其自身决定。
public class Demo3 {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
t1.start();
for(int i = 0; i < 5;i++){
System.out.println(Thread.currentThread().getName()+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//给线程t1添加中断笔记
t1.interrupt();
//结果:Thread-00
//main0
//main1
//Thread-01
//Thread-02
//main2
//Thread-03
//main3
//main4
//Thread-04
//Thread-05
//中断
}
static class MyRunnable implements Runnable{
@Override
public void run() {
for(int i = 0; i < 10;i++){
System.out.println(Thread.currentThread().getName()+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// e.printStackTrace(); //通过return中断 System.out.println(“中断”); return; } } } } }
<a name="GJKxh"></a>
## 守护线程
线程可分为用户线程(user thread) 和 守护线程(daemon thread)。<br />守护线程指在后台运行的线程,也称为后台线程,用于提供后台服务。<br />Java创建的线程默认是用户线程。<br />守护线程:当一个进程不含任务用户线程时,进程结束<br />守护线程:守护用户线程的,最后一个用户线程结束时,所有守护线程自动死亡
public class Demo3 { public static void main(String[] args) { Thread t1 = new Thread(new MyRunnable()); //通过t1.setDaemon(true);设置t1为守护线程 t1.setDaemon(true); t1.start(); for(int i = 0; i < 5;i++){ System.out.println(Thread.currentThread().getName()+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } static class MyRunnable implements Runnable{
@Override
public void run() {
for(int i = 0; i < 10; i++){
System.out.println(Thread.currentThread().getName()+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
// 结果: // Thread-00 // main0 // main1 // Thread-01 // main2 // Thread-02 // Thread-03 // main3 // main4 // Thread-04 // Thread-05
}
}
}
}
}
<a name="D754C"></a>
## 线程安全
3个线程同时启动同时对count--进行操作出现了余票为-1的情况
public class Demo5 { public static void main(String[] args) { Runnable runnable = new Ticket(); new Thread(runnable).start(); new Thread(runnable).start(); new Thread(runnable).start();
}
static class Ticket implements Runnable{
private int count = 10;
@Override
public void run() {
//票数
while (count > 0){
System.out.println("正在出票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("出票成功,余票:"+count);
//结果
// 出票成功,余票:-1
//出票成功,余票:-1
//出票成功,余票:0
}
}
}
}
<a name="EEGly"></a>
### 解决方法
<a name="MBSsA"></a>
#### 1.线程同步代码块
通过synchronized (o)整了一把锁o3个进程在抢票时先抢到的进去,后面的线程先排队等待在进去的线程抢到票后,再3个进程一起抢,因为先进去的离锁最近所以能抢到最多的票
public class Demo5 { public static void main(String[] args) { Runnable runnable = new Ticket(); new Thread(runnable).start(); new Thread(runnable).start(); new Thread(runnable).start();
}
static class Ticket implements Runnable{
private int count = 10;
private Object o = new Object();
@Override
public void run() {
//票数
while (true){
synchronized (o){
if(count > 0){
System.out.println("正在出票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票:"+count);
}else {
break;
}
}
}
}
}
}
[运行结果.png](https://www.yuque.com/attachments/yuque/0/2020/png/2210656/1597305170197-26df062b-3371-4181-b188-0398717f2091.png?_lake_card=%7B%22uid%22%3A%221597305168764-0%22%2C%22src%22%3A%22https%3A%2F%2Fwww.yuque.com%2Fattachments%2Fyuque%2F0%2F2020%2Fpng%2F2210656%2F1597305170197-26df062b-3371-4181-b188-0398717f2091.png%22%2C%22name%22%3A%22%E8%BF%90%E8%A1%8C%E7%BB%93%E6%9E%9C.png%22%2C%22size%22%3A19465%2C%22type%22%3A%22image%2Fpng%22%2C%22ext%22%3A%22png%22%2C%22progress%22%3A%7B%22percent%22%3A99%7D%2C%22status%22%3A%22done%22%2C%22percent%22%3A0%2C%22id%22%3A%22moCbs%22%2C%22card%22%3A%22file%22%7D)
<a name="1j6gT"></a>
#### 2.同步方法
用 synchronized修饰方法,来完成排队。同步方法没用static修饰的, synchronized锁的是this,用static修饰的锁的是(类名.class) 。同步方法也需排队。
public class Demo5 { public static void main(String[] args) { Runnable runnable = new Ticket(); new Thread(runnable).start(); new Thread(runnable).start(); new Thread(runnable).start();
}
static class Ticket implements Runnable{
private int count = 10;
@Override
public void run() {
while (true){
boolean flag = sale();
if(!flag){
break;
}
}
}
public synchronized Boolean sale(){
if(count > 0){
System.out.println("正在出票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票:"+count);
return true;
}
return false;
}
}
}
<a name="Bcn0E"></a>
#### 3.方法3 显示锁Lock
同步方法、同步代码块是隐示锁,是非公平锁。显示锁Lock在创建锁时,加true是公平锁。
public class Demo5 { public static void main(String[] args) { Runnable runnable = new Ticket(); new Thread(runnable).start(); new Thread(runnable).start(); new Thread(runnable).start();
}
static class Ticket implements Runnable{
private int count = 10;
//创建一个锁
Lock l = new ReentrantLock();
@Override
public void run() {
//票数
while (count > 0){
//锁住
l.lock();
if(count > 0){
System.out.println("正在出票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票:"+count);
}else {
break;
}
//解锁
l.unlock();
}
}
}
}
<a name="iUtXo"></a>
#### 4.公平锁:显示锁Lock在创建锁时,加true
public class Demo5 { public static void main(String[] args) { Runnable runnable = new Ticket(); new Thread(runnable).start(); new Thread(runnable).start(); new Thread(runnable).start();
}
static class Ticket implements Runnable{
private int count = 10;
//创建一个锁
Lock l = new ReentrantLock(true);//加true
@Override
public void run() {
//票数
while (count > 0){
//锁住
l.lock();
if(count > 0){
System.out.println("正在出票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票:"+count);
}else {
break;
}
//解锁
l.unlock();
}
}
}
}
[公平锁运行结果.png](https://www.yuque.com/attachments/yuque/0/2020/png/2210656/1597308489627-46da316d-f8d9-44d7-9edd-d55395868b3a.png?_lake_card=%7B%22uid%22%3A%221597308488083-0%22%2C%22src%22%3A%22https%3A%2F%2Fwww.yuque.com%2Fattachments%2Fyuque%2F0%2F2020%2Fpng%2F2210656%2F1597308489627-46da316d-f8d9-44d7-9edd-d55395868b3a.png%22%2C%22name%22%3A%22%E5%85%AC%E5%B9%B3%E9%94%81%E8%BF%90%E8%A1%8C%E7%BB%93%E6%9E%9C.png%22%2C%22size%22%3A19707%2C%22type%22%3A%22image%2Fpng%22%2C%22ext%22%3A%22png%22%2C%22progress%22%3A%7B%22percent%22%3A99%7D%2C%22status%22%3A%22done%22%2C%22percent%22%3A0%2C%22id%22%3A%22sTFV8%22%2C%22card%22%3A%22file%22%7D)
<a name="ojh9j"></a>
#### 5.公平锁和非公平锁
公平锁:顾名思义,它是公平的,可以保证获取锁的线程按照先来后到的顺序,获取到锁。<br />非公平锁:顾名思义,各个线程获取到锁的顺序,不一定和它们申请的先后顺序一致,有可能后来的线程,反而先获取到了锁。
<a name="OtIRq"></a>
##### 优缺点:
非公平锁性能高于公平锁性能。首先,在恢复一个被挂起的线程与该线程真正运行之间存在着严重的延迟。而且,非公平锁能更充分的利用cpu的时间片,尽量的减少cpu空闲的状态时间。
<a name="AJ06t"></a>
##### 使用场景
如果业务中线程占用(处理)时间要远长于线程等待,那用非公平锁其实效率并不明显,但是用公平锁会给业务增强很多的可控制性。
<a name="WFfMJ"></a>
## 线程锁死
解决方案:在能产生锁的方法里不调用另外一个能产生锁的方法
public class Demo6 { public static void main(String[] args) { //线程锁死 Police p = new Police(); Culprint c = new Culprint(); MyThread myThread = new MyThread(c,p); myThread.start(); c.say(p); //结果输出罪犯:你放了我,我放了人质 //警察:你放了人质,我放了你 //程序卡着不动了
}
static class MyThread extends Thread{
private Culprint c;
private Police p;
public MyThread(Culprint c,Police p){
this.c = c;
this.p = p;
}
@Override
public void run() {
p.say(c);
}
}
static class Culprint{
public synchronized void say(Police p){
System.out.println("罪犯:你放了我,我放了人质");
p.fun();
}
public synchronized void fun(){
System.out.println("罪犯跑了,放了人质");
}
}
static class Police{
public synchronized void say(Culprint c){
System.out.println("警察:你放了人质,我放了你");
c.fun();
}
public synchronized void fun(){
System.out.println("救了人质,但罪犯跑了");
}
}
}
<a name="oSO0P"></a>
## 多线程通信
当多线程通信出现数据错乱问题时,可以调用wait方法和notify方法解决。
| `void` | `[wait](#wait())()` | 导致当前线程等待它被唤醒,通常是 _通知_或 _中断_ 。 |
| --- | --- | --- |
| `void` | `[wait](#wait(long))(long timeoutMillis)` | 导致当前线程等待它被唤醒,通常是 _通知_或 _中断_ ,或者直到经过一定量的实时。 |
| `void` | `[wait](#wait(long,int))(long timeoutMillis, int nanos)` | 导致当前线程等待它被唤醒,通常是 _通知_或 _中断_ ,或者直到经过一定量的实时。 |
| `void` | `[notify](#notify())()` | 唤醒正在此对象监视器上等待的单个线程。 |
| --- | --- | --- |
| `void` | `[notifyAll](#notifyAll())()` | 唤醒等待此对象监视器的所有线程。 |
| `[String](String.html)` | `[toString](#toString())()` | 返回对象的字符串表示形式。 |
public class Demo7 { public static void main(String[] args) { Food f = new Food(); new Cook(f).start(); new Waiter(f).start();
}
//厨师
static class Cook extends Thread{
private Food f;
public Cook(Food f){
this.f = f;
}
@Override
public void run() {
for(int i = 0; i < 100;i++){
if(i%2 == 0){
f.setNamesetTaste("西红柿炒鸡蛋","甜咸");
}else{
f.setNamesetTaste("麻婆豆腐","香辣");
}
}
}
}
//服务员
static class Waiter extends Thread{
private Food f;
public Waiter(Food f){
this.f = f;
}
@Override
public void run() {
for(int i = 0; i < 100;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
f.get();
}
}
}
//食物
static class Food{
private String name;
private String taste;
private Boolean flag =true;
public synchronized void setNamesetTaste(String name,String taste) {
if(flag){
this.name = name;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.taste = taste;
flag = false;
this.notifyAll();//唤醒其他线程
try {
this.wait();//关闭厨师线程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void get(){
if(!flag){
System.out.println("服务生端"+name+"味道"+taste);
flag = true;
this.notifyAll();//唤醒其他线程
try {
this.wait();//关闭服务员线程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
<a name="CxVpJ"></a>
## 线程的六种状态
1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
1. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。<br />线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
1. 阻塞(BLOCKED):表示线程阻塞于锁。
1. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
1. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
1. 终止(TERMINATED):表示该线程已经执行完毕。

<a name="ZlO4Y"></a>
## 带返回值的线程Callable
<a name="TIyfj"></a>
#### Runnable 与 Callable
接口定义 //
Callable接口
public interface Callable
//Runnable接口 public interface Runnable { public abstract void run(); }
<a name="wyFkc"></a>
#### Callable使用步骤
- 编写类实现Callable接口 , 实现call方法
class XXX implements Callable<T> { @Override public <T> call() throws Exception { return T; } }
- 创建FutureTask对象 , 并传入第一步编写的Callable类对象
FutureTask<Integer> future = new FutureTask<>(callable);
- 通过Thread,启动线程
```new Thread(future).start();
Runnable 与 Callable的相同点
都是接口
都可以编写多线程程序
都采用Thread.start()启动线程Runnable 与 Callable的不同点
Runnable没有返回值;Callable可以返回执行结果
Callable接口的call()允许抛出异常;Runnable的run()不能抛出Callable获取返回值
Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执
行,如果不调用不会阻塞。
线程池 Executors
**
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程 就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容 器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。线程池就应该线程数组。
线程池的好处
降低资源消耗。
提高响应速度。
提高线程的可管理性
Java中的四种线程池 . ExecutorService
1. 缓存线程池
缓存线程池.
(长度无限制)
执行流程:
1. 判断线程池是否存在空闲线程
2. 存在则使用
3. 不存在,则创建线程 并放入线程池, 然后使用
public class Demo8 {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
//指挥线程池执行新的任务
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"A");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"A");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"A");
}
});
try {
Thread.sleep(1000);//1s后执行的是线程2没有新的线程执行
} catch (InterruptedException e) {
e.printStackTrace();
}
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"A");
}
});
}
}
2. 定长线程池
定长线程池.
长度是指定的数值)
执行流程:
1. 判断线程池是否存在空闲线程
2. 存在则使用
3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
public class Demo9 {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2);
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//结果:线程的名称:pool-1-thread-2
//线程的名称:pool-1-thread-1
//线程的名称:pool-1-thread-2
}
}
3. 单线程线程池
效果与定长线程池 创建时传入数值1 效果一致.
单线程线程池.
执行流程:
1. 判断线程池 的那个线程 是否空闲
2. 空闲则使用
4. 不空闲,则等待 池中的单个线程空闲后 使用
ublic class Demo10 {
public static void main(String[] args) {
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
//结果:线程的名称:pool-1-thread-1
//线程的名称:pool-1-thread-1
//线程的名称:pool-1-thread-1
}
}
4. 周期性任务定长线程池
周期任务 定长线程池.
执行流程:
1. 判断线程池是否存在空闲线程
2. 存在则使用
3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
public class Demo11 {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
/*** 定时执行 *
* 参数1. runnable类型的任务 *
* 参数2. 时长数字 *
* 参数3. 时长数字的单位
* */
// service.schedule(new Runnable() {
// @Override
// public void run() {
// System.out.println("线程的名称:"+Thread.currentThread().getName());
// }
// },5, TimeUnit.SECONDS);//5s后执行一次
/*** 周期执行 *
* 参数1. runnable类型的任务
* 参数2. 时长数字(延迟执行的时长)
* 参数3. 周期时长(每次执行的间隔时间)
* 参数4. 时长数字的单位
* */
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
},5,1,TimeUnit.SECONDS);//5s后执行,每1s执行一次
}
}
Lambda表达式
函数式编程,不关注过程,只注重结果
注:接口只有用户抽象方法时才用Lambda表达式
public class Demo12 {
public static void main(String[] args) {
// print(new MyMath() {
// @Override
// public int sum(int x, int y) {//把匿名内部类删除之后变成Lambda表达式
// return x+y;
// }
// },100,200);
//Lambda表达式
print((int x, int y) -> {
return x+y;
},100,200);
}
public static void print(MyMath myMath,int x,int y){
int num = myMath.sum(x,y);
System.out.println(num);
}
static interface MyMath{
int sum(int x,int y);
}
}