Java多线程

本文深入探讨Java多线程的基本概念和技术,包括线程的创建、生命周期、安全与同步、线程间的通信等内容。通过具体实例讲解如何解决生产者消费者问题、使用线程池、信号灯、同步屏障等高级特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

*进程和线程

进程:一个正在进行的程序,比如你电脑中的word、QQ等

线程:简单的理解就是一个进程中的执行事件,比如Eclipse创建一个HelloWorld,main函数就是一个线程,main函数中的代码是顺序执行,那么如果我们想在运行main函数的时候想让他再执行另一段代码的话,那么这就是多线程。

进程中必须至少有一个线程

多个线程之间的执行是独立的互不相干

*线程的创建

在说线程的创建之前我们先说下线程的生命周期


首先在创建一个线程之后他的状态是新生的,调用他的start方法之后改线程状态变为可运行,这边注意start方法调用之后并不是直接进入运行状态,虚拟机在众多可运行状态的线程中挑选一个进入运行状态,到这边线程中执行的事件才开始运行。但是一个线程并不能独占虚拟机,他可能在运行到某个时候进入睡眠/等待/阻塞状态,那么虚拟机将在重新挑选一个线程进入可运行状态,一个线程进入睡眠或者等待状态之后,睡眠时间到或者等待被唤醒之后就会变成可运行状态。当一个线程中所有的事件都执行完毕之后,线程就进入死亡状态。上述差不多就是一个线程的生命周期。


*线程的创建

线程的创建有三种方式:
1、继承Thread类重写run方法
2、实现Runnable接口实现run方法
3、线程池方式创建线程(后续做解释)

继承Thread类重新run方法

public static void main(String[] args) {
		// TODO Auto-generated method stub
		new Thread(){
			public void run() {
				for(int i =0;i<20;i++){
					System.out.println(Thread.currentThread().getName()+"  "+i);
				}
				
			};
		}.start();
		 
	}



实现Runnable接口实现run方法

public static void main(String[] args) {
		// TODO Auto-generated method stub
		 
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				for(int i =0;i<20;i++){
					System.out.println(Thread.currentThread().getName()+"  "+i);
				}
			}
		}).start();

	}

写在同一个类中运行


两个线程交替运行互不影响,并且每次运行结果不一样。
扩展:如果一个线程又继承了Thread又实现了Runnable接口的话他会去执行谁的run呢
public static void main(String[] args) {
		// TODO Auto-generated method stub
		 
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				for(int j =10;j<20;j++){
					System.out.println(Thread.currentThread().getName()+"  "+j);
				}
			}
		}){
			public void run() {
				for(int i =0;i<10;i++){
					System.out.println(Thread.currentThread().getName()+"  "+i);
				}
				
			};
		}.start();

看下输出:

显然他实现的是重写父类的run。进入Thread的源码可以看见

如果有Runnable的话执行Runnable的run但是这个方法被重写了自然是执行重写之后的方法。
Thread的常用方法
1.Thread.currentThread().toString(); //输出当前线程信息
输出信息为[线程名称,线程优先级,线程所在组]
2.t0.setPriority(10);//设置线程优先级 最小为0 最大为10 默认为5
设置线程的优先级为10并不是说明他一定比优先级为9的更早运行,只是说他比优先级低的线程更有机会获得执行权

3.t0.yield();  //线程让步,当前线程放弃执行权,有CPU在重新选取一个线程开始运行,有可能还是该线程获取执行权

4.join();  //加入当前线程;比如在线程B中调用线程A的join(),那么线程A运行结束之后才会开始运行线程B


*线程的安全与同步

当多个线程操作同一个数据并且操作的时间较长的时候就容易出现数据错误的问题,看下代码

public class Blog1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
           data d=new data();
           Thread t1=new Thread(d);
           Thread t2=new Thread(d);
           Thread t3=new Thread(d);
           Thread t4=new Thread(d);
           t1.start();
           t2.start();
           t3.start();
           t4.start();
	}

}
class data implements Runnable {
	
