二 进程和线程以及线程池

山不过来,我就过去
生产者-消费者模型
在前面我们讲了很多关于同步的问题,然而在现实中,需要线程之间的协作 。比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权 。因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去 。因此,一般情况下,当队列满时,会让生产者交出对临界资源的占用权,并进入挂起状态 。然后等待消费者消费了商品,然后消费者通知生产者队列有空间了 。同样地,当队列空时,消费者也必须等待,等待生产者通知它队列中有商品了 。这种互相通信的过程就是线程间的协作 。
总的来说,就是如下图的这样,不断循环 。
以学生类为资源举例
首先,要对线程等待,唤醒有基本的思想
不同种类线程之间的通信(等待唤醒机制)
出现线程安全问题了:
1.是不是多线程环境
2.是不是由共享资源
3.是否有多条语句在操作共享资源
线程的等待唤醒 类
void wait ()
在其他线程调用此对象的() 方法或() 方法前,导致当前线程等待 。
void()
唤醒在此对象监视器上等待的单个线程 。
void()
唤醒在此对象监视器上等待的所有线程 。

二  进程和线程以及线程池

文章插图
生产线程:如果没有资源资源我就生产,有了资源我就等待,通知消费线程来消费
消费线程:有了资源我就消费,没有资源我就等着,你通知生产线程生产
1)定义一个学生类,定义一个标记,代表有无资源 。
public class Student {public String name;public int age;//定义一个标记boolean flag;//表示资源,false就没有,ture就有}
【二进程和线程以及线程池】2定义一个生产资源类
public class SetThread extends Thread {Student student;int i=0;public SetThread(Student student) {this.student=student;}@Overridepublic void run() {while (true){synchronized (student){if(student.flag){try {student.wait();} catch (InterruptedException e) {e.printStackTrace();}}if(i%2==0){student.name="老大";student.age=15;}else {student.name="老二";student.age=12;}student.flag=true;//通知student.notifyAll();//唤醒以后,还要再次争抢时间片i++;}}}}
3)定义一个消费类
public class GetThread extends Thread {//获得资源Student student;public GetThread(Student student) {this.student=student;}@Overridepublic void run() {while (true){synchronized (student){if(!student.flag){//没有资源try {student.wait();//等着:wait()方法 线程一旦等待 就要立马释放锁,等会被唤醒了,也就从这里醒来} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(student.name + "===" + student.age);//修改标记student.flag = false;student.notify();//通知}}}}
4)接下来,开启线程
public class Test {public static void main(String[] args) {Student student = new Student();SetThread th1 = new SetThread(student);GetThread th2 = new GetThread(student);th1.start();th2.start();}}
结果:
总的就是,生产-消费的关系一直循环(等待唤醒机制)
线程池的概述与使用 1)线程池概述
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互 。
而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池 。
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用 。
在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
2)内置线程池的概述
JDK5新增了一个工厂类来产生线程池,有如下几个方法
public static ExecutorService newCachedThreadPool():根据任务的数量来创建线程对应的线程个数public static ExecutorService newFixedThreadPool(int nThreads):固定初始化几个线程public static ExecutorService newSingleThreadExecutor():初始化一个线程的线程池
这些方法的返回值是对象,该对象表示一个线程池,可以执行对象或者对象代表的线程 。它提供了如下方法
( task)
( task)
使用步骤:
创建线程池对象
创建实例
提交实例
关闭线程
3)匿名内部类的方式实现多线程
a.继承Tread类
b.继承类
public class Thread {public static void main(String[] args) {//匿名实现多线程//继承thread类new Thread(){public void run(){for(int x=0;x<111;x++){System.out.println(getName()+":"+x);}}}.start();//实现runablenew Thread(new Runnable() {@Overridepublic void run() {for(int x=0;x<100;x++){System.out.println("wwww");}}}).start();}}
4)工厂类来产生线程池
首先,定义一个类来继承,需要重新其中的call()方法 。
public class MyCallable implements Callable {int len;public MyCallable(int i) {len=i;}@Overridepublic Integer call() throws Exception{//此方法需要返回值,int sum=0;for (int i = 0; i <= len; i++) {sum=sum+i;}return sum;}}
接下来开启线程池 。
public class Test {public static void main(String[] args) throws ExecutionException, InterruptedException {//Callable 这个任务接口,执行完之后会有返回值 跟线程池配合使用// Runnable 这个任务接口 执行完之后没有返回值//获取一个有指定数量线程对象的线程池ExecutorService service = Executors.newFixedThreadPool(3);//定义线程个数//submit提交要执行的值返回任务,并返回表示挂起的任务结果的 Future 。Future sb1 = service.submit(new MyCallable(10));//第一个线程Future sb2 = service.submit(new MyCallable(100));//Future sb3 = service.submit(new MyCallable(1000));System.out.println(sb1.get());System.out.println(sb2.get());System.out.println(sb3.get());service.shutdown();//关闭线程池}}
记得最后关闭线程池 。