https://blog.csdn.net/weixin_39723337/article/details/80352783
題目:3個線程循環打印ABC,其中A打印3次,B打印2次,C打印1次,循環打印2輪
一.Synchronized同步法
思路:使用synchronized、wait、notifyAll的方法利用線程標記變量控制三個線程的執行順序。
/**
* @author XDarker
* 2018-5-17
*/
public class Main {
public static void main(String[] args) throws InterruptedException {
int num = 1;//當前正在執行線程的標記
ABCPrint print = new ABCPrint(num);
Thread threadA = new Thread(new RunnableA(print));
Thread threadB = new Thread(new RunnableB(print));
Thread threadC = new Thread(new RunnableC(print));
threadA.start();
Thread.sleep(500);
threadB.start();
Thread.sleep(500);
threadC.start();
}
}
class RunnableA implements Runnable{
private ABCPrint print;
public RunnableA(ABCPrint print) {
super();
this.print = print;
}
@Override
public void run() {
print.PrintA();
}
}
class RunnableB implements Runnable{
private ABCPrint print;
public RunnableB(ABCPrint print) {
super();
this.print = print;
}
@Override
public void run() {
print.PrintB();
}
}
class RunnableC implements Runnable{
private ABCPrint print;
public RunnableC(ABCPrint print) {
super();
this.print = print;
}
@Override
public void run() {
print.PrintC();
}
}
class ABCPrint {
private int num;//當前正在執行線程的標記
public ABCPrint(int num) {
super();
this.num = num;
}
public void PrintA(){
for (int j = 0; j < 2; j++)//表示 循環打印2輪
synchronized(this){
while(num != 1){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 0; i < 3; i++) {//表示 打印3次
System.out.println("A");
}
//打印A線程執行完 ,通知打印B線程
num = 2;
this.notifyAll();
}
}
public void PrintB(){
for (int j = 0; j < 2; j++)//表示 循環打印2輪
synchronized(this){
while(num != 2){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 0; i < 2; i++) {//表示 打印2次
System.out.println("B");
}
//打印B線程執行完 ,通知打印C線程
num = 3;
this.notifyAll();
}
}
public void PrintC(){
for (int j = 0; j < 2; j++)//表示 循環打印2輪
synchronized(this){
while(num != 3){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("C");
//打印C線程執行完 ,通知打印A線程
num = 1;
this.notifyAll();
}
}
}
二.Lock鎖方法
思路:Lock鎖機制是JDK 5之后新增的鎖機制,不同於內置鎖,Lock鎖必須顯式聲明,並在合適的位置釋放鎖。Lock是一個接口,通過ReentrantLock具體實現進行顯式的鎖操作,即獲取鎖和釋放鎖。
/**
* @author XDarker
* 2018-5-17
*/
public class Main {
public static void main(String[] args) throws InterruptedException {
int num = 1;//當前正在執行線程的標記
ABCPrint print = new ABCPrint(num);
Thread threadA = new Thread(new RunnableA(print));
Thread threadB = new Thread(new RunnableB(print));
Thread threadC = new Thread(new RunnableC(print));
threadA.start();
Thread.sleep(500);
threadB.start();
Thread.sleep(500);
threadC.start();
}
}
class RunnableA implements Runnable{
private ABCPrint print;
public RunnableA(ABCPrint print) {
super();
this.print = print;
}
@Override
public void run() {
print.PrintA();
}
}
class RunnableB implements Runnable{
private ABCPrint print;
public RunnableB(ABCPrint print) {
super();
this.print = print;
}
@Override
public void run() {
print.PrintB();
}
}
class RunnableC implements Runnable{
private ABCPrint print;
public RunnableC(ABCPrint print) {
super();
this.print = print;
}
@Override
public void run() {
print.PrintC();
}
}
class ABCPrint {
private static final Lock lock = new ReentrantLock();//通過JDK5中的Lock鎖來保證線程的訪問的互斥
private int num;//當前正在執行線程的標記
public ABCPrint(int num) {
super();
this.num = num;
}
public void PrintA(){
for (int j = 0; j < 2;)//表示 循環打印2輪
try {
lock.lock();
while(num == 1){
for (int i = 0; i < 3; i++) {//表示 打印3次
System.out.println("A");
}
//打印A線程執行完 ,通知打印B線程
num = 2;
j++;
}
}finally{//調用了lock方法后,需在finally(finally確保一定會執行,除非執行了exit方法)語句里調用unlock方法。否則會造成死鎖等問題
lock.unlock();
}
}
public void PrintB(){
for (int j = 0; j < 2;)//表示 循環打印2輪
try{
lock.lock();
while(num == 2){
for (int i = 0; i < 2; i++) {//表示 打印2次
System.out.println("B");
}
//打印B線程執行完 ,通知打印C線程
num = 3;
j++;
}finally{
lock.unlock();
}
}
public void PrintC(){
for (int j = 0; j < 2;)//表示 循環打印2輪
try{
lock.lock();
while(num == 3){
System.out.println("C");
//打印C線程執行完 ,通知打印A線程
num = 1;
j++;
}
}finally{
lock.unlock();
}
}
}
三.ReentrantLock結合Condition
思路:Condition中的await()方法相當於Object的wait()方法,Condition中的signal()方法相當於Object的notify()方法。Condition它更強大的地方在於:能夠更加精細的控制多線程的休眠與喚醒。對於同一個鎖,我們可以創建多個Condition,在不同的情況下使用不同的Condition。
/**
* @author XDarker
* 2018-5-17
*/
public class Main{
public static void main(String[] args) {
final AlternateDemo ad = new AlternateDemo();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 2; i++) {
ad.loopA(i);
}
}
},"A").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 2; i++) {
ad.loopB(i);
}
}
},"B").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 2; i++) {
ad.loopC(i);
}
}
},"C").start();
}
}
class AlternateDemo{
private int num = 1;//當前正在執行線程的標記
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
public void loopA(int loop){
lock.lock();
try {
//1.判斷
if(num != 1){
condition1.await();
}
//2.打印
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName()+"-"+"第"+loop+"輪"+"-第"+i+"次");
}
//3.喚醒
num = 2;
condition2.signal();
} catch (Exception e) {
// TODO: handle exception
}finally{
lock.unlock();
}
}
public void loopB(int loop){
lock.lock();
try {
//1.判斷
if(num != 2){
condition2.await();
}
//2.打印
for (int i = 0; i < 2; i++) {
System.out.println(Thread.currentThread().getName()+"-"+"第"+loop+"輪"+"-第"+i+"次");
}
//3.喚醒
num = 3;
condition3.signal();
} catch (Exception e) {
// TODO: handle exception
}finally{
lock.unlock();
}
}
public void loopC(int loop){
lock.lock();
try {
//1.判斷
if(num != 3){
condition3.await();
}
//2.打印
for (int i = 0; i < 1; i++) {
System.out.println(Thread.currentThread().getName()+"-"+"第"+loop+"輪"+"-第"+i+"次");
}
System.out.println("---------------------------");
//3.喚醒
num = 1;
condition1.signal();
} catch (Exception e) {
// TODO: handle exception
}finally{
lock.unlock();
}
}
}
四.AtomicInteger方法
思路:AtomicInteger,一個提供原子操作的Integer的類。在Java中,++i和i++操作並不是線程安全的,在使用的時候,不可避免的會用到synchronized關鍵字。而AtomicInteger則通過一種線程安全的加減操作接口。
/**
* @author XDarker
* 2018-5-17
*/
public class Main {
public static void main(String[] args) {
new ABCPrint("A",3).start();//A打印3次
new ABCPrint("B",2).start();//B打印2次
new ABCPrint("C",1).start();//C打印1次
}
}
class ABCPrint extends Thread {
//打印次數
private int count;
private final String str[] = { "A", "B", "C" };
private final static AtomicInteger atomCount= new AtomicInteger();
public ABCPrint(String name,int count) {
this.setName(name);
this.count = count;
}
@Override
public void run() {
while (true) {
// 循環滿2輪退出打印
if (atomCount.get() / 3 == 2) {
break;
}
synchronized (atomCount) {
// 順序打印A、B、C
if (str[atomCount.get() % 3].equals(getName())) {
atomCount.getAndIncrement();//自增
//對應打印幾次
for (int i = 0; i < count; i++) {
System.out.println(getName());
}
//表示一輪打印結束 方便觀察打印下分隔符
if ("C".equals(getName())) {
System.out.println("================================");
}
// 當前線程打印打印完成后喚醒其它線程
atomCount.notifyAll();
} else {
// 非順序線程wait()
try {
atomCount.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
五.Semaphore信號量方式
思路: 信號量(Semaphore),有時被稱為信號燈,是在多線程環境下使用的一種設施, 它負責協調各個線程, 以保證它們能夠正確、合理的使用公共資源。Semaphore線程同步機制,當調用acquire()時,內部計數器數值增加;調用release()時,內部計數器遞減;計數器值不能小於0,如果等於0,acquire()方法被阻塞,需要等待其他線程調用release()方法。
/**
* @author XDarker
* 2018-5-17
*/
public class Main {
public static void main(String[] args) throws InterruptedException {
new ThreadA(3).start();
new ThreadB(2).start();
new ThreadC(1).start();
}
//以A開始的信號量,初始信號量數量為1
private static Semaphore A = new Semaphore(1);
//B、C信號量,A完成后開始,初始信號數量為0
private static Semaphore B = new Semaphore(0);
private static Semaphore C = new Semaphore(0);
static class ThreadA extends Thread {
private int count;
public ThreadA(int count) {
super();
this.count = count;
}
@Override
public void run() {
try {
for (int i = 0; i < 2; i++) {
A.acquire();// A獲取信號執行,A信號量減1,當A為0時將無法繼續獲得該信號量
for (int j = 0; j < count; j++) {
System.out.print("A");
}
B.release();// B釋放信號,B信號量加1(初始為0),此時可以獲取B信號量
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class ThreadB extends Thread {
private int count;
public ThreadB(int count) {
super();
this.count = count;
}
@Override
public void run() {
try {
for (int i = 0; i < 2; i++) {
B.acquire();
for (int j = 0; j < count; j++) {
System.out.print("B");
}
C.release();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class ThreadC extends Thread {
private int count;
public ThreadC(int count) {
super();
this.count = count;
}
@Override
public void run() {
try {
for (int i = 0; i < 2; i++) {
C.acquire();
for (int j = 0; j < count; j++) {
System.out.println("C");
}
A.release();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
---------------------
作者:XDarker
來源:CSDN
原文:https://blog.csdn.net/weixin_39723337/article/details/80352783
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!