	private  int num=50;

	@Override
	public void run() {
		// TODO Auto-generated method stub
		
		while(true){
			 
  
			if(num>0){
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("当前线程为:"+Thread.currentThread().getName()+"操作时数据为:"+num);
				num--;
			}
			 
			
		}
		
		
	}
	
	
}

开启4个线程对data类中的num变量进行操作,一直做减法,在判断是否大于0之后让线程睡眠100毫秒模拟操作一个很长的事件,看下输出
Thread-3和Thread-0操作时数据都为2并且最后还出现了负数的情况,那是因为当多个线程下if语句里面进行睡眠等待,当执行线程执行num--之后,等待的线程醒了,继续往下执行,就出现了负数的情况。导致数据错误,这段代码原本是想当num大于0时才进行打印。

所以上述代码中应该保证if判断语句、输出语句和num--这块代码必须同步即一个线程在操作的时候其他线程不允许操作,那就要用到同步代码块

同步代码块的关键字为synchronized

格式为synchronized(对象){

需要同步的语句;

}

所以我们将上面的代码修改一下

public class Blog1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
           data d=new data();
           Thread t1=new Thread(d);
           Thread t2=new Thread(d);
           Thread t3=new Thread(d);
           Thread t4=new Thread(d);
           t1.start();
           t2.start();
           t3.start();
           t4.start();
	}

}
class data implements Runnable {
	
	private  int num=50;

	@Override
	public void run() {
		// TODO Auto-generated method stub
		
		while(true){
			synchronized(this){
			if(num>0){
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("当前线程为:"+Thread.currentThread().getName()+"操作时数据为:"+num);
				num--;
			}
			}
			
		}
		
		
	}
	
	
}

加上同步语句,看下输出

数据就不会出现0或者负数了

synchronized同步语句相当于加了一把锁,当多个线程运行到这的时候都会去判断synchronized的锁是不是被其他线程占用了,如果被其他线程占有了就等待,直到该锁被其他线程释放之后能进入同步代码块。

线程的锁就是保证线程同步的关键,synchronized可以不对象直接写成synchronized{需要被同步的代码;} 那么他的锁就默认是当前对象,将synchronized加到方法上,该方法称之为同步方法,同步方法拥有的锁也是当前对象,这种锁称对象锁;
也可以将类的字节码文件作为锁:synchronized(data.class){} 这种锁称之为类锁;

使用锁可以保证线程同步,但是一旦使用不当就会出现线程死锁的情况:当线程0获得A锁之后,执行同步代码块的时候要去获取B锁,然而线程1现在获取了B锁在执行代码块的时候要获取A锁,这样导致,0线程释放不了A锁,1线程获取不了A锁,1线程释放不了B锁,0线程获取不了B锁,两个线程僵持不下谁都不执行了。
package com.ccl.test;

public class DeadLock1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		data1 d1=new data1(true);
		data1 d2=new data1(false);
		Thread t1=new Thread(d1);
		Thread t2=new Thread(d2);
		t1.start();
		t2.start();
	}

}
 class data1 implements Runnable{
	private boolean flag;
	public data1(boolean flag){
		this.flag=flag;
		
	}
	 
	 
	@Override
	public void run() {
		// TODO Auto-generated method stub
		if(flag){
			while(true){
			synchronized(Mylock.obj1){
				System.out.println(Thread.currentThread().getName()+"  get obj1 lock");
				 
				synchronized(Mylock.obj2){
					 
					System.out.println(Thread.currentThread().getName()+" get obj2 lock");
					
				}
			}
		}
			
		}else{
			while(true){
			synchronized(Mylock.obj2){
				System.out.println(Thread.currentThread().getName()+" get obj2 lock");
				 
				synchronized(Mylock.obj1){
					System.out.println(Thread.currentThread().getName()+" get obj1 lock");
					
				}
			}
			
			
			
		}
	  }
	}
	 
 }
 class Mylock {
	 public static final Object obj1=new Object();
	 public static final Object obj2=new Object();
	 
	 
 }
