多线程

class Thread{
                  //声明
                  private Runnable runnable;
                  //构造函数
                  Thread(Runnable runnable)  {  this.runnable=runnable; }
                  //运行方法
                  public void run(){ if(runnable!=null) {  runnable.run();  }}
                  //启动线程
                  public void start()   { run();  }
            }
                  //实例
                  Runnable d=new Demo();
                  Thread t=new Thread(d);
                  t.start();

概括

  • 实现Runnable接口,更加符合面向对象。线程分两部分,一部分线程对象,一部分线程任务。继承Thread类,线程对象和线程任务耦合在一起,一旦创建Thread类的子类对象,既是线程对象,又是线程任务。
  • 进程是程序的运行实例。
  • 进程是程序向操作系统申请资源的基本单位。线程是进程中可独立执行的最小单位。
  • Java标准库类Java.lang.Thread就是java平台对线程的实现。Thread类或其子类的一个实例就是一个线程。
  • runnable接口可以被看做是对任务进行的抽象,任务的处理逻辑就体现在run方法之中。Thread类实际上是Runnable接口的一个实现类。
  • ArrayList HashMap SimpleDateFormat 等都是非线程安全的

状态

  • 新建 (new) : 在创建线程后,它将处于新建状态,该线程并没有纳入线程调度。
  • 就绪 (Runnable) : 当调用线程的start方法后,该线程纳入线程调度的控制,其处于一个可运行状态,等待分配时间片段以并发运行。
  • 运行 (Running) : 线程在开始执行时进入运行状态
  • 睡眠 (Sleeping) : 线程的执行可通过使用 sleep() 方法来暂时中止。在睡眠后,线程将进入就绪状态
  • 等待 (Waiting) : 如果调用了 wait() 方法,线程将处于等待状态。用于在两个或多个线程并发运行时。
  • 挂起 (Suspended) : 在临时停止或中断线程的执行时,线程就处于挂起状态。
  • 恢复 (Resume) : 在挂起的线程被恢复执行时,可以说它已被恢复。
  • 阻塞 (Blocked) :在线程等待一个事件时(例如输入/输出操作),就称其处于阻塞状态。
  • 死亡 (Dead) : 在 run() 方法已完成执行, stop() 方法被调用之后或者在运行过程中抛出了一个未捕获的异常,那么线程结束,等待GC回收. 线程就处于死亡状态。

实现Runnable接口的好处:

  • java仅支持单继承,实现接口可以把继承的机会留给其他类,实现接口更灵活一些
  • 可以将线程与线程要执行的任务分离开减少耦合度
    另外:一个线程仅需要一个实例时可以使用匿名内部类创建,这种方式有助于简化代码

Thread类的sleep()和yield()方法是静态的

Thread类的sleep()和yield()方法将在当前正在执行的线程上运行。所以在其他处于等待状态的线程上调用这些方法是没有意义的。这就是为什么这些方法是静态的。
①sleep方法给其他线程运行机会时不考虑线程的优先级,因此会给低线程优先级运行的机会,而yield方法只会给相同优先级或者更高优先级线程运行的机会

②线程执行sleep()方法后转入阻塞状态,所以,执行sleep()方法的线程在指定的时间内不会被执行,而yield()方法只是使当前线程重新回到可执行状态,所以执行yield()方法的线程可能在进入可执行状态后马上又被执行
1:sleep方法是Thread的静态方法,wait方法是Object类的普通方法:
2:sleep方法不释放同步锁,wait方法释放同步锁(执行notify方法唤醒wait的线程时是不释放同步锁的)
3:wait方法用于线程间通信,而sleep方法用于短暂的暂停线程:
4:sleep针对当前线程,而wait针对被同步代码块加锁的对象
5:sleep方法是当前线程暂停指定时间,将执行机会让给其它线程,时间结束后进入就绪状态等待
调用wait方法会暂停线程,当前线程释放对象的同步锁,进入等待池(wait pool),只有调用对象的notify或者notifyAll方法唤醒时,线程进入等锁池(lock pool),直到线程再次获得对象的锁才会进入就绪状态:
6:wait方法(notify,notifyAll)只能在同步方法或者同步块中使用(如果在non-synchronized函数或non-synchronizedblock中进行调用,虽然能编译通过,但在运行时会发生illegalMonitorStateException的异常);sleep方法可以在任意位置使用

