版权声明:欢迎转载,转载请注明出处哦! https://blog.csdn.net/qq_41647999/article/details/88541889
一、 实例
在进入正题之前,先来看一个例子:
目前不使用线程的通信来完成下面这个功能:
银行有一个账户,有两个储户分别向同一个账户存3000元,每次存1000,存三次。每次存完打印账户余额。
这个例子也会存在,我在之前的博文里面写到的线程安全的问题,通过线程的同步方法即可解决。
class Account{
public Account(){
}
double balance; // 余额
/*
当方法声明为synchronized 时,用的一定是this这个锁。
注意判断此时的this在Account这个对象里面是不是唯一的。
当然,这里在main函数里面是共用的acct这个对象,是唯一的。
*/
public synchronized void deposit(int money){
balance += money;
try {
Thread.currentThread().sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+balance);
}
}
class Customer extends Thread{
Account account;
public Customer(Account account){
this.account = account;
}
public void run(){
for (int i = 0; i < 3; i++) {
account.deposit(1000);
}
}
}
public class TestBankStorageMoney {
public static void main(String[] args) {
Account acct = new Account();
Customer c1 = new Customer(acct);
Customer c2 = new Customer(acct);
c1.setName("甲:");
c2.setName("乙:");
c1.start();
c2.start();
}
}
那么又如何实现两个储户交替实现存钱的操作?先不要急,做事要一步一步的来。
二、 线程通信
在Java.lang.Object提供了以下三个方法,只能在synchronized方法或代码块中使用。
wait() |
“等待状态”。使当前线程挂起并放弃CPU、同步资源,使别的线程可访问并修改共享资源,而当前线程排队等候再次对资源访问。 |
notify() | 唤醒正在排队等待同步资源的线程中优先级最高者结束等待。 |
notifyAll() | 唤醒所有正在排队等待资源的线程。 |
下面举一个例子实现:使用两个线程打印1-100,线程1和线程2交替打印。
当然需要处理一下线程的安全问题,在处理之前的代码和结果如下:
/*
使用两个线程打印1-100,线程1和线程2交替打印
*/
class PrintNum implements Runnable{
int num = 1;
public void run() {
while(true){
if (num <= 100 ){
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":" + num);
num++;
}else {
break;
}
}
}
}
public class TestPrintNum {
public static void main(String[] args) {
PrintNum print = new PrintNum();
Thread t1 = new Thread(print);
Thread t2 = new Thread(print);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
打印出来的数字为什么会出现101呢?如何解决这个问题?
当然是线程的安全问题。因为这里是两个线程,都需要使用同一段代码,直接加上synchronized (同步监视器) { } 代码块。需要考虑的是这里的同步监视器是否可以使用this,那么就需要考虑我们再三强调的:是不是所有的线程都使用的同一把锁。于是这里可以使用this。
请看下方解决BUG的代码:
class PrintNum implements Runnable{
int num = 1;
public void run() {
while(true){
synchronized (this) {
notify();
if (num <= 100 ){
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":" + num);
num++;
}else {
break;
}
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class TestPrintNum {
public static void main(String[] args) {
PrintNum print = new PrintNum();
Thread t1 = new Thread(print);
Thread t2 = new Thread(print);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
是不是对线程通信有一定的了解了,那么下面来解决两个储户交替实现存钱的问题,其实解决也和上例一样。话不多说看代码:
/*
银行有一个账户,有两个储户分别向同一个账户存
3000元,每次存1000,交替存,存三次。每次存完打印账户余额。
*/
class Account2{
public Account2(){
}
double balance; // 余额
/*
当方法声明为synchronized 时,用的一定是this这个锁。
注意判断此时的this在Account这个对象里面是不是唯一的。
当然,这里在main函数里面是共用的acct这个对象,是唯一的。
*/
public synchronized void deposit(int money){
notify();
balance += money;
try {
Thread.currentThread().sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+balance);
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Customer2 extends Thread{
Account2 account;
public Customer2(Account2 account){
this.account = account;
}
public void run(){
for (int i = 0; i < 3; i++) {
account.deposit(1000);
}
}
}
public class TestBankStorageMoney2 {
public static void main(String[] args) {
Account2 acct = new Account2();
Customer2 c1 = new Customer2(acct);
Customer2 c2 = new Customer2(acct);
c1.setName("甲:");
c2.setName("乙:");
c1.start();
c2.start();
}
}