运行结果为:
程序还在运行中但是输出已经停止了,这就是一个死锁程序的一种情况。
关于死锁,在学习时发现一篇文章写得还挺详细可以参考阅读:http://blog.youkuaiyun.com/Alex123980/article/details/52318016
线程间通信
线程间通信的方法主要有:
wait();   //等待 ,线程让出执行权,必须被唤醒才能被获取执行权
notify(); //唤醒,在众多等待的线程中唤醒一个线程,进入可运行状态,准备获取执行权
notifyAll();  //全部唤醒,唤醒所有在等待的线程,进入可运行状态,准备获取执行权
PS:上诉方法并不是Thread类的方法,是Object的方法,是针对锁的方法,即在某个锁上wait的线程,只有在该锁中才能被notify。
看一个经典的生产者和消费者问题来理解线程中的通信
需求:一个生产者生产商品生产完一个就有一个消费者把他买走,买走之后生产者再生产,一直重复
package com.ccl.test;

public class MakerBuyer {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Goods goods=new Goods("包子");
		Maker maker=new Maker(goods);
		Buyer buyer=new Buyer(goods);
		Thread t0=new Thread(maker);
		Thread t1=new Thread(buyer);
		t0.start();
		t1.start();

	}

}


//商品类
class Goods{
	private String name; //商品名称	
	private int count=1; //商品个数
	private boolean isNoMake=false; //是否不能制造商品
	public Goods(String name){
		this.name=name;
		
	}
	public synchronized void makeGoods(){
		if(isNoMake){
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		name="商品"+count++;
		System.out.println("当前线程:"+Thread.currentThread().getName()+"====制造了"+name);
		isNoMake=true;
		notify();
		
		
	}
	public synchronized void buyGoods(){
		if(!isNoMake){
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println("当前线程:"+Thread.currentThread().getName()+"====把"+name+"=============买走了");
		isNoMake=false;
		notify();
		
		
	}
}
class Maker implements Runnable{
     Goods g;
     public Maker(Goods g){
    	 this.g=g;
     }
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			g.makeGoods();
		}
	}

}
class Buyer implements Runnable{
    Goods g;
    public Buyer(Goods g){
   	 this.g=g;
    }
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			g.buyGoods();
		}
	}

}
运行结果:
达到了要求,上诉用同步函数synchronized ,所以是使用对象锁,锁是当前对象,所以上诉的wait ()、notify() 是Goods的方法,可以写成this.wait(),this.notify();   在notify的时候也只能唤醒在该对象上被wait的线程。
这个情况适合只有一个生产者和一个消费者的情况下
我们再main方法上再加一个生产者和消费者,在两个生产者和消费者的情况下在运行上面的代码
public static void main(String[] args) {
		// TODO Auto-generated method stub
		Goods goods=new Goods("包子");
		Maker maker=new Maker(goods);
		Maker maker1=new Maker(goods);
		Buyer buyer=new Buyer(goods);
		Buyer buyer1=new Buyer(goods);
		Thread t0=new Thread(maker);
		Thread t1=new Thread(maker1);
		Thread t2=new Thread(buyer);
		Thread t3=new Thread(buyer1);
		t0.start();
		t1.start();
		t2.start();
		t3.start();

	}