volatile关键字在Java中的作用

volatile可以看成是synchronized的一种轻量级的实现,volatile有synchronized可见性的特性,但没有synchronized原子性的特性。可见性即用volatile关键字修饰的成员变量表明该变量不存在工作线程的副本,线程每次直接都从主内存中读取,每次读取的都是最新的值,这也就保证了变量对其他线程的可见性。另外,使用volatile还能确保变量不能被重排序,保证了有序性。 当我们使用volatile关键字去修饰变量的时候。并不是不缓存直接操作主内存,应该是当有线程修改了volatile的值后,会直接写入主内存中,然后会让其他线程私有内存的值失效(cpu寄存器里面对应的指令失效),然后其他线程再操作该变量时,之前缓存的失效了,会重新去主内存加载。

使线程按顺序执行

[1] 使用join方法
[2] 使用线程的wait方法
[3] 使用线程的线程池方法:单线程化线程池:串行化执行提交的任务。
[4] 使用线程的Condition(条件变量)方法:通常与一个锁关联。需要在多个Contidion中共享一个锁时,可以传递一个Lock/RLock实例给构造方法,否则它将自己生成一个RLock实例
[5] 使用线程的CountDownLatch(倒计数)方法:位于java.util.concurrent包下,利用它可以实现类似计数器的功能。
[6] 使用线程的CyclicBarrier(回环栅栏)方法:通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。我们暂且把这个状态就叫做barrier,当调用await()方法之后,线程就处于barrier了。
[7] 使用线程的Semaphore(信号量)方法:Semaphore是一个计数信号量,从概念上将,Semaphore包含一组许可证,如果有需要的话,每个acquire()方法都会阻塞,直到获取一个可用的许可证,每个release()方法都会释放持有许可证的线程,并且归还Semaphore一个可用的许可证。然而,实际上并没有真实的许可证对象供线程使用,Semaphore只是对可用的数量进行管理维护。

创建守护线程

使用Thread类的setDaemon(true)方法可以将线程设置为守护线程,需要注意的是,需要在调用start()方法前调用这个方法,否则会抛出IllegalThreadStateException异常。

ThreadLocal

ThreadLocal用于创建线程的本地变量,我们知道一个对象的所有线程会共享它的全局变量,所以这些变量不是线程安全的,我们可以使用同步技术。但是当我们不想使用同步的时候,我们可以选择ThreadLocal变量。每个线程都会拥有他们自己的Thread变量,它们可以使用get()\set()方法去获取他们的默认值或者在线程内部改变他们的值。ThreadLocal实例通常是希望它们同线程状态关联起来是private static属性。

阻塞队列

通常使用阻塞队列来实现生产者-消费者模型

  • java.util.concurrent.BlockingQueue的特性是:当队列是空的时,从队列中获取或删除元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。
  • 阻塞队列不接受空值,当你尝试向队列中添加空值的时候,它会抛出NullPointerException。
  • 阻塞队列的实现都是线程安全的,所有的查询方法都是原子的并且使用了内部锁或者其他形式的并发控制。
  • BlockingQueue 接口是java collections框架的一部分,它主要用于实现生产者-消费者问题。

Callable和Future

  • Java 5在concurrency包中引入了java.util.concurrent.Callable 接口,它和Runnable接口很相似,但它可以返回一个对象或者抛出一个异常。
  • Callable接口使用泛型去定义它的返回类型。Executors类提供了一些有用的方法去在线程池中执行Callable内的任务。由于Callable任务是并行的,我们必须等待它返回的结果。java.util.concurrent.Future对象为我们解决了这个问题。在线程池提交Callable任务后返回了一个Future对象,使用它我们可以知道Callable任务的状态和得到Callable返回的执行结果。Future提供了get()方法让我们可以等待Callable结束并获取它的执行结果。

FutureTask

