java虚拟机是支持多线程的,当运行Java程序时,至少已经有一个线程了,那就是main线程。
如何创建和启动一个新的线程:
Java中java.lang.Thread
是表示线程的类,每个Thread类或其子类的实例代表一个线程对象。
通过继承Thread类来创建并启动多线程的步骤:
自定义线程类:
public class MyThread extends Thread {
//定义指定线程名称的构造方法
public MyThread(String name) {
//调用父类的String参数的构造方法,指定线程的名称
super(name);
}
/**
* 重写run方法,完成该线程执行的逻辑
*/
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName()+":正在执行!"+i);
}
}
}
测试类:创建线程对象并启动线程
public class Demo01 {
public static void main(String[] args) {
//创建自定义线程对象
MyThread mt = new MyThread("新的线程!");
//开启新线程
mt.start();
//在主方法中执行for循环
for (int i = 0; i < 10; i++) {
System.out.println("main线程!"+i);
}
}
}
多线程执行情况分析
注意事项:
IllegalThreadStateException
System.exit()
直接退出JVM;Java有单继承的限制,当我们无法继承Thread类时,那么该如何做呢?在核心类库中提供了Runnable接口,我们可以实现Runnable接口,重写run()方法,然后再通过Thread类的对象代理启动和执行我们的线程体run()方法
通过实现Runnable接口创建线程并启动的步骤:
自定义线程任务类:
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
测试类:创建线程对象并启动线程
public class Demo {
public static void main(String[] args) {
//创建自定义类对象 线程任务对象
MyRunnable mr = new MyRunnable();
//创建线程对象
Thread t = new Thread(mr, "小强");
t.start();
for (int i = 0; i < 20; i++) {
System.out.println("旺财 " + i);
}
}
}
Thread类本身也是实现了Runnable接口的,run方法都来自Runnable接口,run方法也是真正要执行的线程任务。
public class Thread implements Runnable {}
匿名内部类对象的方式创建线程,并不是一种新的创建线程的方式,只是在线程任务只需执行一次的情况下,我们无需单独创建线程类,可以采用匿名对象的方式:
new Thread("新的线程!"){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName()+":正在执行!"+i);
}
}
}.start();
new Thread(new Runnable(){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+":" + i);
}
}
}).start();
public final void setPriority(int newPriority) :改变线程的优先级
public static void main(String[] args) {
Thread t = new Thread(){
public void run(){
System.out.println(getName() + "的优先级:" + getPriority());
}
};
t.setPriority(Thread.MAX_PRIORITY);
t.start();
System.out.println(Thread.currentThread().getName() +"的优先级:" + Thread.currentThread().getPriority());
}
void join() :加入线程,当前线程中加入一个新线程,等待加入的线程终止后再继续执行当前线程。
void join(long millis) :等待该线程终止的时间最长为 millis 毫秒。如果millis时间到,将不再等待。
void join(long millis, int nanos) :等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。
public final void stop():强迫线程停止执行。 该方法具有不安全性,已被弃用,最好不要使用。
public static void main(String[] args) {
for (int i = 10; i>=0; i--) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("新年快乐!");
}
主线程:打印[1,10],每隔10毫秒打印一个数字,
自定义线程类:不停的问是否结束,输入Y或N,
现在当主线程打印完5之后,就让自定义线程类加塞,直到自定义线程类结束,主线程再继续。
import java.util.Scanner;
public class TestJoin {
public static void main(String[] args) {
ChatThread t = new ChatThread();
t.start();
for (int i = 1; i <= 10; i++) {
System.out.println("main:" + i);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//当main打印到5之后,需要等join进来的线程停止后才会继续了。
if(i==5){
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class ChatThread extends Thread{
public void run(){
Scanner input = new Scanner(System.in);
while(true){
System.out.println("是否结束?(Y、N)");
char confirm = input.next().charAt(0);
if(confirm == 'Y' || confirm == 'y'){
break;
}
}
input.close();
}
}
案例:编写龟兔赛跑多线程程序,设赛跑长度为30米
兔子的速度是10米每秒,兔子每跑完10米休眠的时间10秒
乌龟的速度是1米每秒,乌龟每跑完10米的休眠时间是1秒
要求:要等兔子和乌龟的线程结束,主线程(裁判)才能公布最后的结果。
public class Racer extends Thread {
private String name;//运动员名字
private long runTime;//每米需要时间,单位毫秒
private long restTime;//每10米的休息时间,单位毫秒
private long distance;//全程距离,单位米
private long totalTime;//跑完全程的总时间
public Racer(String name, long distance, long runTime, long restTime) {
super();
this.name = name;
this.distance = distance;
this.runTime = runTime;
this.restTime = restTime;
}
@Override
public void run() {
long sum = 0;
long start = System.currentTimeMillis();
while (sum < distance) {
System.out.println(name + "正在跑...");
try {
Thread.sleep(runTime);// 每米距离,该运动员需要的时间
} catch (InterruptedException e) {
return ;
}
sum++;
try {
if (sum % 10 == 0 && sum < distance) {
// 每10米休息一下
System.out.println(name+"已经跑了"+sum+"米正在休息....");
Thread.sleep(restTime);
}
} catch (InterruptedException e) {
return ;
}
}
long end = System.currentTimeMillis();
totalTime = end - start;
System.out.println(name+"跑了"+sum+"米,已到达终点,共用时"+totalTime/1000.0+"秒");
}
public long getTotalTime() {
return totalTime;
}
}
public class TestJoin {
public static void main(String[] args) {
Racer rabbit = new Racer("兔子", 30, 100, 10000);
Racer turtoise = new Racer("乌龟", 30, 1000, 1000);
rabbit.start();
turtoise.start();
//因为要兔子和乌龟都跑完,才能公布结果
try {
rabbit.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
turtoise.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("比赛结束");
if(rabbit.getTotalTime()==turtoise.getTotalTime()){
System.out.println("平局");
}else if(rabbit.getTotalTime()<turtoise.getTotalTime()){
System.out.println("兔子赢");
}else{
System.out.println("乌龟赢");
}
}
}
案例:编写龟兔赛跑多线程程序,设赛跑长度为30米
兔子的速度是10米每秒,兔子每跑完10米休眠的时间10秒
乌龟的速度是1米每秒,乌龟每跑完10米的休眠时间是1秒
要求:只要兔子和乌龟中有人到达终点,就宣布比赛结束,没到达终点的也停下来。
public class Player extends Thread{
private String name;//运动员名字
private long runTime;//每米需要时间,单位毫秒
private long restTime;//每10米的休息时间,单位毫秒
private long distance;//全程距离,单位米
private boolean flag = true;
private volatile boolean ended = false;
public Player(String name, long distance, long runTime, long restTime) {
super();
this.name = name;
this.distance = distance;
this.runTime = runTime;
this.restTime = restTime;
}
@Override
public void run() {
long sum = 0;
while (sum < distance && flag) {
System.out.println(name + "正在跑...");
try {
Thread.sleep(runTime);// 每米距离,该运动员需要的时间
} catch (InterruptedException e) {
break ;
}
sum++;
try {
if (sum % 10 == 0 && sum < distance && flag) {
// 每10米休息一下
System.out.println(name+"已经跑了"+sum+"米正在休息....");
Thread.sleep(restTime);
}
} catch (InterruptedException e) {
break ;
}
}
ended = sum == distance ? true : false;
System.out.println(name+"跑了"+sum+"米");
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public boolean isEnded() {
return ended;
}
}
public class TestStop {
public static void main(String[] args) {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
Player rabbit = new Player("兔子", 30, 100, 10000);
Player turtoise = new Player("乌龟", 30, 1000, 1000);
rabbit.start();
turtoise.start();
while(true){
if(rabbit.isEnded() || turtoise.isEnded()){
rabbit.setFlag(false);
turtoise.setFlag(false);
rabbit.interrupt();//中断休眠
turtoise.interrupt();//中断休眠
//只要有人跑完,就结束比赛,并公布结果
break;
}
}
System.out.println("比赛结束");
if(rabbit.isEnded() && turtoise.isEnded()){
System.out.println("平局");
}else if(rabbit.isEnded()){
System.out.println("兔子赢");
}else{
System.out.println("乌龟赢");
}
}
}
volatile的作用是确保不会因编译器的优化而省略某些指令,volatile的变量是说这变量可能会被意想不到地改变,每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份,这样,编译器就不会去假设这个变量的值了。
public class TestThread {
public static void main(String[] args) {
MyDaemon m = new MyDaemon();
m.setDaemon(true);
m.start();
for (int i = 1; i <= 100; i++) {
System.out.println("main:" + i);
}
}
}
class MyDaemon extends Thread {
public void run() {
while (true) {
System.out.println("我一直守护者你...");
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
当我们使用多个线程访问同一资源(可以是同一个变量、同一个文件、同一条记录等)的时候,但是如果多个线程中对资源有读和写的操作,就会出现前后数据不一致问题,这就是线程安全问题。
案例:三个窗口售卖共100张火车票。
示例代码:
package com.atguigu.safe;
public class SaleTicketDemo1 {
public static void main(String[] args) {
Window w1 = new Window();
Window w2 = new Window();
Window w3 = new Window();
w1.start();
w2.start();
w3.start();
}
}
class Window extends Thread{
public void run(){
int total = 100;
while(total>0) {
System.out.println(getName() + "卖出一张票,剩余:" + --total);
}
}
}
结果:发现卖出300张票。
问题:局部变量是每次调用方法都是独立的,那么每个线程的run()的total是独立的,不是共享数据。
package com.atguigu.safe;
public class SaleTicketDemo2 {
public static void main(String[] args) {
TicketSaleThread t1 = new TicketSaleThread();
TicketSaleThread t2 = new TicketSaleThread();
TicketSaleThread t3 = new TicketSaleThread();
t1.start();
t2.start();
t3.start();
}
}
class TicketSaleThread extends Thread{
private int total = 10;
public void run(){
while(total>0) {
System.out.println(getName() + "卖出一张票,剩余:" + --total);
}
}
}
结果:发现卖出300张票。
问题:不同的实例对象的实例变量是独立的。
示例代码:
package com.atguigu.safe;
public class SaleTicketDemo3 {
public static void main(String[] args) {
TicketThread t1 = new TicketThread();
TicketThread t2 = new TicketThread();
TicketThread t3 = new TicketThread();
t1.start();
t2.start();
t3.start();
}
}
class TicketThread extends Thread{
private static int total = 10;
public void run(){
while(total>0) {
try {
Thread.sleep(10);//加入这个,使得问题暴露的更明显
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + "卖出一张票,剩余:" + --total);
}
}
}
结果:发现卖出近100张票。
问题(1):但是有重复票或负数票问题。
原因:线程安全问题
问题(2):如果要考虑有两场电影,各卖100张票,这场卖完就没票了,新的线程对象也没有票卖了
原因:TicketThread类的静态变量,是所有TicketThread类的对象共享。本来成员变量就是run方法共享的数据,再用static不合适。
示例代码:多个Thread线程使用同一个Runnable对象
package com.atguigu.safe;
public class SaleTicketDemo3 {
public static void main(String[] args) {
TicketSaleRunnable tr = new TicketSaleRunnable();
Thread t1 = new Thread(tr,"窗口一");
Thread t2 = new Thread(tr,"窗口一");
Thread t3 = new Thread(tr,"窗口一");
t1.start();
t2.start();
t3.start();
}
}
class TicketSaleRunnable implements Runnable{
private int total = 10;
public void run(){
while(total>0) {
try {
Thread.sleep(10);//加入这个,使得问题暴露的更明显
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖出一张票,剩余:" + --total);
}
}
}
结果:发现卖出近100张票。
问题:但是有重复票或负数票问题。
原因:线程安全问题
出现重复打印票和负数的问题分析(跟阻塞没关系):
总结:线程安全问题的出现因为具备了以下条件
上述线程安全问题的必备条件1和2是我们需要的,要解决只能从第三个点上想办法。要解决上述多线程并发访问一个资源的安全性问题:也就是解决重复票与不存在票问题,Java中提供了线程同步机制来解决。
Java中常使用关键字synchronized 来实现同步机制:
同步方法:synchronized 关键字直接修饰方法,表示同一时刻只有一个线程能进入这个方法,其他线程在外面等着。
public synchronized void method(){
可能会产生线程安全问题的代码
}
同步代码块:synchronized 关键字可以用于某个区块前面,表示只对这个区块的资源实行互斥访问。
格式:
synchronized(同步锁){
需要同步操作的代码
}
同步锁对象:
代码示例:
//售票线程任务类
class SaleTicket implements Runnable {
//票数
private int count = 100;//共享资源
@Override
public void run() {
while (true) {
//同步代码块,this为锁对象
synchronized (this) {
if (count > 0) {
System.out.println(Thread.currentThread().getName() + "--" + count);
count--;
}
}
}
}
}
//测试类
public class DemoSaleTicket {
public static void main(String[] args) {
//创建线程任务对象
SaleTicket st = new SaleTicket(ticket);
//创建售票线程对象,并启动线程
new Thread(st).start();
new Thread(st).start();
}
}
代码改进:
//售票线程任务类
class SaleTicket implements Runnable {
private int count = 100;
//售票线程任务
@Override
public void run() {
while (true) {
synchronized (this) {
sell();//调用售票方法
}
}
}
//提取出售票方法
private void sell() {
if (count > 0) {
System.out.println(Thread.currentThread().getName() + "--" + count);
count--;
}
}
}
//测试类
public class DemoSaleTicket {
public static void main(String[] args) {
//创建线程任务对象
SaleTicket st = new SaleTicket(ticket);
//创建售票线程对象,并启动线程
new Thread(st).start();
new Thread(st).start();
}
}
示例代码:
public class SaleTicket implements Runnable {
private int count = 100;
@Override
public void run() {
while (true) {
//直接调用同步方法
sell();
}
}
//将售票方法改进为:同步方法,非静态的同步方法的锁对象默认为this
private synchronized void sell() {
if (count > 0) {
System.out.println(Thread.currentThread().getName() + "--" + count);
count--;
}
}
}
示例改造:
//共享资源类(将共享数据与同步方法封装到一个类中)
class Ticket {
private int count=100;//票数
//****同步方法,非静态同步方法的锁对象默认为this
public synchronized void sell() {
if (count > 0) {
System.out.println(Thread.currentThread().getName() + "--" + count--);
}
}
public int getCount() {
return count;
}
}
//线程任务类
class SaleTicketRunnable implements Runnable {
//共享的资源
private Ticket ticket;
//通过构造器传入共享资源
public SaleTicketRunnable(Ticket ticket) {
this.ticket = ticket;
}
//线程任务
@Override
public void run() {
while (true) {
//售票
ticket.sell();
if(ticket.getCount()<=0)//售完跳出循环,结束线程任务
break;
}
}
}
//测试类
public class DemoSaleTicket {
public static void main(String[] args) {
//创建共享资源
Ticket ticket = new Ticket();
//创建资源操作线程对象
SaleTicket st = new SaleTicket(ticket);
//创建售票线程对象,并启动线程
new Thread(st).start();
new Thread(st).start();
}
}
锁的范围太小:不能解决安全问题,要同步所有操作共享资源的语句。
锁的范围太大:因为一旦某个线程抢到锁,其他线程就只能等待,所以范围太大,效率会降低,不能合理利用CPU资源。
原则:
步骤:
public class TestSynchronized {
public static void main(String[] args) {
// 2、创建资源对象
Ticket ticket = new Ticket();
// 3、启动多个线程操作资源类的对象
Thread t1 = new Thread("窗口一") {
public void run() {
while (true) {
try {
Thread.sleep(10);// 加入这个,使得问题暴露的更明显
ticket.sale();
} catch (Exception e) {
e.printStackTrace();
break;
}
}
}
};
Thread t2 = new Thread("窗口二") {
public void run() {
while (true) {
try {
Thread.sleep(10);// 加入这个,使得问题暴露的更明显
ticket.sale();
} catch (Exception e) {
e.printStackTrace();
break;
}
}
}
};
Thread t3 = new Thread(new Runnable() {
public void run() {
while (true) {
try {
Thread.sleep(10);// 加入这个,使得问题暴露的更明显
ticket.sale();
} catch (Exception e) {
e.printStackTrace();
break;
}
}
}
}, "窗口三");
t1.start();
t2.start();
t3.start();
}
}
// 1、编写资源类
class Ticket {
private int total = 10;
public synchronized void sale() {
if(total<=0){
throw new RuntimeException(Thread.currentThread().getName() + "发现没有票了");
}
System.out.println(Thread.currentThread().getName() + "卖出一张票,剩余:" + --total);
}
public int getTotal() {
return total;
}
}
设计模式:设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
是软件工程中解决特定问题的最佳实践方案。它们提供了一种在软件开发中遇到常见问题时,可复用的、经过验证的解决方案
饿汉式:上来就创建对象
package com.atguigu.thread4;
public class OnlyOneDemo {
public static void main(String[] args) {
OnlyOne o1 = OnlyOne.INSTANCE;
OnlyOne o2 = OnlyOne.INSTANCE;
System.out.println(o1);
System.out.println(o2);
System.out.println(o1==o2);
}
}
class OnlyOne{
public static final OnlyOne INSTANCE = new OnlyOne();
private OnlyOne(){
}
}
public class Singleton {
private static Singleton ourInstance;
public static Singleton getInstance() {
//一旦创建了对象,之后再次获取对象,都不会再进入同步代码块,提升效率
if (ourInstance == null) {
//同步锁,锁住判断语句与创建对象并赋值的语句
synchronized (Singleton.class) {
if (ourInstance == null) {
ourInstance = new Singleton();
}
}
}
return ourInstance;
}
private Singleton() {
}
}
//测试类
public class Demo {
public static void main(String[] args) {
//开启多个线程获取单例
new SingletonThread().start();
new SingletonThread().start();
new SingletonThread().start();
}
}
//线程类
class SingletonThread extends Thread{
@Override
public void run() {
Singleton instance = Singleton.getInstance();
System.out.println(instance);//打印对象地址,查看每个线程获取的实例是否同一个
}
}
为什么要处理线程间通信:
多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。而多个线程并发执行时, 在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务,并且我们希望他们有规律的执行, 那么多线程之间需要一些通信机制,可以协调它们的工作,以此来帮我们达到多线程共同操作一份数据。
什么是等待唤醒机制
这是多个线程间的一种协作机制。谈到线程我们经常想到的是线程间的竞争(race),比如去争夺锁,但这并不是故事的全部,线程间也会有协作机制。
就是在一个线程满足某个条件时,就进入等待状态(wait()/wait(time)), 等待其他线程执行完他们的指定代码过后再将其唤醒(notify());或可以指定wait的时间,等时间到了自动唤醒;在有多个线程进行等待时,如果需要,可以使用 notifyAll()来唤醒所有的等待线程。
注意:
被通知线程被唤醒后也不一定能立即恢复执行,因为它当初中断的地方是在同步块内,而此刻它已经不持有锁,所以她需要再次尝试去获取锁(很可能面临其它线程的竞争),成功后才能在当初调用 wait 方法之后的地方恢复执行。
总结如下:
- 如果能获取锁,线程就从 WAITING 状态变成 RUNNABLE(可运行) 状态;
- 否则,线程就从 WAITING 状态又变成 BLOCKED(等待锁) 状态
调用wait和notify方法需要注意的细节
等待唤醒机制可以解决经典的“生产者与消费者”的问题。
生产者与消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了两个(多个)共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
生产者与消费者问题中其实隐含了两个问题:
线程的协调工作问题:
案例:有家餐馆的取餐口比较小,只能放10份快餐,厨师做完快餐放在取餐口的工作台上,服务员从这个工作台取出快餐给顾客。现在有1个厨师和1个服务员。
package com.atguigu.thread5;
public class TestCommunicate {
public static void main(String[] args) {
// 1、创建资源类对象
Workbench workbench = new Workbench();
// 2、创建和启动厨师线程
new Thread("厨师") {
public void run() {
while (true) {
workbench.put();
}
}
}.start();
// 3、创建和启动服务员线程
new Thread("服务员") {
public void run() {
while (true) {
workbench.take();
}
}
}.start();
}
}
// 1、定义资源类
class Workbench {
private static final int MAX_VALUE = 10;
private int num;
public synchronized void put() {
if (num >= MAX_VALUE) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num++;
System.out.println(Thread.currentThread().getName() + "制作了一份快餐,现在工作台上有:" + num + "份快餐");
this.notify();
}
public synchronized void take() {
if (num <= 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num--;
System.out.println(Thread.currentThread().getName() + "取走了一份快餐,现在工作台上有:" + num + "份快餐");
this.notify();
}
}
案例:有家餐馆的取餐口比较小,只能放10份快餐,厨师做完快餐放在取餐口的工作台上,服务员从这个工作台取出快餐给顾客。现在有多个厨师和多个服务员。
package com.atguigu.thread5;
public class TestCommunicate2 {
public static void main(String[] args) {
// 1、创建资源类对象
WindowBoard windowBoard = new WindowBoard();
// 2、创建和启动厨师线程
// 3、创建和启动服务员线程
Cook c1 = new Cook("张三",windowBoard);
Cook c2 = new Cook("李四",windowBoard);
Waiter w1 = new Waiter("小红",windowBoard);
Waiter w2 = new Waiter("小绿",windowBoard);
c1.start();
c2.start();
w1.start();
w2.start();
}
}
//1、定义资源类
class WindowBoard {
private static final int MAX_VALUE = 10;
private int num;
public synchronized void put() {
while (num >= MAX_VALUE) {//必须循环判断。
try {
this.wait();//wait后会释放锁,Cooker1线程释放后,Cooker2线程获得锁(本应Waiter获得)进来后if继续wait,释放锁,Cooker1又快速获得锁,继续向下执行。。。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num++;
System.out.println(Thread.currentThread().getName() + "制作了一份快餐,现在工作台上有:" + num + "份快餐");
this.notifyAll();
}
public synchronized void take() {
while (num <= 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num--;
System.out.println(Thread.currentThread().getName() + "取走了一份快餐,现在工作台上有:" + num + "份快餐");
this.notifyAll();
}
}
//2、定义厨师类
class Cook extends Thread{
private WindowBoard windowBoard;
public Cook(String name,WindowBoard windowBoard) {
super(name);
this.windowBoard = windowBoard;
}
public void run(){
while(true) {
windowBoard.put();
}
}
}
//3、定义服务员类
class Waiter extends Thread{
private WindowBoard windowBoard;
public Waiter(String name,WindowBoard windowBoard) {
super(name);
this.windowBoard = windowBoard;
}
public void run(){
while(true) {
windowBoard.take();
}
}
}
任何线程进入同步代码块、同步方法之前,必须先获得对同步监视器的锁定,那么何时会释放对同步监视器的锁定呢?
当前线程的同步方法、同步代码块执行结束。
当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致当前线程异常结束。
当前线程在同步代码块、同步方法中执行了锁对象的wait()方法,当前线程被挂起,并释放锁。
不同的线程分别锁住对方需要的同步监视器对象不释放,都在等待对方先放弃时就形成了线程的死锁。一旦出现死锁,整个程序既不会发生异常,也不会给出任何提示,只是所有线程处于阻塞状态,无法继续。
public class TestDeadLock {
public static void main(String[] args) {
Object g = new Object();
Object m = new Object();
Owner s = new Owner(g,m);
Customer c = new Customer(g,m);
new Thread(s).start();
new Thread(c).start();
}
}
class Owner implements Runnable{
private Object goods;
private Object money;
public Owner(Object goods, Object money) {
super();
this.goods = goods;
this.money = money;
}
@Override
public void run() {
synchronized (goods) {
System.out.println("先给钱");
synchronized (money) {
System.out.println("发货");
}
}
}
}
class Customer implements Runnable{
private Object goods;
private Object money;
public Customer(Object goods, Object money) {
super();
this.goods = goods;
this.money = money;
}
@Override
public void run() {
synchronized (money) {
System.out.println("先发货");
synchronized (goods) {
System.out.println("再给钱");
}
}
}
}
© 著作权归作者所有
本文由 趣代码Blog 创作,采用 知识共享署名4.0 国际许可协议进行许可,本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。