运行结果
一个产品会被多个消费者,不是想要的结果
分析上述代码发现,会有情况生产者1生产一个商品,然后消费者1买走,并wait,这时候notify的是消费者2,消费者2被唤醒之后被没有在去判断是不是可以购买就直接往下走了,所以出现一个商品被两个消费者消费,所以if应该换成while这样线程被唤醒之后都要去判断是不是有权利去购买或者生产。就不会出现这种情况。将if换成while之后再运行程序。
运行结果:
会出现程序还在运行但是已经不生产也不消费了,这就是死锁出现了。
分析死锁情况(最糟糕的情况):
1.生产者1得到执行权生产商品1,不能再生产进入wait,
2.生产者2得到执行权,不能生产进入wait,
3.消费者1得到执行权消费商品1 ,进入wait,唤醒生产者1
4.消费者2得到执行权进来不能再消费进入wait,
5.生产者1得到执行权生产商品2,进入wait,唤醒生产者2
6.生产者2得到执行权不能再生产,进入wait

  所以四个人都在wait没人活动,所以死锁了。
      所以这种情况就是只唤醒一个线程正好唤醒了和自己功能一样的线程,使得所有的线程都等待了,所以只要唤醒的时候唤醒所有正在等待的线程这样死锁就解决了。
但是这样唤醒所有的线程不太好,不用的线程就不要唤醒了,所以再完成一个需求,生产者生产之后只唤醒消费者,消费者消费之后只唤醒生产者,这样的状态才是最理想的。

既然JAVA有万物皆对象的思想,锁应该也是个对象。
可以发现在java.util.concurrent.locks中有lock对象
Lock和Condition :lock代表一个锁,Condition代表条件,condition的await(),signal(),signalAll();可以理解成wait,notify ,notifyAll
所以讲上述需求用Lock和condition来做代码如下
package com.ccl.test;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MakerBuyer {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Goods goods=new Goods("包子");
		Maker maker=new Maker(goods);
		Maker maker1=new Maker(goods);
		Buyer buyer=new Buyer(goods);
		Buyer buyer1=new Buyer(goods);
		Thread t0=new Thread(maker);
		Thread t1=new Thread(maker1);
		Thread t2=new Thread(buyer);
		Thread t3=new Thread(buyer1);
		t0.start();
		t1.start();
		t2.start();
		t3.start();

	}

}


//商品类
class Goods{
	private String name; //商品名称	
	private int count=1; //商品个数
	private boolean isNoMake=false; //是否不能制造商品
	private Lock lock=new ReentrantLock();
	Condition makerCondition=lock.newCondition();
	Condition buyerCondition=lock.newCondition();
	public Goods(String name){
		this.name=name;
		
	}
	public   void makeGoods(){
		lock.lock();
		try{
			while(isNoMake){
				try {
					makerCondition.await();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			name="商品"+count++;
			System.out.println("当前线程:"+Thread.currentThread().getName()+"====制造了"+name);
			isNoMake=true;
			buyerCondition.signal();
			
		}finally{
			lock.unlock();
			
		}
		
		
		
	}
	public   void buyGoods(){
		lock.lock();
		try{
			while(!isNoMake){
				try {
					buyerCondition.await();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			System.out.println("当前线程:"+Thread.currentThread().getName()+"====把"+name+"=============买走了");
			isNoMake=false;
			makerCondition.signal();
			
		}finally{
			lock.unlock();
		}
		
		
		
	}
}
class Maker implements Runnable{
     Goods g;
     public Maker(Goods g){
    	 this.g=g;
     }
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			g.makeGoods();
		}
	}

}
class Buyer implements Runnable{
    Goods g;
    public Buyer(Goods g){
   	 this.g=g;
    }
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			g.buyGoods();
		}
	}

}
运行结果:
达到要求
PS:在使用lock.lock()的时候必须有
try{
//同步的代码
}finally{
lock.unlock();

否则同步的代码发生异常之后将不释放锁。
顺便介绍一下API中的Demo,需求:有一个容量为100的容器,生产者一直往里面生产商品,消费者一直往外消费,当总数已经生产100个了,生产者不再生产等待消费者消费。当总数为0时,消费者不再消费等待生产者生产之后才能消费。当容容器生产到99的时候生产者回过来将商品放入第0的位置,消费者相同。
代码
package com.ccl.test;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionAPIDemo {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		BounderBuffer bounderBuffer=new BounderBuffer();
		Maker1 maker=new Maker1(bounderBuffer);
		Maker1 maker1=new Maker1(bounderBuffer);
		Buyer1 buyer=new Buyer1(bounderBuffer);
		Buyer1 buyer1=new Buyer1(bounderBuffer);
		Thread t0=new Thread(maker);
		Thread t1=new Thread(maker1);
		Thread t2=new Thread(buyer);
		Thread t3=new Thread(buyer1);
		t0.start();
		t1.start();
		t2.start();
		t3.start();

	}
}