FutureTask是Future的一个基础实现,我们可以将它同Executors使用处理异步任务。通常我们不需要使用FutureTask类,单当我们打算重写Future接口的一些方法并保持原来基础的实现是,它就变得非常有用。我们可以仅仅继承于它并重写我们需要的方法。

并发容器的实现

  • Java集合类都是快速失败的,这就意味着当集合被改变且一个线程在使用迭代器遍历集合的时候,迭代器的next()方法将抛出ConcurrentModificationException异常。
  • 并发容器支持并发的遍历和并发的更新。
  • 主要的类有ConcurrentHashMap, CopyOnWriteArrayList 和CopyOnWriteArraySet。

synchronized同步锁的不同使用场景

synchronized加在普通方法上:多个线程不能"同时"执行该方法,只能有先后顺序同步执行.锁定的是当前对象,也就是this.
注:只针对同一个对象访问同一个方法会产生同步,两个不同的对象访问各自的同步方法不会产生同步.

当一个线程进入一个对象的synchronized方法后,其他线程是否可进入此对象的其他方法

该问题需要分不同的情况去解释:
1)这个对象的其他方法前是否加了synchronized关键字,如果加了,那么会产生互斥,如果没有加,则可以访问.
2)如果这个方法内部调用了wait方法,则可以进入其他synchronized修饰的方法,因为wait会释放掉对象锁.
3)如果其他方法都加了synchronized修饰,并且内部没有调用wait方法,则不能,因为多个方法有synchronized产生互斥.
4)如果其他方法是static类型方法,则可以访问,静态方法使用synchronized修饰锁定的是当前.class,而普通方法锁定的是当前对象,即this,两者锁定的对象不是同一个,所以不能同步.

对java线程的解释

  • Java 的线程是通过java.lang.Thread类来实现的。可以通过创建Thread的实例来创建新的线程。每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体。通过调用Thread类的start()方法来启动一个线程。
  • 调用start()后,线程会被放到等待队列,等待CPU调度,并不一定要马上开始执行,只是将这个线程置于可动行状态。然后通过JVM,线程Thread会调用run()方法,执行本线程的线程体。

生产者消费者模式

  • 代码演示
public class TestThread {

	public static void main(String[] args) {
		Resourse resourse=new Resourse();
        Customer customer=new Customer(resourse);
        producter producter=new producter(resourse);
        Thread t1=new Thread(producter);
        Thread t2=new Thread(customer);
        t1.start();
        t2.start();
        
	}
	
	}
	
	class Resourse{
		
	private boolean flage=false;
	private  String   name;
	private int count=1;
	

	
	public synchronized void set(String name){
		if(flage)
			try {
				wait();
			} catch (InterruptedException e) {
				
				e.printStackTrace();
			}
		
		this.name=name+count;
		count++;
		System.out.println(Thread.currentThread().getName()+"-------生产者-------"+this.name);
             flage=true;
             notify();
	
	}
	public synchronized void out(String name){
		if(!flage)
			try {
				wait();
			} catch (InterruptedException e) {
				
				e.printStackTrace();
			}
		System.out.println(Thread.currentThread().getName()+"-------消费者-------"+this.name);
	            flage=false;
	            notify();
	}
	
	}
    class producter implements Runnable {
    	private Resourse resourse;
    	public producter(Resourse r) {
			this.resourse=r;
		}
		public void run() {
			while (true) {
				resourse.set("面包");
			}
		}
	}
    
    

	class Customer implements Runnable {
		private Resourse resourse;
		public Customer(Resourse r) {
			this.resourse=r;
		}

		public void run() {
			while(true){
				resourse.out("面包");
			}
		}
		
	}
	

问题升级

  • 当多个线程同时执行时(多余两个),会出现新的问题。
  • 问题描述
    多个生产者多次生产产品,消费者多次消费产品
  • 产生原因
    在线程被唤醒的时候、没有再次去判断,直接进行生产,导致问题的产生
  • 解决方案
    让被唤醒的线程必须重新去判断标记
    将if判断标记改为while即可,多生产,多消费,必须用while来判断

