The biggest problem here is that you change which object is synchronized. What ends is truly determinate.
In your synchronized block, you:
synchronized (this.last) {
last.next = newLink;
last = newLink;
}
As the latter changes, another thread can enter the enque method and simultaneously enter a synchronized block. It is best to have two objects to block:
private final Object ENQUEUE_LOCK = new Object();
private final Object DEQUEUE_LOCK = new Object();
, , , concurrency.
: , , , , .