一个程序完成某个任务,需要多个线程协调,就需要线程之间存在“通信”,比如生产者和消费者,只有生产了才能被消费。当生产者生产完成才能告知消费者可以消费,那么告知的过程就是线程间的通信。
1)."等待与唤醒机制”就是“线程间通信”的一种体现。
2).工作形式:
1).一个线程做一些“准备性工作”。
2).另一个线程做正常的工作。由于两个线程是“无序的”,很有可能“第二个线程工作时,第一个线程的准备工作还没
有做好,这时就需要第二个线程要主动“等待”,然后第一个线程进入开始做准备,准备工作做好后,再唤醒第二个
线程开始正常工作。
唤醒机制_生产者与消费者
线程通信_包子铺 -----生产者生产包子,放在包子铺,消费者到包子铺取包子,只有生产者生产完包子通知消费者,消费者才
可以到包子铺获取包子,否则只能无限等待
包子铺类:
import java.util.ArrayList;
import java.util.List;
public class BaoZiPu{
List list = new ArrayList<>();
Object obj = new Object();
public void setBaozi(){
synchronized (obj){
list.add("包子");
obj.notifyAll();
}
}
public String getBao() throws InterruptedException {
synchronized (obj){
if(list.size() == 0){
System.out.println("访问的线程需要等待....");
obj.wait();
System.out.println("访问的线程被唤醒......");
}
String s = list.get(0);
list.remove(s);
return s;
}
}
}
获取包子
public class GetBaozi extends Thread{
BaoZiPu baoZiPu;
public GetBaozi(BaoZiPu baoZiPu) {
this.baoZiPu = baoZiPu;
}
@Override
public void run() {
while (true){
try {
System.out.println(baoZiPu.getBao());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
生产包子
public class SetBaozi extends Thread {
BaoZiPu baoZiPu;
public SetBaozi(BaoZiPu baoZiPu) {
this.baoZiPu = baoZiPu;
}
@Override
public void run() {
while (true) {
baoZiPu.setBaozi();
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试类:
public class Demo {
public static void main(String[] args) {
BaoZiPu baoZiPu = new BaoZiPu();
GetBaozi getBaozi = new GetBaozi(baoZiPu);
SetBaozi setBaozi = new SetBaozi(baoZiPu);
getBaozi.start();
setBaozi.start();
}
}
存储了若干多的“线程对象”的一个“容器”。 线程池类:ExecutorService这个线程池就可以缓存大量的线程对象,并可以反复的重用它们。
1).对于一个线程对象“只能启动一次”,若想第二次再用,就需要再次创建一个线程对象,但如果创建线程对象很耗时,这
样如果程序中要反复的使用同一个线程,整个程序的效率就会很低。
2).线程池的思想:在程序启动时,会先创建若干多的线程对象,并存储到一个容器中,作为“线程池”。如果有需要时,取
出一个线程对象,并执行它。执行完毕,线程池会回收这个线程对象,如果再次需要,可以再次取出,并执行它。所以,
线程池中的线程对象是“可以反复重用”的。这样就避免的反复的创建线程对象,从而提高程序的执行效率。
Java里面线程池的顶级接口是 java.util.concurrent.Executor ,但是严格意义上讲Executor 并不是一个线程池,而只是一
个执行线程的工具。真正的线程池接口是java.util.concurrent.ExecutorService 。
Executors类中有个创建线程池的方法如下:
a).public static ExecutorService newFixedThreadPool(int nThreads) :返回线程池对象。(创建的是有界线程池,也就是池
中的线程个数可以指定最大数量)
b).public Future> submit(Runnable task) :获取线程池中的某一个线程对象,并执行
如果创建一个线程需要五秒钟,不用线程池,每创建一个线程就需要五秒,用了线程池,因为线程池执行后,会将此线程
对象"缓存",可以重用,那么再次创建就不需要时间。
示例:
public class MyThread extends Thread {
public MyThread() {
for (int i = 0; i < 5; i++) {
System.out.println("等待中..." + (i + 1) + "秒");
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void run() {
System.out.println("线程运行啦");
}
}
测试类:
public class Demo {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
myThread = new MyThread();
myThread.start();
}
}
方式一是创建Runnable的实现类的对象,通过Thread的构造函数创建线程。
方式二是用Runnable的匿名内部类创建线程。
public class Demo {
public static void main(String[] args) {
//1.方式一:制作Runnable子类的方式
MyRunnable myRunnable = new MyRunnable();
Thread t = new Thread(myRunnable);
t.start();
//2.方式二:匿名内部类的方式
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("i = " + i);
}
}
});
t2.start();
}
}
1).编程思想转换:将“以什么形式做”,转换为“怎样做”。
2).“函数式”编程思想:当调用的方法需要一个“接口”类型时,就可以考虑直接传入一个代替的“函数(方法)”即可,其它无用的语句可以省略。
1).使用前提:具备以下条件,才可以使用Lambda。
1).首先需要的是一个“接口类型”;
2).而且这个接口中有,且只有一个“抽象方法”--函数式接口;
2).标准格式(三部分)
第一部分:一对小括号--形参;
第二部分:一个右箭头:->
第三部分:一对大括号--方法体;
3).标准格式的的写法
无参 ()->{//方法体}
有参 (int a,int b)->{//方法体}
方法体内按照正常代码格式
1).示例:
//Lambda表达式--完整格式
fun((int x, int y) -> {return x + y;}, 10, 20);
//简写的Lambda
fun((x, y) -> x + y, 10, 20);
2).省略规则:
1).形参:Lambda中的"形参类型”都可以省略;
fun((x) -> {return x * x;});
2).形参:如果只有一个形参,形参类型和小括号都可以省略
(要省略小括号,必须省略数据类型)
fun(x -> {return x * x;});
3).方法体:如果方法体中只有一条语句,可以应用省略规则;
A).有返回值:可以省略大括号、return关键字、语句后的分号
(要省全省,要用全用)
fun(x -> x * x);
B).无返回值:可以省略大括号、语句后的分号;
(要省全省,要用全用)
fun(x -> System.out.println("x = " + x));