问题再次升级

  • 出现死锁,所有线程都在等待
  • 解决方案
    唤醒所有,将notify()换为notifyAll();
    仍然存在一些小遗憾,效率低了(唤醒己方,它去判断,浪费时间和资源,唤醒多个对方,但只能有一个去执行,JDK1.5有了新方案去解决)
  • 新版(1.5以后)与旧版的区别
  • 升级版
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestThread {

	public static void main(String[] args) {
		Resourse resourse = new Resourse();
		Customer customer = new Customer(resourse);
		producter producter = new producter(resourse);
		Thread t1 = new Thread(producter);
		Thread t2 = new Thread(customer);
		Thread t3 = new Thread(producter);
		Thread t4 = new Thread(customer);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}

}

class Resourse {

	private boolean flage = false;
	private String name;
	private int count = 1;
	private Lock lock = new ReentrantLock();
	private Condition cum=lock.newCondition();
	private Condition pro=lock.newCondition();
	public void set(String name) {

		lock.lock();
		try {
			while(flage)
				try {
					pro.await();
				} catch (InterruptedException e) {


				}

			this.name = name + count;
			count++;
			System.out.println(Thread.currentThread().getName() + "-------生产者-------" + this.name);
			flage = true;
			cum.signal();}finally{
			lock.unlock();
		}

	}

	public void out(String name) {
		lock.lock();
		try {
			while(!flage)
				try {
					cum.await();
				} catch (InterruptedException e) {
				}
			System.out.println(Thread.currentThread().getName() + "---消费者---" + this.name);
			flage = false;
		pro.signal();
		} finally {
			lock.unlock();
		}
	}

}

class producter implements Runnable {
	private Resourse resourse;

	public producter(Resourse r) {
		this.resourse = r;
	}

	public void run() {
		while (true) {
			resourse.set("面包");
		}
	}
}

class Customer implements Runnable {
	private Resourse resourse;

	public Customer(Resourse r) {
		this.resourse = r;
	}

	public void run() {
		while (true) {
			resourse.out("面包");
		}
	}

}

现实中的应用

class BoundedBuffer {
   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 put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length) 
         notFull.await();
       items[putptr] = x; 
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();
     } finally {
       lock.unlock();
     }
   }

   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0) 
         notEmpty.await();
       Object x = items[takeptr]; 
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();
       return x;
     } finally {
       lock.unlock();
     }
   } 
 }

  • 线程如何停止
    • stop方法过时。因为它会导致安全隐患,是线程不可控,正确的方式是让run方法执行完毕(设计标记flag,当满足条件时,改变标记为flase),run方法中有循环,定时的检查变量的值,来结束方法
    • interrupt()
      interrupt()的功能是将线程的冻结状态清除,让线程回复到运行状态(让线程重新具有CPU的执行资格);因为是强制性的所以会有异常发生,可以在catch中捕获。在异常处理中,改变标记让循环体结束,让run方法结束。

Sleep()、suspend()和wait()之间有什么区别

Thread.sleep()使当前线程在指定的时间处于“非运行”(Not Runnable)状态。线程一直持有对象的监视器。比如一个线程当前在一个同步块或同步方法中,其它线程不能进入该块或方法中。如果另一线程调用了interrupt()方法,它将唤醒那个“睡眠的”线程。
注意:sleep()是一个静态方法。这意味着只对当前线程有效,一个常见的错误是调用t.sleep(),(这里的t是一个不同于当前线程的线程)。即便是执行t.sleep(),也是当前线程进入睡眠,而不是t线程。t.suspend()是过时的方法,使用suspend()导致线程进入停滞状态,该线程会一直持有对象的监视器,suspend()容易引起死锁问题。
object.wait()使当前线程出于“不可运行”状态,和sleep()不同的是wait是object的方法而不是thread。调用object.wait()时,线程先要获取这个对象的对象锁,当前线程必须在锁对象保持同步,把当前线程添加到等待队列中,随后另一线程可以同步同一个对象锁来调用object.notify(),这样将唤醒原来等待中的线程,然后释放该锁。基本上wait()/notify()与sleep()/interrupt()类似,只是前者需要获取对象锁。

