代码之家  ›  专栏  ›  技术社区  ›  stonar96

这个Java线程锁定概念不好吗?

  •  1
  • stonar96  · 技术社区  · 6 年前

    public class Main {
        public static void main(String[] args) {
            Data data = new Data();
    
            while (true) {
                doStuff();
                doStuff();
    
                for (int i = 0; i < 256; i++) {
                    System.out.println("Data " + i + ": " + data.get(i));
                }
    
                doStuff();
                doStuff();
    
                for (int i = 0; i < 256; i++) {
                    data.set(i, (byte) (data.get(i) + 1));
                }
    
                doStuff();
                doStuff();
            }
        }
    
        public static void doStuff() {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public class Data {
        private final byte[] data = new byte[256];
    
        public byte get(int i) {
            return data[i];
        }
    
        public void set(int i, byte data) {
            this.data[i] = data;
        }
    }
    

    重要的是只有主线程修改 data . 现在我要做一个循环 数据 异步。

        public static void main(String[] args) {
            ExecutorService executorService = Executors.newSingleThreadExecutor();
            Data data = new Data();
    
            while (true) {
                doStuff();
                doStuff();
    
                executorService.submit(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 256; i++) {
                            System.out.println("Data " + i + ": " + data.get(i));
                        }
                    }
                });
    
                doStuff();
                doStuff();
    
                for (int i = 0; i < 256; i++) {
                    data.set(i, (byte) (data.get(i) + 1));
                }
    
                doStuff();
                doStuff();
            }
        }
    

    在将任务提交给 executorService 数据 应在提交时打印。

    我知道在这种情况下我可以创建 数据

    这是我为这个问题想出的解决办法。

        public static void main(String[] args) {
            ExecutorService executorService = Executors.newSingleThreadExecutor();
            Data data = new Data();
            Lock lock = new Lock(); // <---------------
    
            while (true) {
                doStuff();
                doStuff();
    
                lock.lock(); // <---------------
                executorService.submit(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 256; i++) {
                            System.out.println("Data " + i + ": " + data.get(i));
                        }
    
                        lock.unlock(); // <---------------
                    }
                });
    
                doStuff();
                doStuff();
    
                lock.waitUntilUnlock(); // <---------------
                for (int i = 0; i < 256; i++) {
                    data.set(i, (byte) (data.get(i) + 1));
                }
    
                doStuff();
                doStuff();
            }
        }
    
    public class Lock {
        private final AtomicInteger lockCount = new AtomicInteger();
    
        public void lock() {
            lockCount.incrementAndGet();
        }
    
        public synchronized void unlock() {
            lockCount.decrementAndGet();
            notifyAll();
        }
    
        public synchronized void waitUntilUnlock() {
            while (lockCount.get() > 0) {
                try {
                    wait();
                } catch (InterruptedException e) {
    
                }
            }
        }
    }
    

    数据 数据 .

    请注意 ReentrantLock 在这种情况下行不通。我必须在提交主线程之前锁定并释放执行器线程上的锁。

    1 回复  |  直到 6 年前
        1
  •  3
  •   JB Nizet    6 年前

    Java具有更高层次的同步抽象。一般来说,您应该真正避免wait()和notifyAll(),因为它们太低级和复杂,无法正确使用和读取。

    在这种情况下,您可以使用共享阻塞队列(a synchronous queue 在两个线程之间:

        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Data data = new Data();
        SynchronousQueue queue = new SynchronousQueue();
    
        while (true) {
            doStuff();
            doStuff();
    
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 256; i++) {
                        System.out.println("Data " + i + ": " + data.get(i));
                    }
                    queue.put(data);
                }
            });
    
            doStuff();
            doStuff();
    
            data = queue.take();
            for (int i = 0; i < 256; i++) {
                data.set(i, (byte) (data.get(i) + 1));
            }
    
            doStuff();
            doStuff();
        }
    
        2
  •  1
  •   Nathan Hughes    6 年前

    您希望这个数据对象是异步构建的,而主线程希望能够继续到某个点,然后需要获取完成的对象。这就是未来的意义,它给你一个可能还没有完成的计算的参考。

    Callable<Integer> task = () -> {
        Data data = new Data();
        for (int i = 0; i < 256; i++) {
            System.out.println("Data " + i + ": " + data.get(i));
        }
        return data;
    };
    
    ExecutorService executor = Executors.newFixedThreadPool(1);
    Future<Data> future = executor.submit(task);
    doStuff();
    // ... main thread goes about its business
    
    // when you get to a point where you need data, 
    // you can block here until the computation is done
    Data data = future.get(); 
    

        3
  •  0
  •   stonar96    6 年前

    我找到了解决这个问题的另一种方法,它也回答了我对@JB niset的回答的意见。 ExecutorService#submit(Runnable) 返回 Future<?> Queue<Future<?>> queue ,始终提供 未来<?> 返回者 queue 在主线程应该等待的地方 #poll().get() 整个 .

    https://stackoverflow.com/a/20496115/3882565

        4
  •  -1
  •   Thomas Bitonti    6 年前

    doStuff 期望对数据有唯一的访问权,这样它就可以安全地修改整个数据。同时, doPrint

    public void runningInThread1() {
        Data someData = getData(); // Obtain the data which this thread is using
        doStuff(someData); // Update the data
        // Looping and such omitted.
    }
    
    public void runningInThread2() {
        Data someData = getData();
        doPrint(someData); // Display the data
    }
    
    public void doStuff(Data data) {
        synchronized ( data ) {
            // Do some stuff to the data
        }
    }
    
    public void doPrint(Data data) {
        synchronized ( data ) {
            // Display the data
        }
    }
    

    火山灰 多普林特 实现为 Data ,则可以通过添加 synchronized 实例方法的关键字。