一、多线程的关键点回顾
1、什么是进程
程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。而进程是程序在处理机上的- -次
执行过程,它是一个动态的概念。
进程是一个具有一定独立功能的程序,一个实体,每一个进程都有它自己的地址空间。
2、进程的状态:
进程执行时的间断性,决定了进程可能具有多种状态。事实上,运行中的进程具有以下三种基本状态。
1)就绪状态(Ready)
2)运行状态( Running)
3)阻塞状态( Blocked)
线程实现的两种方式
在Java中如果要想实现多线程的操作,有两种实现方法:
//(1) - -种是继承Thread类
class MyThread extends Thread{
public void run(){
//逻辑处理
}
}
MyThread mt = new MyThread();
mt.start();
//(2)另外一种是实现Runnable接口
class MyRunnable implements Runnable{
public void run(){
//逻辑处理
}
}
MyRunnable mr = new MyRunnable();
Thread t = new Thread(mr);
t.start();
其中第二种方法是最常用的
线程休眠;sleep
使用sleep方法实现,使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),释放CPU的时间片,具体取
决于系统定时器和调度程序的精度和准确性。线程不会丢失任何显示器的所有权。
进程与线程的区别;
进程是一个独立的应用程序,具有独立的内存空间。
线程是一个进程中的执行路径,在同一个进程中,多个线程共享-个内存空间。
(PS:进程是一个概念,一个应用程序由一个或多个线程来组成,从概念上就把一个应用程序当做是系统中的一个进程)
线程等待;wait
使用wait方法实现,让线程暂时停止执行,释放CPU的时间片,并释放对象监视器的所有权,等待其它线程通过
notify方法来唤醒。
多线程图解;
New(初始化状态)
Runnable(可运行/运行状态)
Blocked(阻塞状态)
Dead(终止状态)
Waiting(无时间限制的等待状态)
Timed_ Waiting(有 时间限制的等待状态)
二、生产者与消费者案例分析
饭店里的有一个厨师和一个服务员,这个服务员必须等待厨师准备好膳食。.
当厨师准备好时,他会通知服务员,之后服务员上菜,然后返回继续等待。
这是一个任务协作的示例,厨师代表生产者,而服务员代表消费者。
厨 师=生产者;
服务员=消费者。
Demo;
public class ProducterConsumerDemo {
public static void main(String[] args) {
Food food = new Food();
Producter p = new Producter(food);
Consumer c = new Consumer(food);
Thread pt = new Thread(p);
Thread ct = new Thread(c);
pt.start();
ct.start();
}
}
/**
* 生产者
*/
class Producter implements Runnable{
private Food food;
public Producter(Food food){
this.food = food;
}
@Override
public void run() {
//模拟生产者不断的生产食物
//一共生产30份菜,交替生产两种菜
for (int i=0;i<30;i++){
if(i%2==0){
food.set("大盘鸡","分量大,吃得饱");
}else{
food.set("韭菜炒鸡蛋","大补,鑫鑫最爱");
}
}
}
}
/**
* 消费者
*/
class Consumer implements Runnable{
private Food food;
public Consumer(Food food){
this.food = food;
}
@Override
public void run() {
//消费30份食物
for (int i=0;i<30;i++){
food.get();
}
}
}
//食物类
class Food{
private String name; //食物名
private String desc; //食物描述
private boolean flag = true; //true 表示可以生产,不能消费,false 表示可以消费,不能生产
//Object obj = new Object();// 只是作为锁来用
/**
* 存值(生产产品)
* @param name
* @param desc
*/
public void set(String name,String desc){
synchronized (this) { //在对象上上锁
if(flag==false){ //表示不能生产,所以就让出 CPU,并释放对象锁
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//表示正在生产
this.name = name;
try {
//模拟生产食物的时间过程
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.desc = desc;
flag = false;
this.notify(); //唤醒在此对象锁上的任意一个线程
}
}
/**
* 取值(消费产品)
*/
public void get(){
synchronized (this) {
if(flag==true){ //表示不能消费
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//正在消费
System.out.println(this.name + "--" + this.desc);
flag = true;
this.notify(); //唤醒在此对象锁上的任意一个线程
}
}
public Food() {
}
public Food(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
PS:
当多个线程共享同一个数据,会出现线程安全问题
JAVA 的多线程是使用抢占式来实现
对象锁:JAVA 在设计时把锁的概念定义在对象身上,而不是线程
生产者与消费者是 互斥关系
对象设置锁,就是线程开发中的同步
在此示例中,我们需要达到的效果是:两个线程在各自调用同一个 Food对象的不同方法时,
要限制两个方法以互斥的方式进行,比如,A 线程调用了set方法,那么 B 线程必须等待 A 线程完成set方法的调用后
才能执行,相反是一样的。
应用场景:一个是负责提供数据的线程,把数据拿到后放到队列中,另一个线程不断的从队列中取出数据进行操作
遗留问题:
1、生产者消费者模型的作用是什么
2、sleep方 法和wait方法有什么区别
3、如何在两个线程之间共享数据
4、为什么wait()方法和notify()/notifyAl()方法要在同步块中被调用
5、wait()方法和notify()/notifAIl()方法在放弃对象监视器时有什么区别.
6、Thread.sleep(0)的作用是什么