什么是线程饿死,什么是活锁

  • 当所有线程阻塞,或者由于需要的资源无效而不能处理,不存在非阻塞线程使资源可用。JavaAPI中线程活锁可能发生在以下情形:
    当所有线程在程序中执行Object.wait(0),参数为0的wait方法。程序将发生活锁直到在相应的对象上有线程调用Object.notify()或者Object.notifyAll()。
    当所有线程卡在无限循环中。

什么是Java Timer类,如何创建一个有特定时间间隔的任务

java.util.Timer是一个工具类,可以用于安排一个线程在未来的某个特定时间执行。Timer类可以用安排一次性任务或者周期任务。
java.util.TimerTask是一个实现了Runnable接口的抽象类,我们需要去继承这个类来创建我们自己的定时任务并使用Timer去安排它的执行。

Java中的同步集合与并发集合有什么区别?

同步集合与并发集合都为多线程和并发提供了合适的线程安全的集合,不过并发集合的可扩展性更高。
在Java1.5之前程序员们只有同步集合来用且在多线程并发的时候会导致争用,阻碍了系统的扩展性。
Java5介绍了并发集合像ConcurrentHashMap,不仅提供线程安全还用锁分离和 内部分区等现代技术提高了可扩展性。

Java中invokeAndWait 和 invokeLater有什么区别

这两个方法是Swing API 提供给Java开发者用来从当前线程而不是事件派发线程更新GUI组件用的。InvokeAndWait()同步更新GUI组件,比如一个进度条,一旦进度更新了,进度条也要做出相应改变。如果进度被多个线程跟踪,那么就调用invokeAndWait()方法请求事件派发线程对组件进行相应更新。而invokeLater()方法是异步调用更新组件的。

多线程中的忙循环是什么

忙循环就是程序员用循环让一个线程等待,不像传统方法wait(), sleep() 或 yield() 它们都放弃了CPU控制,而忙循环不会放弃CPU,它就是在运行一个空循环。这么做的目的是为了保留CPU缓存。
在多核系统中,一个等待线程醒来的时候可能会在另一个内核运行,这样会重建缓存。为了避免重建缓存和减少等待重建的时间就可以使用它了。

Java中活锁和死锁的区别

  • 活锁:一个线程通常会有会响应其他线程的活动。如果其他线程也会响应另一个线程的活动,那么就有可能发生活锁。同死锁一样,发生活锁的线程无法继续执行。然而线程并没有阻塞——他们在忙于响应对方无法恢复工作。
  • 死锁:两个或更多线程阻塞着等待其它处于死锁状态的线程所持有的锁。死锁通常发生在多个线程同时但以不同的顺序请求同一组锁的时候,死锁会让你的程序挂起无法完成任务。

如何避免死锁

  • 死锁的发生必须满足以下四个条件:
    • 互斥条件:一个资源每次只能被一个进程使用。
    • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
    • 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
    • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
      

三种用于避免死锁的技术:
加锁顺序(线程按照一定的顺序加锁)
加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
死锁检测

stop()和suspend()方法为何不推荐使用?

反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。

设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。

public class ThreadTest1{
private int j;
public static void main(String args[]){
  Inc inc=new Inc();
  Dec dec=new Dec();
  
  for(int i=0;i<2;i++){
   Thread t=new Thread(inc);
   t.start();
   t=new Thread(dec);
   t.start();
  } }
  
 private synchronized void inc(){  j++; }
 private synchronized void dec(){  j--; }
 
 class Inc implements Runnable{
  public void run(){
   for(int i=0;i<100;i++){    inc();}} }
   
  class Dec implements Runnable{
  public void run(){
   for(int i=0;i<100;i++){    dec();}  }}}

单例模式在多线程的应用


线程池

在这里插入图片描述
线程池作用就是限制系统中执行线程的数量。
根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。

  • Java里面线程池的顶级接口是Executor,但严格意义上讲Executor并不是一个线程池,只是一个执行线程的工具。真正的线程池接口是ExecutorService。
public interface Executor {

