Java Multithreading -> Access Shared Resources

 synchronized

The solution to thread conflicts is basically the same: serialized access to shared resources . That is, when multiple threads access the shared resource at the same time, the shared resource is locked and accessed. The synchronized keyword is provided in Java. 
In java, all objects automatically hold a single lock . That is to say, when a task calls the f() method identified by synchronized on an object, other tasks can no longer call the g() method identified by synchronized on the object, but this task can be embedded in f(). Set of calls to the g() method. The implementation principle is as follows: a task can obtain the lock of an object multiple times (the original words of "Thinking in java" Chinese Fourth Edition page 677), that is, if an object is unlocked (the lock is completely released), its count becomes 0; the counter becomes 1 when a task locks an object for the first time, and increments each time the same task acquires a lock on this object (nested calls); only the task that acquired the lock first The lock can continue to be acquired (because the count is not 0); every time the task leaves a synchronized method, the count is decremented. When the count is 0, the lock is completely released, and other tasks can obtain the lock. The JVM is responsible for keeping track of how many times an object is locked
In java, there is also a lock for each class (as part of the class's class object). In this way, synchronized static methods prevent concurrent access to static data within the scope of the class. That is to say, if a task calls the f() method identified by synchronized static, other tasks can no longer call the g() method identified by synchronized static. 
Every method that accesses a critical resource must be synchronized
Here is an example of using the synchronized keyword to resolve the concurrency error in the previous blog post

package org.fan.learn.thread.share;
public class SynchronizedEvenGenerator extends IntGenerator {
    private int evenValue = 0;
    @Override
    public synchronized int next() {
        ++evenValue;
        Thread.yield();
        ++evenValue;
        return evenValue;
    }
    public static void main(String[] args) {
        EvenChecker.test(new SynchronizedEvenGenerator());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

这个即使调用了yield方法,也不会产生任何并发错误。

lock

lock看上去很直接,就是加锁。《Thinking in java》说,lock与内建的锁形式相比,代码缺乏优越性,但是对某些具体问题,lock更加灵活。 
看个例子:

package org.fan.learn.thread.share;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockEvenGenerator extends IntGenerator {
    private int evenValue = 0;
    Lock lock = new ReentrantLock();
    @Override
    public int next() {
        lock.lock();
        try {
            ++evenValue;
            Thread.yield();
            ++evenValue;
            return evenValue;
        } finally {
            lock.unlock();
        }
    }
    public static void main(String[] args) {
        EvenChecker.test(new LockEvenGenerator());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

注意使用lock方式来同步,最好使用上面的写法来写,特别是return语句的位置。return语句必须在try子句中出现,以确保unlock不会过早放生,从而将数据暴露给了第二个任务。 
lock方式还可以使用trylock方式,如果没有获得锁,不会一直阻塞,而是直接返回。 
还可以给trylock传递一个时间,表示在这个时间段内如果没有获取锁会直接返回。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325151457&siteId=291194637