java多线程同步和通信的方法有如下几种:
1、synchronized关键字修饰方法或代码段,实现数据的互斥访问
2、volatile修饰变量,实现多线程环境下数据的同步
3、ReentrantLock可重入锁,实现数据的互斥访问
3、synchronized结合Object的wait和notify方法,实现线程间的等待通知机制
4、ReentrantLock结合Condition接口的await()和signal()方法,实现线程间的等待通知机制
1、synchronized关键字修饰方法或代码段,只保证临界数据是互斥访问的
java的每个对象都有一个内置锁,当用synchronized关键字修饰时,线程会获取该对象的内置锁,其他线程没有获取该对象的内置锁就会进入阻塞状态。
对象锁: synchronized关键字修饰代码段时,需要传入一个对象,通过该对象的内置锁来实现代码块的同步。
synchronized关键字修饰方法时,会将实例化的java对象的内置锁传进去,通过该锁来实现代码块的同步。
类锁: synchronized关键字修饰静态方法,会锁住该类的Class对象,此时如果调用该静态方法,将会锁住整个类。
注意: 同步是一种高开销的操作,因此应该尽量减少同步的内容。通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。
class MyThread extends Thread{
Bank bank;
public MyThread(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
for(int i=0;i<10;i++)
{
bank.save(100); // 多线程调用该同步方法
}
}
}
class Bank{
private int account = 100;//临界数据
public int getAccount(){
return account;
}
//同步方法
public synchronized void save(int money){
account+=money;
System.out.println("当前线程:"+Thread.currentThread().getId()+" 当前余额:"+getAccount());
}
public void save1(int money){
//同步代码块
synchronized(this) { // 获取当前对象锁
account+=money;
System.out.println("当前线程:"+Thread.currentThread().getId()+" 当前余额:"+getAccount());
}
}
}
public static void main(String[] args)
{
Bank bank = new Bank();
MyThread th1 = new MyThread("线程1",bank);
th1.start();
MyThread th2 = new MyThread("线程2",bank);
th2.start();
}
2、volatile修饰变量
使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,因此可以保证在多线程环境下保证数据的一致性
class Bank {
//需要同步的变量加上volatile
private volatile int account = 100;
public int getAccount() {
return account;
}
//这里不再需要synchronized
public void save(int money) {
account += money;
}
}
3、ReentrantLock可重入锁,实现数据的互斥访问
class Bank{
private ReentrantLock lock = new ReentrantLock();
private int account = 100;//临界区数据
public int getAccount(){
return account;
}
public void put(int money)
{
lock.lock();
try {
account+=money;
System.out.println(Thread.currentThread().getId()+"存入"+money+" 当前余额:"+getAccount());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void get(int money)
{
lock.lock();
try {
account-=money;
System.out.println(Thread.currentThread().getId()+"取出"+money+" 当前余额:"+getAccount());
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
}
4、wait和notify,实现线程间的等待通知机制
通常在synchronized修饰的代码块中使用wait、notify/notifyAll函数.
当wait()被执行的时,会释放当前所持有的锁,然后让出CPU,进入等待阻塞状态.
当notify/notifyAll()被执行时候,会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,因此最好在同步代码块最后执行notify/notifyAll.
//消费者线程类,每隔100ms消费一个产品
class CustomerThread extends Thread{
Bank bank;
public CustomerThread(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
while(true){
bank.get(1);
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//生产者线程类,每隔300ms生产一个产品
class ProductorThread extends Thread{
Bank bank;
public ProductorThread(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
while(true){
bank.put(1);
try {
sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//数据缓冲区
class Bank{
private int account = 100;//临界区数据
public int getAccount(){
return account;
}
public void put(int money){
synchronized(this) //获取当前对象锁
{
if(getAccount() >= 120){ //若数量大于120则阻塞当前线程,释放对象锁
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
account+=money;//生产一个产品
System.out.println(Thread.currentThread().getId()+"存入"+money+" 当前余额:"+getAccount());
this.notifyAll();//唤醒其他线程
}
}
public void get(int money){
synchronized(this)
{
if(getAccount() <= 0){ // 若数量小于等于0则阻塞当前线程,释放对象锁
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
account-=money; // 消费一个产品
System.out.println(Thread.currentThread().getId()+"取出"+money+" 当前余额:"+getAccount());
this.notifyAll();//唤醒其他线程
}
}
}
//主函数调用例子
public static void main(String[] args)
{
Bank bank = new Bank();//创建一个缓冲区对象
ProductorThread th1 = new ProductorThread(bank);//创建生产者线程1
th1.start();
ProductorThread th2 = new ProductorThread(bank);//创建生产者线程2
th2.start();
CustomerThread th3 = new CustomerThread(bank);//创建消费者线程1
th3.start();
}
5、ReentrantLock结合Condition接口,实现线程间的等待通知机制
class CustomerThread extends Thread{
Bank bank;
public CustomerThread(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
while(true){
bank.get(1);
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class ProductorThread extends Thread{
Bank bank;
public ProductorThread(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
while(true){
bank.put(1);
try {
sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Bank{
private ReentrantLock lock = new ReentrantLock(); // 创建可重入锁
private Condition notEmpty = lock.newCondition(); // 创建非空条件变量
private Condition notFullCondition = lock.newCondition(); // 创建非满条件变量
private int account = 100;//临界区数据
public int getAccount(){
return account;
}
public void put(int money){
lock.lock();
try {
if(getAccount() >= 120){ //当数量已满时,等待非满条件
notFullCondition.await();
}
//进行生产
account+=money;
System.out.println(Thread.currentThread().getId()+"存入"+money+" 当前余额:"+getAccount());
notEmpty.signal();// 非空条件释放信号
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
public void get(int money){
lock.lock();
try {
if(getAccount() <= 0){ //当数量为空时,等待非空条件
notEmpty.await();
}
// 进行消费
account-=money;
System.out.println(Thread.currentThread().getId()+"取出"+money+" 当前余额:"+getAccount());
notFullCondition.signal();// 非满条件释放信号
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
}
public static void main(String[] args)
{
Bank bank = new Bank();
ProductorThread th1 = new ProductorThread(bank);
th1.start();
CustomerThread th3 = new CustomerThread(bank);
th3.start();
}