new thread:
//构造方法给指定的线程指定名称,推荐
Thread thread = new Thread(t1) {
@Override
public void run() {
//要执行的任务
}
};
//启动线程
thread.start();
new runnable:
Runnable runnable = new Runnable() {
@Override
public void run() {
//要执行的任务
}
};
Thread thread = new Thread(runnable);
thread.start();
Future
FutureTask<Integer> task = new FutureTask<>(() -> {
System.out.println(执行的任务);
return 100;
});
new Thread(task,t1).start();
Integer integer = task.get();
System.out.println(结果是+integer);
其实就是多个线程之间在操作同一个资源,但是操作的动作不同
就比如下图,一个线程进行set方法给属性赋值,另一个线程get方法获取属性的值
而本章中要学习的就是:如何保证线程每写一个值,读线程便同步获取写入的那个值;
package com.disney;
class Res {
public String name;
public String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
/**
* 写入的线程
*
* 当为偶数为男孩,奇数为女孩
*/
class InputThread extends Thread {
private Res res;
public InputThread(Res res ) {
this.res=res;
}
@Override
public void run() {
int count = 0;
while (true){
if (count==0){
res.name="william";
res.sex="boy";
} else{
res.name="tom";
res.sex="girl";
}
//得到count+1的模
count=(count+1)%2;
}
}
}
/**
* 读取的线程
*/
class OutPutThread extends Thread{
private Res res;
public OutPutThread(Res res ) {
this.res=res;
}
@Override
public void run() {
while (true){
System.out.println(res.name+"======"+res.getSex());
}
}
}
public class MainDemo{
public static void main(String[] args) {
Res res = new Res();
OutPutThread outPutThread = new OutPutThread(res);
outPutThread.start();
InputThread inputThread = new InputThread(res);
inputThread.start();
}
}
输出结果
根据现实的结果我们可以看出,本来我们是william和boy在一组进行设置 ,但是发现在进行读的时候,数据发生了交叉,以上的数据就是线程通讯不安全的表现,接下来讲解如何解决这个问题
package com.disney;
class Res {
public String name;
public String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
/**
* 写入的线程
*
* 当为偶数为男孩,奇数为女孩
*/
class InputThread extends Thread {
private Res res;
public InputThread(Res res ) {
this.res=res;
}
@Override
public void run() {
int count = 0;
while (true){
synchronized (res){
if (count==0){
res.name="william";
res.sex="boy";
} else{
res.name="tom";
res.sex="girl";
}
//得到count+1的模
count=(count+1)%2;
}
}
}
}
/**
* 读取的线程
*/
class OutPutThread extends Thread{
private Res res;
public OutPutThread(Res res ) {
this.res=res;
}
@Override
public void run() {
while (true){
synchronized (res) {
System.out.println(res.name + "======" + res.getSex());
}
}
}
}
public class MainDemo{
public static void main(String[] args) {
Res res = new Res();
OutPutThread outPutThread = new OutPutThread(res);
outPutThread.start();
InputThread inputThread = new InputThread(res);
inputThread.start();
}
}
输出结果
可以看出来的确姓名和性别之间的对应关系有了匹配,但是并不是符合 我们 输出william->toim->william-tom这样逐一变化的规律
wait()、notify()、notifyAll()是三个定义在Object类里的方法,可以用来控制线程的状态。
这三个方法最终调用的都是jvm级的native方法。随着jvm运行平台的不同可能有些许差异。
• 如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。
• 如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。
• 如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。
package com.disney;
class Res {
public String name;
public String sex;
public boolean flag =false;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
/**
* 写入的线程
*
* 当为偶数为男孩,奇数为女孩
*/
class InputThread extends Thread {
private Res res;
public InputThread(Res res ) {
this.res=res;
}
@Override
public void run() {
int count = 0;
while (true){
synchronized (res){
if (res.flag){
try {
res.wait();
} catch (InterruptedException e) {
}
}
if (count==0){
res.name="william";
res.sex="boy";
} else{
res.name="tom";
res.sex="girl";
}
count=(count+1)%2;
res.flag=true;
//得到count+1的模
res.notify();
}
}
}
}
/**
* 读取的线程
*/
class OutPutThread extends Thread{
private Res res;
public OutPutThread(Res res ) {
this.res=res;
}
@Override
public void run() {
while (true){
synchronized (res) {
if (!res.flag) {
try {
res.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(res.name + "======" + res.getSex());
res.flag=false;
res.notify();
}
}
}
}
public class MainDemo{
public static void main(String[] args) {
Res res = new Res();
OutPutThread outPutThread = new OutPutThread(res);
InputThread inputThread = new InputThread(res);
outPutThread.start();
inputThread.start();
}
}
在 jdk1.5 之后,并发包中新增了 Lock 接口(以及相关实现类)用来实现锁功能,Lock 接口提供了与 synchronized 关键字类似的同步功能,但需要在使用时手动获取锁和释放锁。
Local的作用其实就类似synchronized 。但是因为wait是和synchronized 搭配使用,此时我们使用lock就需要一个能代替wait的方法,此时
Condition 的就诞生啦‘’
package com.disney;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Res {
public String name;
public String sex;
public boolean flag =false;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
/**
* 写入的线程
*
* 当为偶数为男孩,奇数为女孩
*/
class InputThread extends Thread {
private Res res;
public InputThread(Res res ) {
this.res=res;
}
@Override
public void run() {
int count = 0;
while (true){
res.lock.lock();
if (res.flag){
try {
res.condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (count==0){
res.name="william";
res.sex="boy";
} else{
res.name="tom";
res.sex="girl";
}
count=(count+1)%2;
res.flag=true;
//得到count+1的模
res.condition.signal();
res.lock.unlock();
}
}
}
/**
* 读取的线程
*/
class OutPutThread extends Thread{
private Res res;
public OutPutThread(Res res ) {
this.res=res;
}
@Override
public void run() {
while (true){
if (!res.flag) {
res.lock.lock();
try {
res.condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(res.name + "======" + res.getSex());
res.flag=false;
res.condition.signal();
res.lock.unlock();
}
}
}
public class MainDemo{
public static void main(String[] args) {
Res res = new Res();
OutPutThread outPutThread = new OutPutThread(res);
InputThread inputThread = new InputThread(res);
outPutThread.start();
inputThread.start();
}
}
优点: Future配合线程池能够显著提高程序的执行效率
public static void main(String[] args) throws ExecutionException, InterruptedException {
long startTime = System.currentTimeMillis();
ExecutorService executorService = Executors.newFixedThreadPool(3);
FutureTask<String> stringFutureTask1 = new FutureTask<>(() -> {
TimeUnit.SECONDS.sleep(5);
return over;
});
executorService.submit(stringFutureTask1);
FutureTask<String> stringFutureTask2 = new FutureTask<>(() -> {
TimeUnit.SECONDS.sleep(3);
return over;
});
executorService.submit(stringFutureTask2);
FutureTask<String> stringFutureTask3 = new FutureTask<>(() -> {
TimeUnit.SECONDS.sleep(3);
return over;
});
executorService.submit(stringFutureTask3);
System.out.println(stringFutureTask1.get());
System.out.println(stringFutureTask2.get());
System.out.println(stringFutureTask3.get());
executorService.shutdown();
long endTime = System.currentTimeMillis();
System.out.println(耗时了+(endTime-startTime));
}
输出结果:
over
over
over
耗时了5084
可以看到如果是串行输出,结果是5s+3s+3s的耗时;
从上面的程序,我必须得到stringFutureTask1
执行完后,主线程才能执行任务, 输出耗时时间,我们的期望是 stringFutureTask1在耗时5s的时间内
,主线程忙其他事情, 并询问下stringFutureTask1
是否执行完毕, 如果执行完毕,则输出耗时时间;
比如我们最多只能等待5s, 但是如果get()方法执行10s的话, 则会影响我们的程序
public static void main(String[] args) throws ExecutionException, InterruptedException {
long startTime = System.currentTimeMillis();
ExecutorService executorService = Executors.newFixedThreadPool(3);
FutureTask<String> stringFutureTask1 = new FutureTask<>(() -> {
TimeUnit.SECONDS.sleep(5);
return over;
});
executorService.submit(stringFutureTask1);
executorService.shutdown();
while (true) {
if (stringFutureTask1.isDone()){
long endTime = System.currentTimeMillis();
System.out.println(耗时了 + (endTime - startTime));
}
}
}
对于真正的异步处理我们希望可以通过传入回调函数,在Future结束时自动调用该函数;这样我们就不用等待结果了;
public class CompletableFuture implements Future, CompletionStage
CompletionStage
代表计算过程的一个阶段,一个阶段完成以后可能会触发另一个阶段;
一个阶段的计算,可以是一个function,consumer或者runnable. 比如stage.thenApply(x->square(x)).thenAccept(x->system.out.print(x)).thenReturn(()->system.out.println())
一个阶段的执行可能是被单个阶段的完成触发,也可能是由多个阶段一起触发;
返回值 | 具体方法 |
---|---|
static CompletableFuture | runAsync(Runnable runnable) |
static CompletableFuture | runAsync(Runnable runnable, Executor executor) |
代码示例
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
},executorService);
System.out.println(voidCompletableFuture.get());
}
返回值 | 具体方法 |
---|---|
static CompletableFuture | supplyAsync(Supplier supplier) |
static CompletableFuture | supplyAsync(Supplier supplier, Executor executor) |
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return over;
}, executorService);
System.out.println(stringCompletableFuture.get());<br></br> System.out.println(stringCompletableFuture.jion());
}
输出结果
pool-1-thread-1
over
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return over;
}, executorService).whenComplete((v, e) -> {
System.out.println(hello +v);
if (e == null) {
System.out.println(没有异常,更新完成);
}
}).exceptionally(s -> {
s.printStackTrace();
System.out.println(异常了,主线程先忙其他事情);
return null;
});
System.out.println(主线程工作);
}
对计算结果进行合并
public static void main(String[] args) throws Exception {
CompletableFuture<Integer> task1 = CompletableFuture.supplyAsync(() -> {
return 1;
});
CompletableFuture<Integer> task2 = CompletableFuture.supplyAsync(() -> {
return 10;
});
CompletableFuture<Integer> integerCompletableFuture = task1.thenCombine(task2, (x, y) -> {
return x + y;
});
System.out.println(integerCompletableFuture.get());
}