Discuss / Java / 示例代码中一个是读操作,一个是写操作为什么要加同一个锁?分开两个锁是不是效率会更高?

示例代码中一个是读操作,一个是写操作为什么要加同一个锁?分开两个锁是不是效率会更高?

Topic source

重写代码:

import java.util.LinkedList;
import java.util.Queue;
public class TaskQueue {
    Queue<String> queue=new LinkedList<>();   
    final Object addLock =new Object();   
    final Object getLock =new Object();   
    public void addTask(String s){
        synchronized (addLock){
            this.queue.add(s);       
          }
    }
    public String getTask(){
        synchronized (getLock){
            while (this.queue.isEmpty()){
                try {
                    getLock.wait();               
                   }catch (InterruptedException e){
                    System.out.println("中断线程");        
                   }
            }
            return queue.remove();        
         }
    }
}

main方法:

public static void main(String[] args) throws InterruptedException  {
    TaskQueue taskQueue=new TaskQueue();  
    List<Thread> ts=new ArrayList<>();   
    for (int i=0;i<5;i++){
        Thread t= new Thread(() -> {
            while (true){
                String s=taskQueue.getTask();          
                System.out.println("execute task: " + s);       
            }
        });       
        t.start();      
        ts.add(t);   
     }
    Thread add = new Thread(()->{
        for (int i=0;i<100;i++){
            String s="t-"+Math.random();    
            System.out.println("add task: " + s);      
            taskQueue.addTask(s);           
            try {
                Thread.sleep(100);         
            } catch (InterruptedException e) {
                e.printStackTrace();       
           }
        }
    });  
   add.start();   
   add.join();   
    for (Thread t: ts) {
        t.interrupt();  
    }
}

廖雪峰

#2 Created at ... [Delete] [Delete and Lock User]

写的过程会改变内部的数据结构,导致读的数据可能是错误的。

锁首先要保证数据的正确性。

谢谢廖大指点,有点理解了,这有点类似读锁和写锁的关系,读锁是排它锁。

时倾

#4 Created at ... [Delete] [Delete and Lock User]

读锁是共享锁吧,写锁才是排他锁

Loading...

#5 Created at ... [Delete] [Delete and Lock User]

如果读是一把锁,写是一把锁。第一次读的时候任务队列为空。你的读线程挂起了。

  getLock.wait();      

挂起几个就不一定了。因为和写线程是同时进行的。但是你写完以后还没有释放被挂起的某些读线程。

taskQueue.addTask(s);

因为读线程和写线程是同时进行的,所以你有可能,有机会能看到一些读线程获取到了数据。但是某些读线程会永远被挂起。你的程序结束不了。

execute task: t-0.08415295157300218

或者你这么操作,添加完以后唤醒所有读线程。我试了一下,报错了,因为wait()和notifyAll()操作仅支持对当前锁对象进行操作。但是我感觉这样操作应该也是合理的,不知道廖大能不能看到,给解释一下,我到这也有点晕了。

 synchronized (addLock){
            this.queue.add(s);
            getLock.notifyAll();
        }

不过你分锁以后只添加,这样做肯定是不对的。你的读线程已经被挂起了,不管了吗

 synchronized (addLock){
            this.queue.add(s);       
          }

Loading...

#6 Created at ... [Delete] [Delete and Lock User]

你的读线程被挂起,而很重要的一点就是

for (Thread t: ts) {    t.interrupt();    System.out.println(t.getState());}

interrupt方法打断线程之后,该线程并不会停下来,而是继续运行,如果是wait状态和sleep状态则会抛出异常。

所以你在这打印一下你的读线程,是全部waitting的。程序不会结束,jvm一直在运行


  • 1

Reply