7月22线程
1.线程简介
多线程
三高: 高可用 高性能 高并发
thread 类
线程是程序中执行的线程。 Java虚拟机允许应用程序同时运行多个执行线程。
多线程:
多任务执行,多条路径可以执行
多线程的优点:
提高效率
进程 与 线程 之间的区别:
进程: 系统中的应用程序,一个进程之间包含1~n个线程,进程具有自己的资源,内存空间,进程是资源分配的最小单位
线程: 一个程序中的顺序流,多个线程共享一个进程的资源和数据空间,每一个线程具有自己的程序计数器,线程是cpu调度的最小单位
学习的目标:
1.线程的创建于开启 *****
2.线程的状态 : 新生 就绪 运行 阻塞 终止
3.线程安全 *****
4.线程通信 wait() notify()
线程的创建:
1.继承Thread,重写run()方法,定义线程体 + start()
2.实现Runnable接口,重写run()方法 + start() --> 推荐
3.juc包下Callable接口,重写call()方法
public class Class001_Thread extends Thread{
public static void main(String[] args) {
//创建线程
Class001_Thread th = new Class001_Thread();
//开启线程 start() 可以被cpu调度了
th.start();
for(int i=1;i<=20;i++){
System.out.println("一边讲课....");
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//思考:
//1) 不使用start()开启线程,直接调用run()方法
//2)start开启位置是否可以修改?
}
/*
run() 定义线程体
*/
@Override
public void run() {
for(int i=1;i<=20;i++){
System.out.println("一边喝水....");
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
实现Runnable接口,重写run方法 + start()
1.类是单继承,接口多实现
2.可以实现资源共享
public class Class002_Thread implements Runnable{
public static void main(String[] args) {
//创建线程
Thread th = new Thread(new Class002_Thread());
//开启线程
th.start();
//new Class002_Thread().run(); //方法的调用,不是线程的开启
for(int i=1;i<=20;i++){
System.out.println("一边陪女朋友");
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*
定义线程体
*/
@Override
public void run() {
for(int i=1;i<=20;i++){
System.out.println("一边打游戏");
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
一边陪女朋友
一边打游戏
一边打游戏
一边陪女朋友
一边陪女朋友
一边打游戏
/*
模拟12306购票
100张票 3个人买完
共享的资源: 100张张票
多线程: 4个线程 1个主线程 3个线程买票
注意: 多线程同时操作同一份资源就有可能出现线程不安全问题
*/
public class Class003_Web12306 implements Runnable{
int tickets = 100; //100张票
/*
每个线程购票的流程
*/
@Override
public void run() {
//重复买票
while(true){
//没有票停止购买
//A B C
if(tickets<=0){
break;
}
System.out.println(Thread.currentThread().getName()+"正在购买第"+ tickets-- +"张票");
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Class003_Web12306 web = new Class003_Web12306();
//创建线程
Thread th1 = new Thread(web,"石潘超");
Thread th2 = new Thread(web,"郑达");
Thread th3 = new Thread(web,"刘本军");
th1.start();
th2.start();
th3.start();
}
}
刘本军正在购买第98张票
石潘超正在购买第100张票
郑达正在购买第99张票
石潘超正在购买第97张票
刘本军正在购买第96张票
郑达正在购买第95张票
。。。
郑达正在购买第4张票
石潘超正在购买第3张票
郑达正在购买第1张票
刘本军正在购买第0张票
/*
网络资源下载
*/
public class Class004_TestWebDownloader{
public static void main(String[] args) {
Downloader th1 = new Downloader("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","src/baidu.png");
Downloader th2 = new Downloader("https://img13.360buyimg.com/pop/s590x470_jfs/t1/191473/22/13064/97981/60efcd7fEde609b1a/8640695d031c2e4f.jpg","src/jd1.jpg");
Downloader th3 = new Downloader("https://imgcps.jd.com/ling4/3548676/6Ziy6Zuo6Ziy5rGb5b-F5aSH/54iG5qy-5L2O6IezOS455YWD/p-5bd8253082acdd181d02fa42/4f1be342/cr/s/q.jpg","src/jd2.jpg");
th1.start();
th2.start();
th3.start();
}
}
/*
网络资源下载的JAVABEAN
*/
class Downloader extends Thread{
private String url;
private String pathName;
public Downloader(String url, String pathName) {
this.url = url;
this.pathName = pathName;
}
@Override
public void run() {
//下载的过程
WebDownloader.down(url,pathName);
}
}
//网络资源下载工具类
class WebDownloader{
//网络资源下载步骤
public static void down(String url,String pathName){
try {
FileUtils.copyURLToFile(new URL(url),new File(pathName));
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
* 模拟龟兔赛跑
* 兔子每跑十步休息一下 10毫秒
* 乌龟正常跑
* 只要有参赛者跑了100步就结束
* 使用多线程模拟比赛流程
*
* 注意:
* run方法上不能抛出异常,也没有返回值
* 通过添加一个标识判断控制线程的结束
*/
public class Class005_Racer implements Runnable{
//赢的参赛者名字
private String winner = null;
public static void main(String[] args) {
//一场比赛
Class005_Racer racer = new Class005_Racer();
//参赛者
Thread th1 = new Thread(racer,"兔子");
Thread th2 = new Thread(racer,"乌龟");
//开始比赛
th1.start();
th2.start();
}
@Override
public void run() {
for(int steps=1;steps<=100;steps++){
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在第"+steps+"步...");
//判断是否为兔子每跑十步休息一下 100毫秒
if("兔子".equals(Thread.currentThread().getName()) && steps%10==0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//游戏是否结束,是否有参赛者赢了
if(checkOver(steps)){
break;
}
}
}
/**
* 检查游戏是否结束
* @param steps 当前参赛者的步数
* @return 是否结束
* true-->结束
* false -->不结束
*/
private boolean checkOver(int steps) {
if(winner!=null){
return true;
}
if(steps==100){
winner = Thread.currentThread().getName();
return true;
}
return false;
}
}
/*
重写方法抛出异常类型要求:
重写方法上抛出异常类型<=被重写方法上异常的抛出类型
*/
interface Fu{
void test() throws FileNotFoundException;
}
class Impl implements Fu{
@Override
public void test() throws FileNotFoundException{
}
}
。。。
乌龟正在第98步…
兔子正在第69步…
乌龟正在第99步…
兔子正在第70步…
乌龟正在第100步…
2.其他表示线程方法
/*
* 实现Callable,重写call(),在方法内部定义线程体 --> 了解
* 1.call可以抛出异常
* 2.可以定义返回值
*/
public class Class006_Racer implements Callable<Integer> {
//赢的参赛者名字
private String winner = null;
public static void main(String[] args) throws ExecutionException, InterruptedException {
//一场比赛
Class006_Racer racer = new Class006_Racer();
//1.创建执行服务
ExecutorService server = Executors.newFixedThreadPool(2);
//2.提交任务
Future<Integer> future1 = server.submit(racer);
Future<Integer> future2 = server.submit(racer);
//3.获取结果
System.out.println(future1.get());
System.out.println(future2.get());
//3.终止服务
server.shutdown();
}
@Override
public Integer call() {
for(int steps=1;steps<=100;steps++){
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在第"+steps+"步...");
//判断是否为兔子每跑十步休息一下 100毫秒
if("pool-1-thread-1".equals(Thread.currentThread().getName()) && steps%10==0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//游戏是否结束,是否有参赛者赢了
if(checkOver(steps)){
return steps;
}
}
return null;
}
/**
* 检查游戏是否结束
* @param steps 当前参赛者的步数
* @return 是否结束
* true-->结束
* false -->不结束
*/
private boolean checkOver(int steps) {
if(winner!=null){
return true;
}
if(steps==100){
winner = Thread.currentThread().getName();
return true;
}
return false;
}
/*
* 其他定义线程体的方式
*/
public class Class007_Thread {
//内部类
static class Inner implements Runnable{
@Override
public void run() {
for(int i=1;i<=20;i++){
System.out.println("一边吸烟...");
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
new Thread(new Inner()).start();
//匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
for(int i=1;i<=20;i++){
System.out.println("一边烫头...");
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
//lambda表达式
new Thread(() -> {
for(int i=1;i<=20;i++){
System.out.println("一边洗澡...");
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
for(int i=1;i<=20;i++){
System.out.println("一边喝酒...");
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
一边吸烟…
一边烫头…
一边喝酒…
一边洗澡…
3.线程的状态
3.2、状态
线程的状态:
新生状态 : new Thread(),当前线程处于新生状态
就绪状态 : start(),这个线程进入就绪状态,线程准备好可以被cpu调度,会进入到就绪队列,等待cpu的调度
运行状态 : 当cpu调度到这个线程,线程进入到运行状态开始执行
阻塞状态 : 当程序无法正常执行(进入阻塞状态的多种方式...)
终止状态 : 线程结束
1.一个线程一旦终止,无法恢复
2.一个线程如果进入阻塞状态,阻塞接触会直接恢复到就绪状态
进入到阻塞状态的几种方式 :
1. sleep()
2. join()
3. wait()
4. IO
进入线程就绪状态的方式:
1. start()
2. yield()
3. 线程切换
4. 阻塞接触
进入终止状态的方式:
1.stop 已经过时,不推荐使用 2.正常之间完毕 3.添加标识判断 --> 推荐
sleep(ms) : 1s = 1000ms
线程休眠|睡眠
会进入到阻塞状态,当指定的ms数结束,线程会恢复到就绪状态
抱着资源睡觉
让出cpu的资源
抱着是指对象的资源|锁
作用 :
1) 模拟网络延迟
2) 方法问题的可能性
3.2 yield
yield 礼让线程 高风亮节
当一个线程调用yield,让出cpu的资源,进入到就绪状态
静态方法
public class Class002_Yield implements Runnable{
public static void main(String[] args) {
new Thread(new Class002_Yield(),"A").start();
new Thread(new Class002_Yield(),"B").start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始了");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"结束了");
}
}
A开始了
B开始了
A结束了
B结束了
3.3 join
join() 插队线程
join(ms) 插队指定的ms数
成员方法join,线程对象.join() 当前线程对象插队
注意: 先就绪,后插队
public class Class003_Join {
public static void main(String[] args) {
new Thread(new Father()).start();
}
}
//父亲线程
class Father implements Runnable{
@Override
public void run() {
System.out.println("想抽烟了...");
System.out.println("给儿子钱,让他去买烟....");
//创建儿子线程
Thread th = new Thread(new Son());
//开启线程
th.start();
//插队
try {
th.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("接过烟,吸一口...");
System.out.println("把零花钱给儿子....");
}
}
class Son implements Runnable{
@Override
public void run() {
System.out.println("接过钱,去买烟");
System.out.println("路上遇到一家游戏厅,进去玩10s钟...");
for(int i = 10;i>=0;i--){
System.out.println(i+"s过去了");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("赶紧去买烟...");
System.out.println("把烟递给老爸,然后把钱给他...");
}
}
想抽烟了…
给儿子钱,让他去买烟…
接过钱,去买烟
路上遇到一家游戏厅,进去玩10s钟…
10s过去了
9s过去了
。。。
0s过去了
赶紧去买烟…
把烟递给老爸,然后把钱给他…
接过烟,吸一口…
把零花钱给儿子…
3.3 Interrupt
void interrupt() 为线程添加一个中断标识
boolean isInterrupted() 测试此线程是否已被中断,是否已经调用过 interrupt()方法添加中断标识,是->true 不是->false
static boolean interrupted() 测试当前线程是否已被中断, 是否已经调用过 interrupt()方法添加中断标识,是->true 不是->false,同时会复位标识
注意: 一个线程如果进入终止状态,线程的中断标识会复位
sleep--> InterruptedException - 如果有任何线程中断了当前线程。 抛出此异常时,将清除当前线程的中断状态 。
public class Class004_Interrupt implements Runnable{
@Override
public void run() {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=1;i<=100;i++){
if(Thread.interrupted()){
System.out.println("--------->"+Thread.currentThread().isInterrupted());
System.out.println("结束");
break;
}
System.out.println(i);
}
}
public static void main(String[] args) {
Thread th = new Thread(new Class004_Interrupt());
//就绪
th.start();
System.out.println(th.isInterrupted());
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//为th线程添加中断标识
th.interrupt();
System.out.println(th.isInterrupted());
}
}
false
1
2
3
4
5
.。。
40
41
42
true
--------->false
结束
如果上面的Thread.sleep() 秒数比下面大,会报错 sleep.interrupt。
3.4getSate
getState() 获取当前线程的状态
Thread.State 线程状态。 线程可以处于以下状态之一:
NEW
尚未启动的线程处于此状态。
RUNNABLE
在Java虚拟机中执行的线程处于此状态。
BLOCKED
被阻塞等待监视器锁定的线程处于此状态。多线程操作下,处于等待对象锁的线程处于这种状态
WAITING
无限期等待另一个线程执行特定操作的线程处于此状态。 wait(),join()
TIMED_WAITING
正在等待另一个线程执行最多指定等待时间的操作的线程处于此状态。阻塞与时间相关的线程处于这种状态 sleep(ms),join(ms),wait(ms)
TERMINATED
已退出的线程处于此状态。
3.5 Priority
Priority 线程的优先级
线程的优先级越高,有限执行的可能性越大
1~10 线程优先级的数值范围
默认5 NORM_PRIORITY
最小1 MIN_PRIORITY
最大10 MAX_PRIORITY
setPriority(int) 设置一个线程的优先级
getPriority() 获取一个线程的优先级
public class Class006_Priority implements Runnable{
public static void main(String[] args) {
Class006_Priority pri = new Class006_Priority();
Thread th1 = new Thread(pri,"A");
Thread th2 = new Thread(pri,"B");
Thread th3 = new Thread(pri,"C");
th1.setPriority(1);
th3.setPriority(Thread.MAX_PRIORITY);
System.out.println(th1.getPriority());
System.out.println(th2.getPriority());
System.out.println(th3.getPriority());
th1.start();
th2.start();
th3.start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
3.6守护线程daemom
线程:
用户线程
守护线程
守护线程就是用来守护用户线程,当程序中所有的用户线程全部执行完毕,守护线程会直接结束
setDaeson(true) 设置守护线程
isDaeson() 判断线程是否为守护线程
现在所创建的线程默认都是用户线程
垃圾回收机制典型的守护线程
public class Class007_Daemon{
public static void main(String[] args) {
Thread th = new Thread(()->{
int i = 1;
while(true){
System.out.println("守护线程"+i++);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
/*设置为守护线程*/
th.setDaemon(true);
//开启线程
th.start();
for(int i=100;i<=110;i++){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
System.out.println("主线程执行完毕");
}
}
100
守护线程1
101
102
103
104
105
守护线程2
106
107
108
109
110
主线程执行完毕
守护线程3
4.Synchronized线程安全问题
线程安全问题:
多线程同时操作同一份资源才有可能遇到线程不安全问题
同步锁: 让关键重点代码排队执行,其他代码依旧同时执行,在提高效率的基础上保证数据安全
synchronized 关键字
关注点:
1.限制条件: 对象锁(每一个对象只有一把锁) 锁哪一个对象能锁住
2.同步的代码范围: 范围太大,效率低,范围太小,容易锁不住
使用方式:
同步方法
静态方法
排队执行的代码范围: 方法体
锁的对象 : 锁类
成员方法 :
排队执行的代码范围: 方法体
锁的对象 : 调用成员方法的对象
同步块
synchronized(锁的对象){
排队执行的代码;
}
锁的对象 : this 类:类的Class对象 资源
同步方法特点:
1.简单
2.范围太大,效率低
4.1同步方法
public class Class001_Web12306 implements Runnable{
int tickets = 100; //100张票
/*
同步方法
*/
@Override
public void run() {
//重复买票
while(true){
//没有票停止购买
//A B C
if(buyTicket()){
break;
}
/*方法问题的可能性*/
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 封装每一次购票的流程
* @return
* true: 结束购票
* false: 继续购买下一章
*/
public synchronized boolean buyTicket(){
if(tickets<=0){
return true;
}
System.out.println(Thread.currentThread().getName()+"正在购买第"+ tickets-- +"张票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
}
public static void main(String[] args) {
Class001_Web12306 web = new Class001_Web12306();
//创建线程
Thread th1 = new Thread(web,"石潘超");
Thread th2 = new Thread(web,"郑达");
Thread th3 = new Thread(web,"刘本军");
th1.start();
th2.start();
th3.start();
}
}
石潘超正在购买第100张票
郑达正在购买第99张票
刘本军正在购买第98张票
郑达正在购买第97张票
。。。
石潘超正在购买第4张票
郑达正在购买第3张票
刘本军正在购买第2张票
郑达正在购买第1张票
4.2同步块
同步块
synchronized(对象){
同步代码段;
}
()-->
this
类
资源
注意:
同步成员方法,相当于同步调用成员方法的对象,相当于同步this
成员方法中this指代调用成员方法的对象
锁对象相当于锁住了这个对象的所有资源|成员,如果只想要锁住某个资源,可以单独只锁资源
public class Class002_Web12306 implements Runnable{
int tickets = 100; //100张票
public void run() {
//重复买票
while(true){
//同步块
synchronized (this){
if(tickets<=0){
break;
}
System.out.println(Thread.currentThread().getName()+"正在购买第"+ tickets-- +"张票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/*多次购买之间休眠一定时间*/
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Class002_Web12306 web = new Class002_Web12306();
//创建线程
Thread th1 = new Thread(web,"石潘超");
Thread th2 = new Thread(web,"郑达");
Thread th3 = new Thread(web,"刘本军");
th1.start();
th2.start();
th3.start();
}
}
结果同上一题
同步块
synchronized(对象){
同步代码段;
}
()-->
this
类
资源
注意:
锁不变的内容
自定义引用数据类型对象地址不变
双重检查 double check可以提高效率
public class Class003_Web12306 implements Runnable{
Tickets tickets = new Tickets(); //自定义引用数据类型的对象
public void run() {
//重复买票
while(true){
//同步块
//ABC
if(tickets.num<=0){
break;
}
synchronized (tickets){
//双重检查 double check
if(tickets.num<=0){
break;
}
System.out.println(Thread.currentThread().getName()+"正在购买第"+ tickets.num-- +"张票");
/* try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
/*多次购买之间休眠一定时间*/
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Class003_Web12306 web = new Class003_Web12306();
//创建线程
Thread th1 = new Thread(web,"石潘超");
Thread th2 = new Thread(web,"郑达");
Thread th3 = new Thread(web,"刘本军");
th1.start();
th2.start();
th3.start();
}
}
//票
class Tickets{
int num = 100;
}
同步块
synchronized(对象){
同步代码段;
}
()-->
类 : 类的Class对象
获取类型的Class对象-->类名.class
注意:
锁类相当于锁了这个类的所有对象,如果只想要锁当前类得到某一个对象,可以直接去锁对象,用哪个锁哪个
public class Class004_Web12306 implements Runnable{
Tickets tickets = new Tickets(); //自定义引用数据类型的对象
public void run() {
//重复买票
while(true){
synchronized (Class004_Web12306.class){
if(tickets.num<=0){
break;
}
System.out.println(Thread.currentThread().getName()+"正在购买第"+ tickets.num-- +"张票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/*多次购买之间休眠一定时间*/
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Class004_Web12306 web = new Class004_Web12306();
//创建线程
Thread th1 = new Thread(new Class004_Web12306(),"石潘超");
Thread th2 = new Thread(new Class004_Web12306(),"郑达");
Thread th3 = new Thread(new Class004_Web12306(),"刘本军");
th1.start();
th2.start();
th3.start();
}
}
石潘超正在购买第100张票
郑达正在购买第100张票
刘本军正在购买第100张票
郑达正在购买第99张票
石潘超正在购买第99张票
。。
郑达正在购买第2张票
石潘超正在购买第8张票
郑达正在购买第1张票
石潘超正在购买第7张票
石潘超正在购买第6张票
5.wait和notify
线程通信
多线程之间打成通信沟通的效果,协作完成业务需求
Object:
wait() 线程等待 当调用某一个对象 的wait方法,当前线程就会进入到与这个对象相关的等待池中进行等待--->等待阻塞,等待被唤醒
会让出cpu的资源,并且会释放对象的锁
notify() 唤醒线程 当调用一个对象的notify方法,会唤醒当前对象等待池中正在等待的线程,唤醒某一个
这个线程会进入到就绪状态,要想要运行: 1)cpu的调度 2)获取对象锁
wait(ms) 阻塞等待指定时间
notifyAll() 唤醒全部
wait与notify必须使用在一个同步环境下,用于控制多线程之间协调工作问题,保证数据安全
sleep与wait之间区别
sleep : 线程休眠 抱着资源睡觉: 让出cpu资源,抱着对象的锁
人车共用街道:
街道 : 红绿灯 boolean flag 绿灯-->人走 true 红灯-->车走 false ns南北走向 we东西走向
人 : ns南北
车 : we东西
生产者消费者模式:
通过信号灯法
public class Class001_Wait {
public static void main(String[] args) {
Street street = new Street(); //共享街道
new Thread(new Person(street)).start();
new Thread(new Car(street)).start();
}
}
//街道
class Street{
//红绿灯
private boolean flag = false;
//ns
public synchronized void ns(){
//判断是否为绿灯
if(flag){
/* try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
System.out.println("人走......");
//红绿灯变为红灯
flag=false;
//唤醒对方线程
this.notify();
//自己等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//we
public synchronized void we(){
if(!flag){
/*try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
System.out.println("车走......");
//红绿灯变为红灯
flag=true;
//唤醒对方线程
this.notify();
//自己等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//人
class Person implements Runnable{
//街道
private Street street = null;
public Person(Street street) {
this.street = street;
}
@Override
public void run() {
while(true){
street.ns();
}
}
}
//车
class Car implements Runnable{
//街道
private Street street = null;
public Car(Street street) {
this.street = street;
}
@Override
public void run() {
while(true){
street.we();
}
}
}
。。。
人走…
车走…
人走…
车走…
。。。