自学Java——day_17 线程池、Lambda表达式

1.线程池

1.1 线程间通信

        多个线程并发执行时, 在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务,并且我们希望他们有规律的执行, 那么多线程之间需要一些协调通信,以此来帮我们达到多线程共同操作一份数据。

1.2 等待唤醒机制

在一个线程进行了规定操作后,就进入等待状态(wait()), 等待其他线程执行完他们的指定代码过后再将其唤醒(notify());在有多个线程进行等待时, 如果需要,可以使用 notifyAll()来唤醒所有的等待线程。

等待唤醒机制就是用于解决线程间通信的问题的,使用到的3个方法的含义如下:

  1. wait:线程不再活动,不再参与调度,进入 wait set 中,因此不会浪费 CPU 资源,也不会去竞争锁了,这时 的线程状态即是 WAITING。它还要等着别的线程执行一个特别的动作,也即是“通知(notify)”在这个对象 上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue)中

  2. notify:则选取所通知对象的 wait set 中的一个线程释放;例如,餐馆有空位置后,等候就餐最久的顾客最先入座。

  3. notifyAll:则释放所通知对象的 wait set 上的全部线程。

     等待唤醒机制其实就是经典的“生产者与消费者”的问题。
    

例:包子铺线程生产包子,吃货线程消费包子。当包子没有时(包子状态为false),吃货线程等待,包子铺线程生产包子 (即包子状态为true),并通知吃货线程(解除吃货的等待状态),因为已经有包子了,那么包子铺线程进入等待状态。 接下来,吃货线程能否进一步执行则取决于锁的获取情况。如果吃货获取到锁,那么就执行吃包子动作,包子吃完(包 子状态为false),并通知包子铺线程(解除包子铺的等待状态),吃货线程进入等待。包子铺线程能否进一步执行则取 决于锁的获取情况。

public class BaoZi {      
	String  pier ;      
	String  xianer ;      
	boolean  flag = false ;//包子资源 是否存在  包子资源状态 
}
public class ChiHuo extends Thread{     
	private BaoZi bz;       
	public ChiHuo(String name,BaoZi bz){         
		super(name);         
		this.bz = bz;     
	}     
	@Override     
	public void run() {         
		while(true){             
			synchronized (bz){                 
				if(bz.flag == false){//没包子                     
					try {                         
						bz.wait();                     
					} catch (InterruptedException e) {                         
						e.printStackTrace();                     
					}                 
				}                 
				System.out.println("吃货正在吃"+bz.pier+bz.xianer+"包子");                 
				bz.flag = false;                 
				bz.notify();             
			}         
		}     
	} 
}
public class BaoZiPu extends Thread {       
	private BaoZi bz;       
	public BaoZiPu(String name,BaoZi bz){         
		super(name);         
		this.bz = bz;     
	}       
	@Override     
	public void run() {         
		int count = 0;         
		//造包子         
		while(true){             
			//同步             
			synchronized (bz){                 
				if(bz.flag == true){//包子资源存在                     
					try {                           
						bz.wait();                       
					} catch (InterruptedException e) {                         
						e.printStackTrace();                     
					}                 
				}                   
				// 没有包子  造包子                 
				System.out.println("包子铺开始做包子");                 
				if(count%2 == 0){                     
					// 冰皮  五仁                     
					bz.pier = "冰皮";                     
					bz.xianer = "五仁";                 
				}else{                     
					// 薄皮  牛肉大葱                     
					bz.pier = "薄皮";                     
					bz.xianer = "牛肉大葱";                 
				}                 
				count++;                   
				bz.flag=true;                 
				System.out.println("包子造好了:"+bz.pier+bz.xianer);                 
				System.out.println("吃货来吃吧");                 
				//唤醒等待线程 (吃货)                 
				bz.notify();             
			}         
		}     
	} 
}
public class Demo {     
	public static void main(String[] args) {         
		//等待唤醒案例         
		BaoZi bz = new BaoZi();           
		ChiHuo ch = new ChiHuo("吃货",bz);         
		BaoZiPu bzp = new BaoZiPu("包子铺",bz);           
		ch.start();         
		bzp.start();     
	} 
}

1.3 线程池概述

