Java多线程开发五——线程的同步

本文深入探讨了Java中的同步机制,包括同步方法与同步块的概念及其使用方式,详细解析了等待和通知方法的工作原理,并通过生产者/消费者的经典案例展示了这些机制的实际应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 
同步方法
同步方法接近于临界区的概念,同一时间只能运行一个线程,一个类中所有的同步方法同一时间内只能有一个线程在一个方法体内执行。例如:
public class Resource {
       public synchronized String get(){
              //..........
              return null;
       }
       public synchronized void put(String str){
              //..........
       }
      
}
当一个线程执行get()时,其他线程既不能执行get(),也不能执行put()。
 
同步块
同步会造成系统性能的下降,为缩小同步的范围,java提供同步块功能。
public class Resource {
       public String get(){
              //..........
              Synchronized(this){
                     //………….
}
              return null;
       }
       public void put(String str){
              //..........
Synchronized(this){
                     //………….
}
 
       }
      
}
同步块与同步方法的意义是一样的,但同步块应用范围更广,使用起来更灵活。同步块类似于锁,可以把synchronized(this)中的this当成一个锁,进入同步块上锁,离开同步块时解锁。使用同一个锁的同步块同一时间内只能有一个同步块被执行。如上面的示例,get中的同步块被执行时,其他的线程既不能进入get同步块,也不能进入put同步块。
如果在Synchronized后面使用不同的对象,则相当于使用多个锁,只有使用相同锁的同步块不能同时执行,使用不同锁得同步块可以同时执行。比如:
private static Object obj1 = new Object();
 
       private static Object obj2 = new Object();
 
       public void test1() {
              System.out.println("test1 enter");
              synchronized (obj1) {
                     for (int i = 0; i < 4; i++) {
                            System.out.println("test1 excute");
                            try {
                                   Thread.sleep(500);
                            } catch (InterruptedException e) {
                                   // TODO Auto-generated catch block
                                   e.printStackTrace();
                            }
                     }
              }
              System.out.println("test1 exit");
       }
 
       public void test2() {
              System.out.println("test2 enter");
              synchronized (obj2) {
                     for (int i = 0; i < 4; i++) {
                            System.out.println("test2 excute");
                            try {
                                   Thread.sleep(400);
                            } catch (InterruptedException e) {
                                   // TODO Auto-generated catch block
                                   e.printStackTrace();
                            }
                     }
              }
              System.out.println("test2 exit");
       }
以上代码执行结果
test1 enter
test1 excute
test2 enter
test2 excute
test2 excute
test1 excute
test2 excute
test1 excute
test2 excute
test1 excute
test2 exit
test1 exit
可以看到test1 test2是交互执行的。也就是说同时不会有两个线程在test1或是在test2中执行,但同时可以一个线程执行test1,一个线程执行test2

等待和通知方法  

JDK1.5之前,Java提供的主要线程同步技术只有等待/通知函数配合同步方法或同步块,suspendresume早就不提倡使用。
Wait()和notify()/notifyAll()是java.lang.Object类的方法,java中任一个类都是Object的子类,换句话说,Java中任一个类都提供wait()notify()/notifyAll()方法。我们来看一下等待通知函数的使用。
我们以一个经典的多线程场景“生产者/消费者”来说明如何使用等待通知函数
 
1、 等待通知函数只能应用在同步方法体或同步块内。也就是说,调用某个对象wait()notify()方法前必须获得这个对象的监视器(monitor
2、 调用某个对象的Wait()方法时,相当于把当前线程放到这个对象的等待区中(可以假想这是对象的一个Collection类型的属性,把线程对象放到这个Collection中),然后挂起这个线程,最后释放这个对象的监视器。Notify()/notifyAll()方法则刚好相反,从对象的等待区中取出线程,恢复,最后释放这个对象的监视器。Notify()notifyAll()的区别是notify从等待区中拿出一个线程并且激活,而notifyAll是从等待区中拿出所有线程并且激活。

使用同步块和等待通知方法的“生产者/消费者”示例

public class SynchronizedSample ...{
    
//模拟资源
    private List<String> resource=new ArrayList<String>();
    
volatile int i=0;
    
boolean exit = false;

    
public SynchronizedSample()...{
        Thread consumer 
= new Thread(new Consumer());
        Thread producer 
= new Thread(new Producer());
        consumer.start();
        producer.start();
    }


    
public void exit()...{
        
this.exit = true;
    }


    
public static void main(String[]args)...{
        SynchronizedSample sample 
= new SynchronizedSample();
        
try ...{
            Thread.currentThread().sleep(
2000);
        }
 catch (InterruptedException e) ...{
            
return;
        }

        sample.exit();
    }


    
public class Consumer implements Runnable ...{

        
public void run() ...{
            
while (!exit) ...{
                
synchronized (resource) ...{
                    
while (resource.isEmpty()) ...{
                        
try ...{
                            
//调用resource的等待方法
                            resource.wait();
                        }
 catch (InterruptedException e) ...{
                            
return;
                        }

                    }

                    System.out.println(
"Consumer take: "+resource.remove(0));
                    
//调用resource的通知方法
                    resource.notifyAll();
                }

            }


        }


    }


    
public class Producer implements Runnable ...{

        
public void run() ...{
            
while (!exit) ...{
                
synchronized (resource) ...{
                    
while (resource.size() >= 10...{
                        
try ...{
                            resource.wait();
                        }
 catch (InterruptedException e) ...{
                            
return;
                        }

                    }

                    String r 
= "Resource"+i++;
                    System.out.println(
"Producer put: "+r);
                    resource.add(r);
                    resource.notifyAll();
                }

            }

        }


    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值