class BounderBuffer{
	final Lock lock=new ReentrantLock();//定义锁
	final Condition notFull=lock.newCondition();
	final Condition notEmpty=lock.newCondition();
	final Object []items=new Object[100];
	
	int putptr,takeptr,count;
	
	public void makeGoods(Object goodsName){
		lock.lock();
		try{
			while(count==items.length){	 
					notFull.await();		
			}
			items[putptr]=goodsName.toString()+putptr;
			System.out.println("当前线程 :"+Thread.currentThread().getName()+"===生产了"+goodsName+" "+putptr ); 
			if(++putptr==items.length) putptr=0;	
			count++;
			notEmpty.signal();
		}catch(Exception e){
			//处理语句
		}finally{
			lock.unlock();
		}
	
	}
	public void buyGoods(){
		lock.lock();
		try{
			while(count==0){	 
					notEmpty.await();		
			}
			Object goodsName=items[takeptr];
			System.out.println("当前线程 :"+Thread.currentThread().getName()+"===消费了商品========"+goodsName);
			if(++takeptr==items.length) takeptr=0;
			count--;
			notFull.signal();
		}catch(Exception e){
			//处理语句
		}finally{
			lock.unlock();
		}
	
	}
	
}
class Maker1 implements Runnable{
	BounderBuffer bounderBuffer; 
    public Maker1(BounderBuffer bounderBuffer){
   	 this.bounderBuffer=bounderBuffer;
    }
	@Override
	public void run() {
		// TODO Auto-generated method stub
	      
		while(true){
			
			bounderBuffer.makeGoods("包子");
		}
	}

}
class Buyer1 implements Runnable{
	BounderBuffer bounderBuffer;
   public Buyer1(BounderBuffer bounderBuffer){
  	 this.bounderBuffer=bounderBuffer;
   }
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			bounderBuffer.buyGoods();
		}
	}

}
读者可以研究一下运行结果。

线程池

需求三个工人组装十个商品每个商品组装需要3个步骤,每个工人必须组装完一个商品之后才能继续组装下一个商品

ps:ExecutorService threadPool=Executors.newFixedThreadPool(3);   //可以创建有3个线程的线程池
 代码:
package com.ccl.test;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ThreadPoolTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//固定的线程池
		ExecutorService threadPool=Executors.newFixedThreadPool(3);
		//缓存的线程池,几个任务就生成几个线程
		//ExecutorService threadPool=Executors.newCachedThreadPool();
		//单线程线程池     如果线程死了自动生成一个替补
		//ExecutorService threadPool=Executors.newSingleThreadExecutor();
		 for(int i =1 ;i<=10;i++){
			 final int task=i;
		 threadPool.execute(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				for(int j=1;j<=3;j++){
					 try {
						Thread.sleep(30);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} 
					System.out.println(Thread.currentThread().getName() +"=====正在生产商品"+task+"的第"+j+"个步骤");
					
				}
			}
		});
	   }
		 
	 
		 
	}

}

运行结果:

创建一个线程池,固定里面有3个线程,让线程池去执行一个Runnable,重复十次模拟生产十个商品。
如果我们需要线程在做完任务之后返回结果该如何实现?、
需求:有三个工人组装十个商品每个商品组装需要3个步骤,每个工人必须组装完一个商品之后才能继续组装下一个商品,商品生产完成之后,放入特定的容器并提示是哪个工人生产了哪个商品。