我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间。
那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?
在Java中可以通过线程池来达到这样的效果。

	线程池:其实就是一个容纳多个线程的容器,其中的线程可以反复使用,
	省去了频繁创建线程对象的操作, 无需反复创建线程而消耗过多资源。

合理利用线程池能够带来三个好处:

  1. 降低资源消耗。
  2. 提高响应速度。
  3. 提高线程的可管理性。

1.4 线程池的使用

Java里面线程池的顶级接口是 java.util.concurrent.Executor ,但是严格意义上讲Executor 并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是 java.util.concurrent.ExecutorService 。
要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优 的,因此在 java.util.concurrent.Executors 线程工厂类里面提供了一些静态工厂,生成一些常用的线程池。官 方建议使用Executors工程类来创建线程池对象。

Executors类中有个创建线程池的方法如下:

  • public static ExecutorService newFixedThreadPool(int nThreads) :返回线程池对象。(创建的是有界线程池,也就是池中的线程个数可以指定最大数量)
  • public Future submit(Runnable task) :获取线程池中的某一个线程对象,并执行Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用。
public class MyRunnable implements Runnable {     
	@Override     
	public void run() {         
		System.out.println("我要一个教练");         
		try {             
			Thread.sleep(2000);         
		} catch (InterruptedException e) {             
			e.printStackTrace();         
		}         
		System.out.println("教练来了: " + 
		Thread.currentThread().getName());         
		System.out.println("教我游泳,交完后,教练回到了游泳池");     
	} 
}
public class ThreadPoolDemo {     
	public static void main(String[] args) {         
		// 创建线程池对象         
		ExecutorService service = 
		Executors.newFixedThreadPool(2);//包含2个线程对象         
		// 创建Runnable实例对象         
		MyRunnable r = new MyRunnable();           
		//自己创建线程对象的方式         
		// Thread t = new Thread(r);         
		// t.start(); ‐‐‐> 调用MyRunnable中的run()           
		// 从线程池中获取线程对象,然后调用MyRunnable中的run()         
		service.submit(r);         
		// 再获取个线程对象,调用MyRunnable中的run()         
		service.submit(r);         
		service.submit(r);         
		// 注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。         
		// 将使用完的线程又归还到了线程池中         
		// 关闭线程池         
		//service.shutdown();     
	} 
}

2.Lamdba表达式

  • 面向对象的思想: 做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情。
  • 函数式编程思想: 只要能获取到结果,谁去做的,怎么做的都不重要,重视的是做什么,不重视怎么做。

2.1 Lambda语法格式

Lambda省去面向对象的条条框框,格式由3个部分组成:

  • 一些参数
  • 一个箭头
  • 一段代码

Lambda表达式的标准格式为:

	 (参数类型 参数名称) ‐> { 代码语句 }

格式说明:

  • 小括号内的语法与传统方法参数列表一致:无参数则留空;多个参数则用逗号分隔。
  • -> 是新引入的语法格式,代表指向动作。
  • 大括号内的语法与传统方法体要求基本一致。

2.2 Lambda表达式的使用前提

Lambda的语法非常简洁,完全没有面向对象复杂的束缚。但是使用时有几个问题需要特别注意:

  1. 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。 无论是JDK内置的 Runnable 、 Comparator 接口还是自定义的接口,只有当接口中的抽象方法存在且唯一 时,才可以使用Lambda。
  2. 使用Lambda必须具有上下文推断。 也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

补充:有且仅有一个抽象方法的接口,称为“函数式接口”。

2.3 使用Lambda

从一定程度上来说,简洁的lambda的使用取代了复杂的匿名内部类。

public class Person {      
	private String name;     
	private int age;          
	// 省略构造器、toString方法与Getter Setter  
}
import java.util.Arrays;   public class Demo07ComparatorLambda {     
	public static void main(String[] args) {         
		Person[] array = {            
			new Person("古力娜扎", 19),               
			new Person("迪丽热巴", 18),               
			new Person("马尔扎哈", 20) 
		};              
		Arrays.sort(array, (Person a, Person b)> {            
			return a.getAge() ‐ b.getAge();            
		});           
		for (Person person : array) {             
			System.out.println(person);         
		}     
	}
}

你可能感兴趣的:(java基础知识)