线程之间的通讯:
多线程的执行顺序是由cpu决定的,但是有时候需要线程按照预期的顺序执行,就需要线程之间的通讯
线程通讯的方式有很多种:
1. 休眠唤醒:
Object:
- wait() :
导致当前线程等待- notify() :
唤醒正在等待对象监视器的单个线程。 (唤醒当前线程)- notifyAll() :
唤醒正在等待对象监视器的所有线程。(唤醒现在的所有线程)
Object休眠唤醒必须与synchronized搭配用,wait()必须由notify()来唤醒
Condition:
- await()
导致当前线程等到发信号或 interrupted- signal()
唤醒一个等待线程- signalAll()
唤醒所有等待线程Condition休眠唤醒必须与lock锁搭配使用。并且由lock . newCondition() 来创建Condition, await()必须由signal()来唤醒
2. CountDownLatch:
允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助。()
- CountDownLatch(int count) :
指定一个计数- countDown() :
减少锁存器的计数,如果计数达到零,释放所有等待的线程。- await():
导致当前线程等到锁存器计数到零,除非线程是 interrupted (中断)。3. CyclicBarrier:
几个线程互相等待,每条线程到达特定的屏障点就停止,只有所有的线程都到达屏障点后,所有的线程再同时启动
- await() :
等待所有 parties已经在这个障碍上调用了 await 。4. Semaphore:
利用Semaphore可以设置某种临界资源有几个
- Semaphore(int permits) :
创建一个 Semaphore与给定数量的许可证和非公平公平设置。(设置数量,超过这个数量时,后面来的线程就获得不到了,这里默认用非公平机制将接下来释放的一个临界资源给一个等待的线程)- Semaphore(int permits, boolean fair) :
创建一个 Semaphore与给定数量的许可证和给定的公平设置。(前面的参数和上面那个构造函数里的参数意思一样,后面的参数则意为指定一个线程最先可以获得释放后的临界资源)- acquire() :
从该信号量获取许可证,阻止直到可用,或线程为 interrupted (中断)。- release():
释放许可证,将其返回到信号量。
第一种方法(休眠唤醒):
Object:
import java.util.Date;
public class Comm {
private int ticket=10;
private Object object=new Object();
public void show1(){
synchronized (object){
while(ticket>0){
if(ticket%2==1){
object.notify();
try{
Thread.sleep(0);
}
catch (Exception e){
e.printStackTrace();
}
finally {
System.out.println(Thread.currentThread().getName()+"线程"+"进行线程打印票:\t"+ticket--+"时间:"+new Date().getTime());
}
}
else {
try{
object.wait();
}catch (Exception e)
{
e.printStackTrace();
}
finally {
System.out.println("奇数打印线程被暂停,等待。。。");
}
}
}
}
}
public void show2(){
synchronized (object){
while(ticket>0){
if(ticket%2==0){
object.notify();
try{
Thread.sleep(0);
}catch (Exception e){
e.printStackTrace();
}
finally {
System.out.println(Thread.currentThread().getName()+"线程"+"进行线程打印票:\t"+ticket--+"时间:"+new Date().getTime());
}
}
else {
try{
object.wait();
}catch (Exception e)
{
e.printStackTrace();
}
finally {
System.out.println("偶数打印线程被暂停,等待。。。");
}
}
}
}
}
}
主函数:
import com.one.CommObject;
public class OneObjectTest {
public static void main(String[] args) {
CommObject comm=new CommObject();
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
comm.show1();
}
},"奇数");
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
comm.show2();
}
},"偶数");
thread.start();
thread1.start();
}
}
Condition:
import java.util.Date;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Comm {
private int ticket=10;
//private Object object=new Object();
private Lock lock=new ReentrantLock();
//需要借助lock来创建Condition
private Condition condition= lock.newCondition();
public void show1(){
while(ticket>0){
lock.lock();
try{
if(ticket%2==1){
condition.signal();
try{
Thread.sleep(0);
}
catch (Exception e){
e.printStackTrace();
}
finally {
System.out.println(Thread.currentThread().getName()+"线程"+"进行线程打印票:\t"+ticket--+"时间:"+new Date().getTime());
}
}
else {
try{
condition.await();
}catch (Exception e)
{
e.printStackTrace();
}
finally {
System.out.println("奇数打印线程被暂停,等待。。。");
}
}
}finally {
lock.unlock();
}
}
}
public void show2(){
while(ticket>0){lock.lock(); try{
if(ticket%2==0){
condition.signal();
try{
Thread.sleep(0);
}catch (Exception e){
e.printStackTrace();
}
finally {
System.out.println(Thread.currentThread().getName()+"线程"+"进行线程打印票:\t"+ticket--+"时间:"+new Date().getTime());
}
}
else {
try{
condition.await();
}catch (Exception e)
{
e.printStackTrace();
}
finally {
System.out.println("偶数打印线程被暂停,等待。。。");
}
}
}finally {
lock.unlock();
}
}
}
}
主函数:
import com.one.CommCondition;
public class OneConditionTest {
public static void main(String[] args) {
CommCondition comm=new CommCondition();
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
comm.show1();
}
},"奇数");
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
comm.show2();
}
},"偶数");
thread.start();
thread1.start();
}
}
第二种方法:
CountDownLatch
利用CountDownLatch计数,先在类中创建一个静态变量,因为这个计数需要几个方法能够同步他的数字,所以变量需要唯一存在
创建一个类,存在两个员工方法,其中添加countDown()方法用来减去对象实例化时赋予的初值,存在一个老板方法,其中添加await() 方法用来当计数为 0 时进行唤醒线程:
import java.util.concurrent.CountDownLatch;
public class CommCountDownLatch {
private static CountDownLatch countDownLatch=new CountDownLatch(2);
public void show1(){
String name=Thread.currentThread().getName();
System.out.println(name+"线程正在编辑。。。");
try{
Thread.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(name+"编辑完成---");
countDownLatch.countDown();
}
public void show2(){
String name=Thread.currentThread().getName();
System.out.println(name+"线程正在编辑。。。");
try{
Thread.sleep(500);
}catch (Exception e){
e.printStackTrace();
}
System.out.println(name+"编辑完成");
countDownLatch.countDown();
}
public void Boss() {
String name =Thread.currentThread().getName();
System.out.println("正在等待编辑完成。。。由"+name+"线程整合");
try{
countDownLatch.await();
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("编辑完成==="+name+"开始整合");
}
}
主函数:
import com.one.CommCountDownLatch;
public class OneCountDownLatch {
public static void main(String[] args) {
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
new CommCountDownLatch().show1();
}
},"员工1号");
Thread thread2=new Thread(new Runnable() {
@Override
public void run() {
new CommCountDownLatch().show2();
}
},"员工2号");
Thread thread3=new Thread(new Runnable() {
@Override
public void run() {
new CommCountDownLatch().Boss();
}
},"老板");
thread1.start();
thread2.start();
thread3.start();
}
}
第三种方法:
CyclicBarrier
创建一个方法,因为CyclicBarrier,所以需要等线程运行到参数2次以后才可以让所有线程继续运行,这里主函数启用了两个线程,一个执行一次,并且分别阻塞,第三次运行就打破了屏障点,然后会同时启动所有的阻塞线程
import java.util.Date;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CommCyclicBarrier {
private static CyclicBarrier cyclicBarrier=new CyclicBarrier(2);
public void show() {
System.out.println("准备线程"+Thread.currentThread().getName());
try{
cyclicBarrier.await();
Thread.sleep(1000);
}catch (BrokenBarrierException e){
e.printStackTrace();
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("准备完成。。"+new Date().getTime());
}
}
主函数;
import com.one.CommCyclicBarrier;
public class OneCyclicBarrier {
public static void main(String[] args) {
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
new CommCyclicBarrier().show();
}
});
Thread thread2=new Thread(new Runnable() {
@Override
public void run() {
new CommCyclicBarrier().show();
}
}); thread1.start();
thread2.start();
}
}
第四种方法:
Semaphore
import java.util.Date;
import java.util.concurrent.Semaphore;
public class CommSemaphore {
private Semaphore semaphore;
public void Machine(Semaphore semaphore){
this.semaphore=semaphore;
try{
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"线程获得机器。。。"+new Date().getTime());
Thread.sleep(500);
semaphore.release();
System.out.println(Thread.currentThread().getName()+"线程++释放++机器"+new Date().getTime());
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"完成工作==="+new Date().getTime());
}
}
System.out.println(Thread.currentThread().getName()+"完成工作==="+new Date().getTime());
}
}
主方法:
设置五个人,因为要让五个人使用这 四 个临界资源,所以需要创建五个线程来模拟这五个人,所以这里需要 for 利用循环,循环创建五个线程,每创建一个线程,就去获得一个临界资源,然后再睡眠500毫秒(借助上面的代码),当四个临界资源都分配出去时,第五个创建的线程因为得不到临界资源就等待,直到下一个临界资源释放,再让等待的线程去获得它
import com.one.CommSemaphore;
import java.util.concurrent.Semaphore;
public class OneSemaphoreTest {
private static int works=5;
static Semaphore semaphore=new Semaphore(4);
public static void main(String[] args) {
for (int i=0;i<works;i++){
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
new CommSemaphore().Machine(semaphore);
}
});
thread.start();
}
}
}
结果:
0 ,1 ,2 ,4线程先获得临界资源,等到这四个中的任意一个资源释放,就给等待的线程分配(不需要等到所有的资源都是释放)
Thread-0线程获得机器。。。1642343042402
Thread-2线程获得机器。。。1642343042402
Thread-4线程获得机器。。。1642343042402
Thread-1线程获得机器。。。1642343042402
Thread-1线程++释放++机器1642343042903
Thread-0线程++释放++机器1642343042903
Thread-0完成工作===1642343042903
Thread-4线程++释放++机器1642343042903
Thread-3线程获得机器。。。1642343042903
Thread-2线程++释放++机器1642343042903
Thread-4完成工作===1642343042903
Thread-1完成工作===1642343042903
Thread-2完成工作===1642343042903
Thread-3线程++释放++机器1642343043403
Thread-3完成工作===1642343043403
Process finished with exit code 0