加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

Java多线程同步器代码详解

发布时间:2020-05-23 15:33:40 所属栏目:Java 来源:互联网
导读:同步器为每种特定的同步问题提供了解决方案,同步器是一些使线程能够等待另一个线程的对象,允许它们协调动作。最常用的同步器是CountDownLatch和Semaphore,不常用的是Barrier和Exchanger

同步器

为每种特定的同步问题提供了解决方案,同步器是一些使线程能够等待另一个线程的对象,允许它们协调动作。最常用的同步器是CountDownLatch和Semaphore,不常用的是Barrier 和Exchanger

Semaphore

Semaphore【信号标;旗语】,通过计数器控制对共享资源的访问。

测试类:

package concurrent;
import concurrent.thread.SemaphoreThread;
import java.util.concurrent.Semaphore;
/**
  * 拿客
  * www.coderknock.com
  * QQ群:213732117
  * 创建时间:2016年08月08日
  * 描述:
  */
public class SemaphoreTest {
	public static void main(String[] args) {
		//在Thread里声明并不是同一个对象
		Semaphore semaphore = new Semaphore(3);
		SemaphoreThread testA = new SemaphoreThread("A",semaphore);
		SemaphoreThread testB = new SemaphoreThread("B",semaphore);
		SemaphoreThread testC = new SemaphoreThread("C",semaphore);
		SemaphoreThread testD = new SemaphoreThread("D",semaphore);
		SemaphoreThread testE = new SemaphoreThread("E",semaphore);
		SemaphoreThread testF = new SemaphoreThread("F",semaphore);
		SemaphoreThread testG = new SemaphoreThread("G",semaphore);
		testA.start();
		testB.start();
		testC.start();
		testD.start();
		testE.start();
		testF.start();
		testG.start();
	}
}

线程写法:

package concurrent.thread;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.Semaphore;
/**
  * 拿客
  * www.coderknock.com
  * QQ群:213732117
  * 创建时间:2016年08月08日
  * 描述:
  */
public class SemaphoreThread extends Thread {
	private static final Logger logger = LogManager.getLogger(SemaphoreThread.class);
	//创建有3个信号量的信号量计数器
	public Semaphore semaphore;
	public SemaphoreThread(String name,Semaphore semaphore) {
		setName(name);
		this.semaphore = semaphore;
	}
	@Override
	    public void run() {
		try {
			logger.debug(getName() + " 取号等待... " + System.currentTimeMillis());
			//取出一个信号
			semaphore.acquire();
			logger.debug(getName() + " 提供服务... " + System.currentTimeMillis());
			sleep(1000);
			logger.debug(getName() + " 完成服务... " + System.currentTimeMillis());
		}
		catch (InterruptedException e) {
			e.printStackTrace();
		}
		logger.debug(getName() + " 释放... " + System.currentTimeMillis());
		//释放一个信号
		semaphore.release();
	}
}

执行结果【以下所有输出结果中[]中为线程名称- 后为输出的内容】:

  [C] - C 取号等待... 1470642024037
  [F] - F 取号等待... 1470642024036
  [E] - E 取号等待... 1470642024036
  [B] - B 取号等待... 1470642024037
  [D] - D 取号等待... 1470642024037
  [A] - A 取号等待... 1470642023965
  [D] - D 提供服务... 1470642024039
  [C] - C 提供服务... 1470642024039
  [G] - G 取号等待... 1470642024036
  [F] - F 提供服务... 1470642024040
  [D] - D 完成服务... 1470642025039
  [C] - C 完成服务... 1470642025039
  [D] - D 释放... 1470642025040
  [F] - F 完成服务... 1470642025040
  [C] - C 释放... 1470642025041
  [B] - B 提供服务... 1470642025042
  [A] - A 提供服务... 1470642025042
  [F] - F 释放... 1470642025043
  [E] - E 提供服务... 1470642025043
  [A] - A 完成服务... 1470642026043
  [B] - B 完成服务... 1470642026043
  [B] - B 释放... 1470642026043
  [A] - A 释放... 1470642026043
  [G] - G 提供服务... 1470642026044
  [E] - E 完成服务... 1470642026045
  [E] - E 释放... 1470642026045
  [G] - G 完成服务... 1470642027045
  [G] - G 释放... 1470642027046

可以看到,当3个信号量被领取完之后,之后的线程会阻塞在领取信号的位置,当有信号量释放之后才会继续执行。

CountDownLatch

CountDownLatch【倒计时锁】,线程中调用countDownLatch.await()使进程进入阻塞状态,当达成指定次数后(通过countDownLatch.countDown())继续执行每个线程中剩余的内容。