    void execute(Runnable command);
}
  • ExecutorService
    扩展了Executor接口。添加了一些用来管理执行器生命周期和任务生命周期的方法
    在这里插入图片描述
  • 构造器
public ThreadPoolExecutor(int corePoolSize,//线程池初始启动时线程的数量
                          int maximumPoolSize,//最大线程数量
                          long keepAliveTime,//空闲线程多久关闭?
                          TimeUnit unit,// 计时单位
                          BlockingQueue<Runnable> workQueue,//放任务的阻塞队列
                          ThreadFactory threadFactory,//线程工厂
                          RejectedExecutionHandler handler// 拒绝策略) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

在向线程池提交任务时,会通过两个方法:execute和submit。

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    // clt记录着runState和workerCount
    int c = ctl.get();
    //workerCountOf方法取出低29位的值,表示当前活动的线程数
    //然后拿线程数和 核心线程数做比较
    if (workerCountOf(c) < corePoolSize) {
        // 如果活动线程数<核心线程数
        // 添加到
        //addWorker中的第二个参数表示限制添加线程的数量是根据corePoolSize来判断还是maximumPoolSize来判断
        if (addWorker(command, true))
            // 如果成功则返回
            return;
        // 如果失败则重新获取 runState和 workerCount
        c = ctl.get();
    }
    // 如果当前线程池是运行状态并且任务添加到队列成功
    if (isRunning(c) && workQueue.offer(command)) {
        // 重新获取 runState和 workerCount
        int recheck = ctl.get();
        // 如果不是运行状态并且 
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            //第一个参数为null,表示在线程池中创建一个线程,但不去启动
            // 第二个参数为false,将线程池的有限线程数量的上限设置为maximumPoolSize
            addWorker(null, false);
    }
    //再次调用addWorker方法,但第二个参数传入为false,将线程池的有限线程数量的上限设置为maximumPoolSize
    else if (!addWorker(command, false))
        //如果失败则拒绝该任务
        reject(command);
}

这里写图片描述
比较重要的几个类

  • ExecutorService:真正的线程池接口。
  • ScheduledExecutorService:能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。
  • ThreadPoolExecutor:ExecutorService的默认实现。
  • ScheduledThreadPoolExecutor:继承ThreadPoolExecutor的
  • newSingleThreadExecutor
    创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
  • newFixedThreadPool
    创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
    线程池满了之后
    创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。
  • newCachedThreadPool
    创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,
    那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
  • newScheduledThreadPool
    创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
  • Thread.currentThread().getName()(获取当前线程名称)

例如,创建一个有固定线程数量的线程池:

   //创建一个可重用固定线程数的线程池
        ExecutorService pool = Executors. newSingleThreadExecutor();
        //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口
        Thread t1 = new MyThread();
        //将线程放入池中进行执行
        pool.execute(t1);
        pool.shutdown();

创建使用线程池

JDK8提供了五种创建线程池的方法

  • 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
  • (JDK8新增)会根据所需的并发数来动态创建和关闭线程。能够合理的使用CPU进行对任务进行并发操作,所以适合使用在很耗时的任务。注意返回的是ForkJoinPool对象。
public static ExecutorService newWorkStealingPool(int parallelism) {
    return new ForkJoinPool
        (parallelism,
         ForkJoinPool.defaultForkJoinWorkerThreadFactory,
         null, true);
}

public ForkJoinPool(int parallelism,
                        ForkJoinWorkerThreadFactory factory,
                        UncaughtExceptionHandler handler,
                        boolean asyncMode) {
        this(checkParallelism(parallelism),
             checkFactory(factory),
             handler,
             asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
             "ForkJoinPool-" + nextPoolId() + "-worker-");
        checkPermission();
    }

使用一个无限队列来保存需要执行的任务,可以传入线程的数量;不传入,则默认使用当前计算机中可用的cpu数量;使用分治法来解决问题,使用fork()和join()来进行调用。

  • 创建一个可缓存的线程池,可灵活回收空闲线程,若无可回收,则新建线程。
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
  • 创建一个单线程的线程池
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
  • 创建一个定长线程池,支持定时及周期性任务执行
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值