package com.ccl.test;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class CallableAndFutureTest {

	public static void main(String[] args) {
		 
          
        System.out.println("工人们正在努力生产商品中。。。");
       //创建线程池3个线程
        ExecutorService threadPool=Executors.newFixedThreadPool(3);
        CompletionService<Map<String,String >> completionService=new ExecutorCompletionService<Map<String,String >>(threadPool);
        //生产10个商品
        for(int i=1;i<=10;i++){
        	final int task=i;
        	completionService.submit(new Callable<Map<String,String >>() {

				@Override
				public Map<String,String > call() throws Exception {
					// TODO Auto-generated method stub
					Map<String,String> map=new HashMap<String, String>();
					//生产每个商品有3个步骤
					 for(int j=1;j<=3;j++){
	 					 try {
	 						Thread.sleep(30);
	 					} catch (InterruptedException e) {
	 						// TODO Auto-generated catch block
	 						e.printStackTrace();
	 					} 
	 					System.out.println(Thread.currentThread().getName() +"=====正在生产商品"+task+"的第"+j+"个步骤");
	 					
	 				}
					 //商品生产结束之后存入map
					 map.put("result",Thread.currentThread().getName()+"===生产了商品"+task);
					 return map;
				}
			});
        }
       
        
        //获取数据,获取十次数据,若是没有返回数据则等待
        for (int i=1;i<=10;i++){
        	try {
			   Map<String , String >	resultMap=completionService.take().get();
			   System.out.println(resultMap.get("result"));
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ExecutionException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        	
        }
        
	}

}

运行结果:

Semaphore信号灯

 semaphore信号灯可以判断当前线程继续往下执行或者原地等待,如果可以获取信号灯就执行,如果所有的信号灯都被其他线程占有,那么当前线程先等待,直到有一个线程释放了信号灯,当前线程才有机会往下执行,举个例子:
需求:有十个工人,每个人要组装一件商品,但是操作间只有3个,每各操作间只允许有一个工人在操作,操作间满的时候,其他工人需要等待,直到有工人完成等待的工人才有机会进入。
package com.ccl.test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class SemaphoreTest {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//定义一个缓存线程池
        ExecutorService threadPool=Executors.newCachedThreadPool();
        //定义semaphore信号灯
        final Semaphore semaphore=new Semaphore(3);
        //开启十个任务
        for(int i=0;i<10;i++){
        	threadPool.execute(new Runnable() {
				
				@Override
				public void run() {
					// TODO Auto-generated method stub
					try {
						//判断是否还有信号灯可以获取,如果没有则等待
						Thread.sleep((long) (Math.random()*10000));
						semaphore.acquire();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println("工人:"+Thread.currentThread().getName()+"进入操作间,当前还剩"+(semaphore.availablePermits())+"间操作间");
					try {
						//模拟工人在操作间进行操作要花费时间
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println("工人:"+Thread.currentThread().getName()+"操作完毕,即将离开操作间");
					//释放信号灯
					semaphore.release();
					System.out.println("工人:"+Thread.currentThread().getName()+"已经离开操作间,当前还剩"+(semaphore.availablePermits())+"间操作间");
				}
			});
        	
        	
        }
        
	}
}
运行结果:

PS:semaphore.acquire()//判断是否有信号灯可以获取
        semaphore.availablePermits();//获取还有几个信号灯
 semaphore.release();//信号灯释放
CyclicBarrier  同步屏障
 CyclicBarrier 同步屏障是指多个线程之间要相互等待,只有全部线程都到达之后才开始后面的工作,举个例子:
需求:有10个工人要一起生产商品,但是要进入操作间之前必须要十个人全部到齐,先到的工人需要等待,等到第十个人到达之后,一起进入操作间生产商品
package com.ccl.test;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CyclicBarrierTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ExecutorService threadPool=Executors.newCachedThreadPool();
		final CyclicBarrier cyclicBarrier=new CyclicBarrier(10);
		//等待10个工人同时到达
		for(int i=0;i<10;i++){
			threadPool.execute(new Runnable() {
				
				@Override
				public void run() {
					// TODO Auto-generated method stub
					try {
						//模拟十个工人到达时间不一致
						Thread.sleep((long) (Math.random()*10000));
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println("工人:"+Thread.currentThread().getName()+"已准备好,还需要"+(10-cyclicBarrier.getNumberWaiting()-1)+"工人可以开始生产");
				  try {
					cyclicBarrier.await();
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}  
				System.out.println("工人:"+Thread.currentThread().getName()+"正在生产商品");  
				
				}
			});
			
			
		}

	}

}

运行结果:

CountDownLatchTest同步计数器
线程等待对应的计数器为0时才继续往下操作。否则等待,举个例子:
需求:有3个工人要比赛生产商品,一个裁判。3个工人在等待裁判的开始指令,裁判开始之后,3个工人开始生产商品,同时裁判等待3个工人完成比赛,当3个工人全部完成时,裁判宣布比赛结束。
package com.ccl.test;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountDownLatchTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
     ExecutorService threadPool = Executors.newCachedThreadPool();
     //定义裁判计数器
     final CountDownLatch countDownLatch1=new CountDownLatch(1);
     //定义有几个工人在比赛
     final CountDownLatch countDownLatch2=new CountDownLatch(3);
     //模拟3个工人在比赛
     for(int i=0;i<3;i++){
    	 threadPool.execute(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("工人:"+Thread.currentThread().getName()+"已准备就绪");
				try {
					//等待裁判计时器为0时继续往下走
					countDownLatch1.await();
					//模拟工人生产商品时需要的时间
					System.out.println("工人"+Thread.currentThread().getName()+"开始生产");
					Thread.sleep((long) (Math.random()*10000));
					System.out.println("工人"+Thread.currentThread().getName()+"已生产完毕");
					//将剩余人数计数器减1
					countDownLatch2.countDown();
					
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		});
    	 
    	 
     }
     //模拟裁判开始比赛和结束比赛
     try{
    	 Thread.sleep((long) (Math.random()*10000));
    	 System.out.println("各就各位,预备。。。。。。");
    	 countDownLatch1.countDown();
    	 System.out.println("比赛开始");
    	 countDownLatch2.await();
    	 System.out.println("全部工人已完成商品,比赛结束");
     }catch(Exception e){
    	 e.printStackTrace();
     }
     
     
     
     
	}

}
运行结果:
Exchanger  两个线程之间换取数据
需求:有两个工人,一个需要零件1缺少零件2,一个缺少零件2需要零件1,他们约定换取各自所需零件
package com.ccl.test;

