4.线程的同步
4.1 为什么会存在线程安全问题?
多个线程操作共享数据的时候,为执行完的情况下,另外的线程参与进来,导致共享数据存在安全问题
4.2 如何解决线程的安全问题?
在一个线程操作共享数据的时候,必须让这个线程操作完毕之后,其他线程才有机会参与操作
4.3 java中的线程同步机制
方式一、同步代码块
synchronized(同步监视器){
//需要同步的代码块
}
1. 共享数据
2. 同步监视器,由一个类来充当(任何一个类都有可以) 哪个线程获取此监视器,就执行同步代码块、
要求:在需要所有线程都操作同一数据的时候,所有的线程必须公用同一把锁。
一般在实现Runnable接口的的方式中使用this充当同步监视器
而在继承Thread的方式中,共享数据和同步监视器都应该标识为 static 才能达到同步的效果
方式二、同步方法
show();
public synchronized void show(){
}
将操作共享数据的方法声明为synchronized,当一个线程在执行此方法的时,其他线程只有等待该线程执行完毕之后才可以操作同步方法
和同步代码块存在一样的问题,两种线程同步的方式,在操作过程中都应该公用一把锁,
而在继承Thread的方式中,所有的线程都会同时的拥有自己的一把锁(当前对象),这种方式在同步代码块中只需要将当前对象声明为 static 即可解决。而在同步方法中,也可以这样解决
4.4 单利模式的线程安全问题
单利模式(懒汉式)同样的在并发的情况下,有可能发生线程安全问题。使用synchronized(this){}同步代码块同样可以解决。
4.5 线程同步的好处
可以使得程序数据更加的安全。
注:在使用实现Runnable接口创建线程的时候,可以使用this(当前对象)来充当锁(同步监视器)。
但在使用继承Thread类创建线程的时候,必须保证每个独立线程都公用一把锁的,this慎重使用。
弊端
在使用线程同步的同时,虽然增加了数据的安全性,但是因为在同一时间只能执行一个线程,
但效率变低了
有可能出现死锁
4.6 锁
释放锁的操作
当前线程同步代码块 同步方法执行结束之后释放
当前线程执行期间出现了未处理的Error 和 Exception
执行了当前对象的wait()方法
sleep()方法和wait()方法的区别
sleep()方法在线程执行中不会释放资源
wait()方法在线程执行的时候会释放资源
4.7 死锁
不同的线程在执行过程中分别占用对方需要的资源不放弃,都在等待对方放弃自己需要的源就形成了死锁
解决方法
专门的算法 规则
尽量减少同步资源的定义
5.线程的通信
设计到三个方法
wait(): 令线程挂起 并放弃CPU资源,同步资源
notifyAll(): 唤醒正在排队等待同步资源的所有线程结束等待
notify(): 唤醒正在排队等待同资源的线程优先级最高者结束等待
使用方式 都必须在同步代码块中使用
6.生产者和消费者
class Clerk {// 商店
int product = 10;// 商品
public synchronized void addProduct() {
if (product >= 20) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
product++;
System.out.println(Thread.currentThread().getName() + ":生产了第" + product + "产品");
notifyAll();
}
}
public synchronized void customer() {
if (product <= 0) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
System.out.println(Thread.currentThread().getName() + ":消费了第" + product + "产品");
product--;
notifyAll();
}
}
}
class Customer1 implements Runnable {// 消费者
Clerk clerk;
public Customer1(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
clerk.customer();
}
}
}
class Producer implements Runnable {// 生产者
Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
clerk.addProduct();
}
}
}
线程同步的练习
有一个银行账户,两个储户向同一个账号存钱,每次存1000,每次存完打印账户余额
package com.feng.thread;
class Bank {
private int money = 0;
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
}
class Person implements Runnable {
private Bank bank = new Bank();
int money = 0;
@Override
public void run() {
while (true) {
synchronized (this) {
if (money < 3000) {
bank.setMoney(money+=1000);
System.out.println(Thread.currentThread().getName() + "存入了:====" + "1000" + " 当前余额:====" + bank.getMoney());
} else {
return;
}
}
}
}
}