The problem
How do I get two threads to print numbers from 1 to 100 alternately? No more nonsense, directly on the code:
Synchronized + AtomicInteger lock
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; public class StrangePrinter { private int max; private AtomicInteger status = new AtomicInteger(1); // AtomicInteger ensures visibility. Volatile public StrangePrinter(int Max) {this. Max = Max; } public static void main(String[] args) { StrangePrinter strangePrinter = new StrangePrinter(100); ExecutorService executorService = Executors.newFixedThreadPool(2); executorService.submit(strangePrinter.new MyPrinter("Print1", 0));
executorService.submit(strangePrinter.new MyPrinter("Print2", 1));
executorService.shutdown();
}
class MyPrinter implements Runnable {
private String name;
private int type; Public MyPrinter(String name, int) public MyPrinter(String name, inttype) {
this.name = name;
this.type = type;
}
@Override
public void run() {
if (type= = 1) {while(status.get() <= Max) {synchronized (strangeprinter-.class) {synchronized (strangeprinter-.class) {// Print an even numberif(status.get() <= Max && status.get() % 2 == 0) {// Prints even system.out.println (name +)"-"+ status.getAndIncrement()); }}}}else {
while(status.get() <= Max) {synchronized (strangeprinter.class) {// print oddif(status.get() <= max && status.get() % 2 ! = 0) {// Print an odd number system.out.println (name +"-" + status.getAndIncrement());
}
}
}
}
}
}
}
Copy the code
Two points to note here:
- Use AtomicInteger to ensure visibility of multithreaded data.
- Do not consider synchronized locking superfluous; without locking, thread 1 and thread 2 May not print alternately. If there is no lock, imagine that thread 1 prints an odd number and thread 2 prints the next even number
status.getAndIncrement()
When the CPU suspends thread 2 and dispatches thread 1, thread 1 prints the next odd number before thread 2 prints the even number. That’s not what they’re asking us to do. Therefore, locking is necessary to ensure that there is an atomic operation in the code block.
Use the wait and notify implementations of Object
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; public class StrangePrinter2 { Object odd = new Object(); Object even = new Object(); Private int Max; private AtomicInteger status = new AtomicInteger(1); // AtomicInteger ensures visibility. Volatile public StrangePrinter2(int Max) {this. Max = Max; } public static void main(String[] args) { StrangePrinter2 strangePrinter = new StrangePrinter2(100); ExecutorService executorService = Executors.newFixedThreadPool(2); executorService.submit(strangePrinter.new MyPrinter("Even a Printer", 0));
executorService.submit(strangePrinter.new MyPrinter("Odd Printer", 1));
executorService.shutdown();
}
class MyPrinter implements Runnable {
private String name;
private int type; Public MyPrinter(String name, int) public MyPrinter(String name, inttype) {
this.name = name;
this.type = type;
}
@Override
public void run() {
if (type= = 1) {while(status.get() <= Max) {// Prints odd numbersif(status.get() % 2 == 0) {synchronized (odd) {try {odd.wait(); } catch (InterruptedException e) { e.printStackTrace(); }}}else {
System.out.println(name + "-"+ status.getAndIncrement()); // print the odd synchronized (even) {// print the even synchronized thread even.notify(); }}}}else {
while(status.get() <= Max) {// Prints even numbersif(status.get() % 2 ! Synchronized (even) {try {even.wait(); } catch (InterruptedException e) { e.printStackTrace(); }}}else {
System.out.println(name + "-"+ status.getAndIncrement()); // Print even synchronized (odd) {// notify the odd print thread odd.notify(); } } } } } } }Copy the code
Use ReentrantLock+Condition
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; public class StrangePrinter3 { private int max; private AtomicInteger status = new AtomicInteger(1); // AtomicInteger ensures visibility. Volatile private ReentrantLock lock = new ReentrantLock(); private Condition odd = lock.newCondition(); private Condition even = lock.newCondition(); public StrangePrinter3(int max) { this.max = max; } public static void main(String[] args) { StrangePrinter3 strangePrinter = new StrangePrinter3(100); ExecutorService executorService = Executors.newFixedThreadPool(2); executorService.submit(strangePrinter.new MyPrinter("Even a Printer", 0));
executorService.submit(strangePrinter.new MyPrinter("Odd Printer", 1));
executorService.shutdown();
}
class MyPrinter implements Runnable {
private String name;
private int type; Public MyPrinter(String name, int) public MyPrinter(String name, inttype) {
this.name = name;
this.type = type;
}
@Override
public void run() {
if (type= = 1) {while(status.get() <= Max) {// Prints odd lock.lock(); try {if (status.get() % 2 == 0) {
odd.await();
}
if (status.get() <= max) {
System.out.println(name + "-"+ status.getAndIncrement()); // Print odd numbers} even.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); }}}else {
while(status.get() <= Max) {// Prints even lock.lock(); try {if(status.get() % 2 ! = 0) { even.await(); }if (status.get() <= max) {
System.out.println(name + "-"+ status.getAndIncrement()); // Prints even} odd.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } } } }Copy the code
This implementation is similar to the wait and notify mechanisms for objects.
This is implemented through flags
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; public class StrangePrinter4 { private int max; private AtomicInteger status = new AtomicInteger(1); // AtomicInteger ensures visibility. Volatile private Boolean oddFlag = is also availabletrue;
public StrangePrinter4(int max) {
this.max = max;
}
public static void main(String[] args) {
StrangePrinter4 strangePrinter = new StrangePrinter4(100);
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(strangePrinter.new MyPrinter("Even a Printer", 0));
executorService.submit(strangePrinter.new MyPrinter("Odd Printer", 1));
executorService.shutdown();
}
class MyPrinter implements Runnable {
private String name;
private int type; Public MyPrinter(String name, int) public MyPrinter(String name, inttype) {
this.name = name;
this.type = type;
}
@Override
public void run() {
if (type= = 1) {while(status.get() <= Max) {// Prints odd numbersif (oddFlag) {
System.out.println(name + "-"+ status.getAndIncrement()); // Print odd oddFlag =! oddFlag; }}}else {
while(status.get() <= Max) {// Prints even numbersif(! oddFlag) { System.out.println(name +"-"+ status.getAndIncrement()); // Print odd oddFlag =! oddFlag; } } } } } }Copy the code
This is the simplest and most efficient way to implement it, since no locks are required. It’s a little better than either of the previous implementations.