import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExchangerTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
       ExecutorService threadPool=Executors.newCachedThreadPool();
       final Exchanger exchanger=new Exchanger();
       threadPool.execute(new Runnable() {
		
		@Override
		public void run() {
			// TODO Auto-generated method stub
			String data1="零件1";
			System.out.println("工人:"+Thread.currentThread().getName()+"已到达,正在等待拿零件1换取零件2");
			try {
				Thread.sleep((long) (Math.random()*10000));
				String data2=(String) exchanger.exchange(data1);
				System.out.println("工人:"+Thread.currentThread().getName()+"已经换取"+data2);
			} catch ( Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			
		}
	});
       
       
       
       threadPool.execute(new Runnable() {
   		
   		@Override
   		public void run() {
   			// TODO Auto-generated method stub
   			String data1="零件2";
   			System.out.println("工人:"+Thread.currentThread().getName()+"已到达,正在等待拿零件2换取零件1");
   			try {
   				Thread.sleep((long) (Math.random()*10000));
   				String data2=(String) exchanger.exchange(data1);
   				System.out.println("工人:"+Thread.currentThread().getName()+"已经换取"+data2);
   			} catch ( Exception e) {
   				// TODO Auto-generated catch block
   				e.printStackTrace();
   			}
   			
   			
   		}
   	});
	}

}
运行结果:

























评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值