一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

  用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次――计数无法被重置。如果需要重置计数,请考虑使用 CyclicBarrier。

测试类:

package concurrent.thread;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.CountDownLatch;

public class package concurrent;
import concurrent.thread.CountDownLatchThread;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
/**
 * 拿客
 * www.coderknock.com
 * QQ群:213732117
 * 创建时间:2016年08月08日
 * 描述:
 */
public class CountDownLatchTest {
	private static final Logger logger = LogManager.getLogger(CountDownLatchTest.class);
	public static void main(String[] args) throws InterruptedException {
		//设定当达成三个计数时触发
		CountDownLatch countDownLatch = new CountDownLatch(3);
		new CountDownLatchThread("A",countDownLatch).start();
		new CountDownLatchThread("B",countDownLatch).start();
		new CountDownLatchThread("C",countDownLatch).start();
		new CountDownLatchThread("D",countDownLatch).start();
		new CountDownLatchThread("E",countDownLatch).start();
		for (int i = 3; i > 0; i--) {
			Thread.sleep(1000);
			logger.debug(i);
			countDownLatch.countDown();
		}
	}
}

线程类:

package concurrent.thread;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.CountDownLatch;

public class CountDownLatchThread extends Thread {
	private static final Logger logger = LogManager.getLogger(CountDownLatchThread.class);
	//计数器
	private CountDownLatch countDownLatch;
	public CountDownLatchThread(String name,CountDownLatch countDownLatch) {
		setName(name);
		this.countDownLatch = countDownLatch;
	}
	@Override
	  public void run() {
		logger.debug("执行操作...");
		try {
			sleep(1000);
		}
		catch (InterruptedException e) {
			e.printStackTrace();
		}
		logger.debug("等待计数器达到标准...");
		try {
			//让线程进入阻塞状态,等待计数达成后释放
			countDownLatch.await();
			logger.debug("计数达成,继续执行...");
		}
		catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

执行结果:

 [E] - 执行操作...
 [B] - 执行操作...
 [A] - 执行操作...
 [C] - 执行操作...
 [D] - 执行操作...
 [main] DEBUG concurrent.CountDownLatchTest - 3
 [B] - 等待计数器达到标准...
 [E] - 等待计数器达到标准...
 [C] - 等待计数器达到标准...
 [D] - 等待计数器达到标准...
 [A] - 等待计数器达到标准...
 [main] DEBUG concurrent.CountDownLatchTest - 2
 [main] DEBUG concurrent.CountDownLatchTest - 1
 [E] - 计数达成,继续执行...
 [C] - 计数达成,继续执行...
 [B] - 计数达成,继续执行...
 [D] - 计数达成,继续执行...
 [A] - 计数达成,继续执行...

CyclicBarrier

CyclicBarrier【Cyclic周期,循环的 Barrier屏障,障碍】循环的等待阻塞的线程个数到达指定数量后使参与计数的线程继续执行并可执行特定线程(使用不同构造函数可以不设定到达后执行),其他线程仍处于阻塞等待再一次达成指定个数。

测试类:

package concurrent;
import concurrent.thread.CyclicBarrierThread;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierTest {
	private static final Logger logger = LogManager.getLogger(CyclicBarrierTest.class);
	public static void main(String[] args) {
		//可以使用CyclicBarrier(int parties)不设定到达后执行的内容
		CyclicBarrier cyclicBarrier = new CyclicBarrier(5,() -> {
			logger.debug("---计数到达后执行的内容----");
		}
		);
		new CyclicBarrierThread("A",cyclicBarrier).start();
		new CyclicBarrierThread("B",cyclicBarrier).start();
		new CyclicBarrierThread("C",cyclicBarrier).start();
		new CyclicBarrierThread("D",cyclicBarrier).start();
		new CyclicBarrierThread("E",cyclicBarrier).start();
		new CyclicBarrierThread("A2",cyclicBarrier).start();
		new CyclicBarrierThread("B2",cyclicBarrier).start();
		new CyclicBarrierThread("C2",cyclicBarrier).start();
		new CyclicBarrierThread("D2",cyclicBarrier).start();
		new CyclicBarrierThread("E2",cyclicBarrier).start();
		//需要注意的是,如果线程数不是上面设置的等待数量的整数倍,比如这个程序中又加了个线程,
		// 那么当达到5个数量时,只会执行达到时的五个线程的内容,
		// 剩余一个线程会出于阻塞状态导致主线程无法退出,程序无法结束
		// new CyclicBarrierThread("F",cyclicBarrier).start();//将这行注释去掉程序无法自动结束
	}
}

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读