Синхронизация на разных объектах

Синхронизация на разных объектах

Когда synchronized-метод блокирует объект класса, в котором этот метод определён, никакой другой поток не может получить доступ к этому объекту.
Если в объекте определены методы, выполняющие несвязанные друг с другом задачи, то в случае захвата монитора одним потоком, второй поток всё равно будет вынужден ждать его высвобождения.


class Post {
    private int countComments;
    private int countLikes;
    private DateTimeFormatter formatter = DateTimeFormatter.ofPattern(" mm:ss:n");

    public synchronized void countCommentsIncrease() {
        printWhoBlockedMonitor();
        sleep(1000L);
        countComments++;
        System.out.println("countComments: " + countComments);
    }

    public synchronized void plusOneLike() {
        printWhoBlockedMonitor();
        sleep(20L);
        countLikes++;
        System.out.println("countLikes: " + countLikes);
    }

    private void printWhoBlockedMonitor() {
        String threadName = Thread.currentThread().getName();
        System.out.println("Monitor is blocked by " + threadName
                + LocalDateTime.now().format(formatter));
    }

    private void sleep(long delay) {
        try {
            Thread.sleep(delay);
        } catch (InterruptedException ex) {
            ex.printStackTrace(System.out);
        }
    }
}


abstract class Manager extends Thread {
    Post post;
    String name;
    long startTime;
    long endTime;

    Manager(Post post) {
        this.post = post;
    }

    @Override
    public void run() {
        name = Thread.currentThread().getName();
        startTime = System.currentTimeMillis();
        action();
        endTime = System.currentTimeMillis();
        System.out.println(name + ": Spent " + (endTime - startTime) + " ms");
    }

    abstract void action();
}


class CommentsManager extends Manager {

    CommentsManager(Post post) {
        super(post);
    }

    @Override
    void action() {
        post.countCommentsIncrease();
    }
}


class LikesManager extends Manager {

    LikesManager(Post post) {
        super(post);
    }

    @Override
    void action() {
        post.plusOneLike();
    }
}


Post post = new Post();
Thread mComments = new CommentsManager(post);
Thread mLikes = new LikesManager(post);
mComments.start();
mLikes.start();


/*
Monitor is blocked by Thread-0 52:04:225000000
countComments: 1
Monitor is blocked by Thread-1 52:05:239000000
Thread-0: Spent 1045 ms
countLikes: 1
Thread-1: Spent 1076 ms
 */
Чтобы избежать такого бесполезного простоя, синхронизацию методов можно выполнять на разных объектах. Перепишем класс Post так:


class Post {
    private int countComments;
    private int countLikes;
    private DateTimeFormatter formatter = DateTimeFormatter.ofPattern(" mm:ss:n");
    private Object ObjComments = new Object();
    private Object ObjLikes = new Object();

    public void countCommentsIncrease() {
        synchronized (ObjComments) {
            printWhoBlockedMonitor();
            sleep(1000L);
            countComments++;
            System.out.println("countComments: " + countComments);
        }
    }

    public synchronized void plusOneLike() {
        synchronized (ObjLikes) {
            printWhoBlockedMonitor();
            sleep(20L);
            countLikes++;
            System.out.println("countLikes: " + countLikes);
        }
    }

    private void printWhoBlockedMonitor() {
        /* ... */
    }

    private void sleep(long delay) {
        /* ... */
    }
}


/*
Monitor is blocked by Thread-1 54:24:954000000
Monitor is blocked by Thread-0 54:24:954000000
countLikes: 1
Thread-1: Spent 62 ms
countComments: 1
Thread-0: Spent 1045 ms
*/
Многопоточность