线程同步,线程间的通信

1.线程同步

关键字:synchronized

百度一下:synchronized 关键字,代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法(或者该类的其他同步方法),有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完这个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行。它包括两种用法:synchronized 方法和 synchronized 块。

保证当前线程完整执行该方法后,才能由其他线程调用。

2.以银行存取款为例

public class Bank {
    private String account;
    private Integer balance;

    public Bank(String account, Integer balance) {
        this.account = account;
        this.balance = balance;
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public Integer getBalance() {
        return balance;
    }

    public void setBalance(Integer balance) {
        this.balance = balance;
    }

    @Override
    public String toString() {
        return "Bank{" +
                "account='" + account + '\'' +
                ", balance=" + balance +
                '}';
    }

    //存100
    public void saveAccount(){
    //加上延时,模拟更加真实 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } setBalance(this.getBalance()+100); System.out.println("存款后:"+getBalance()); } public void drawAccount(){ Integer temp= getBalance()-200;
  //加上延时,模拟更加真实
  try { Thread.sleep(1000);
} catch (InterruptedException e) {
  e.printStackTrace();
} setBalance(temp); System.out.println("取款后:"+getBalance()); } }
//存款
class Save implements Runnable{
    private Bank bank;
    public Save(Bank bank){
        this.bank=bank;
    }
    @Override
    public void run() {
        bank.saveAccount();
    }
}
//取款
class Draw implements Runnable{
    private Bank bank;
    public Draw(Bank bank){
        this.bank=bank;
    }
    @Override
    public void run() {
        bank.drawAccount();
    }
}
public class BankTest {
    public static void main(String[] args) {
        Bank bank=new Bank("Sir li",1000);
        Save save=new Save(bank);
        Draw draw=new Draw(bank);
        Thread thread=new Thread(save);
        Thread thread1=new Thread(draw);
        thread.start();
        thread1.start();
        try {
            thread.join();
            thread1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(bank);
    }
}

 

运行结果:

存款后:1100
取款后:1100
Bank{account='Sir li', balance=1100}

 可以看到程序是:在执行了saveAccount()后,balance=1000+100, 在准备执行drawAccount()时,碰到了延时,没有执行 setBalance(temp),直接执行输出语句,最后时主线程中的语句,join()保证了线程的执行顺序没有问题,但是跳过了方法中某些语句的执行。

解决办法:

关键字:synchronized 。给方法加锁

运行结果:正常

存款后:1100
取款后:900
Bank{account='Sir li', balance=900}

 3.线程间通信

  关键字:wait(),死锁,notify(),notifyAll()

举例:生产与消费。 生产n个,消费 n个,来看看程序如何处理

public class Queue {
    private int n;
    public synchronized int getN() {
        System.out.println("消费"+n);
        return n;
    }

    public synchronized void setN(int n) {
        System.out.println("生产"+n);
        this.n = n;
    }
}
class Producer implements Runnable{
    private Queue queue;
    public Producer(Queue queue){
        this.queue=queue;
    }
    @Override
    public void run() {
        int i=0;
        while (true){
            queue.setN(i++);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}
class Consumer implements Runnable{
    private Queue queue;
    public Consumer(Queue queue){
        this.queue=queue;
    }
    @Override
    public void run() {
        while (true){
            queue.getN();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class ProducerTest {

    public static void main(String[] args) {
        Queue queue=new Queue();
        Producer producer=new Producer(queue);
        Consumer consumer=new Consumer(queue);
        Thread thread=new Thread(producer);
        Thread thread1=new Thread(consumer);
        thread.start();
        thread1.start();
    }
}

运行结果

生产0
消费0
生产1 //生产一次消费了两次,程序还是有问题的
消费1
消费1
生产2
消费2

程序应该满足这样的要求:在没有生产的时候是不能消费的,消费的线程需要”等待“。同样在没有被消费之前,也是不能生产的,生产线程需要的”等待“。

public class Queue {
    private int n;
    boolean isHave=false;// 增加判断条件

    public synchronized int getN() {
        if (!isHave){
            try {
                wait(); //当没有生产的时候,进入阻塞状态
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("消费"+n);
        isHave=false;  
        return n;
    }

    public  synchronized void setN(int n) {
        if(isHave){
            try {
                wait(); //在没有消费的时候,进行阻塞状态
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("生产"+n);
        this.n = n;
        isHave=true;
    }
}

运行结果:

生产0
消费0
生产1
消费1
生产2//程序停止输出了,这时候进入了死锁状态,线程之间都在互相等待,需要执行唤醒操作

解决办法:

在getN(),setN()方法中添加:notifyAll();//唤醒线程

结果正常了。。。

4.总结

join():会让当前线程执行完毕,再去执行别的线程。保证了线程的执行顺序。

sychronized: 让被修饰的方法完整的执行。

wait():从功能来看,与判断组合,它也保证了线程的执行顺序。

死锁:程序因为遭到线程都在“等待”状态,而无法运行的情况。

上一篇:Django的事务


下一篇:【诡异并发三大恶人】原子性