目录
0.学习目标
1、整理多线程题目
(1)wait和sleep区别
(2)线程的状态有哪些
(3)在同步代码块或者同步方法中,什么情况下会释放锁
2、编写多线程代码
/**
* 创建两个线程
* 创建一个初始 变量 值 包子状态为false
* 实现一个线程吃包子,另外一个线程做包子
* 交替,进行10次
*/
3、编写泛型方法,实现两个数相加,限定参数只能是数字
1. 笔记
1、线程间通信 (1)创建线程因为操作系统进行调度的,创建顺序不确定的,在程序通过条件控制,让线程执行有 顺序的,这个个过程--等待唤醒(通知)机制 (2)等待唤醒(通知)机制调用方法 第一个 wait() : 释放锁,需要被唤醒 第二个 notify()/notifyAll():通知其他线程 * 上面三个方法都是Object类里面的方法 (3)编写线程间通信例子 /** * 创建两个线程 * 创建一个初始 变量 值是0 * 实现一个线程对变量+1 ,另外一个线程对变量-1 * 交替,进行10次 */ 2、线程其他概念 (1)线程生命周期 - 线程的生命周期有五种状态:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)、死亡(Dead)。 (2)线程阻塞: - 线程调用了sleep()方法,主动放弃所占用的CPU资源; - 线程试图获取一个同步监视器,但该同步监视器正被其他线程持有; - 线程执行过程中,同步监视器调用了wait(),让它等待某个通知(notify); - 线程执行过程中,同步监视器调用了wait(time) - 线程执行过程中,遇到了其他线程对象的加塞(join); public enum State { NEW, 新建 RUNNABLE, 准备就绪 BLOCKED, 阻塞 WAITING, 不见不散 TIMED_WAITING, 过时不候 TERMINATED; 终结 } (3)Thread和Runnable区别 1. 适合多个相同的程序代码的线程去共享同一个资源。 2. 可以避免java中的单继承的局限性。 3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。 4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。 (4)释放锁操作 * 当前线程的同步方法、同步代码块执行结束。 * 当前线程在同步代码块、同步方法中遇到break、return终止该代码块、该方法的继续执行。 * 当前线程在同步代码块、同步方法中出现未处理的Error或Exception,导致当前线程异常结束。 * 当前线程在同步代码块、同步方法中执行了锁对象的wait()方法,当前线程被挂起,并释放锁。 (5)死锁 不同的线程分别锁住对方需要的同步监视器对象不释放,都在等待对方先放弃时就形成了线程的死锁。 一旦出现死锁,整个程序既不会发生异常,也不会给出任何提示,只是所有线程处于阻塞状态,无法继续。 (6)sleep()和wait()方法的区别 - sleep()不释放锁,wait()释放锁 - sleep()自动在指定时间唤醒,wait()可以指定时间可以使用notify或notifyAll唤醒 - sleep()在Thread类中声明的静态方法,wait方法在Object类中声明
2. 练习
练习一:比较接口创建线程(分开)和匿名内部类创建线程区别(合并)
接口创建线程
public class ThreadDemo2 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread Thread = new Thread(myRunnable,"接口创建线程");
Thread.start();
}
}
//实现Runnable接口创建线程
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
匿名内部类创建线程
public class ThreadDemo3 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}, "匿名内部类创建线程").start();
}
}
练习二:
/** * 创建两个线程 * 创建一个初始 变量 值是0 * 实现一个线程对变量+1 ,另外一个线程对变量-1 * 交替,进行10次 */
public class ThreadDemo1 {
public static void main(String[] args) {
OneAddDiv oneAddDiv = new OneAddDiv();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <=10; i++) {
//+1
try {
oneAddDiv.addOne();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"AA").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <=10; i++) {
//+1
try {
oneAddDiv.divOne();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"BB").start();
}
}
//第一步 创建资源类,定义属性和操作方法
class OneAddDiv {
//定义变量 0
private int number = 0;
//定义操作方法
// +1方法
public synchronized void addOne() throws InterruptedException {
//如果number不是0,等待
if(number !=0) { // 不等于0 就是1
//等待
this.wait();
}
//如果number是0,进行+1操作
number++;
System.out.println(Thread.currentThread().getName()+" "+number);
//通知其他线程
this.notifyAll();
}
//-1方法
public synchronized void divOne() throws InterruptedException {
if(number != 1) {
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+" "+number);
this.notifyAll();
}
}
练习三:一对一做包子吃包子问题
解释:包子铺线程生产包子,吃货线程消费包子。当包子没有时(包子状态为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();
}
}
执行效果