- 创建线程的方式:
1.继承Thread类
class XxxThread extends Thread{
public void run(){}
}
new XxxThread().start();
- 2.实现Runnable接口:
class XxxRunnable implements Runnable{
public void run(){}
}
new Thread(new XxxRunnable()).start();
- 3.匿名内部类:
new Thread(){
public void run(){
}
}.start();
或
new Thread(new Runnable(){
public void run(){}
}).start();
- 4.实现线程安全的方式:
1.同步代码块
synchronized(锁对象){ }
2.同步方法
修饰符 synchronized 返回值类型 方法名(参数列表){}
3.Lock接口
void lock() 获取锁
void unlock() 释放锁
获取锁和释放锁必须成对出现。
1.1 线程池概念:
- 什么是线程池:一个用来创建和管理线程对象的容器。
- 线程池的核心思想:线程复用。
- 线程池的好处:
- 减少资源的消耗,避免频繁的创建和销毁线程。
- 提高程序的响应速度。
- 提高线程的可管理性。
1.2 线程池的使用
1.2.1线程池的创建方式:
如何获得线程池对象:
- 在JDK1.5之后,官方专门提供了一个线程池工具类用来创建线程池对象。
- 该类叫:Executors,通过该类的静态方法来创建线程池对象,静态方法如下:
public static ExecutorService newFixedThreadPool(int nThreads);
根据指定的线程数量创建线程池对象。
- ExecutorService接口概述:只要是实现了该接口的对象就是一个线程池对象。
- ExecutorService接口常用方法:
- Future<?> submit(Runnable task);提交Runnable任务
- Future<?> submit(Callable<T> task);提交Callable任务(用future接收)
- void shutdown();销毁线程池,会等待线程池中已经提交的所有任务执行完毕之后再销毁。
- shutdownNow();立即销毁线程池:如果线程池中还没有开始执行的任务就不会执行了。
- Future接口概述:
- 用来封装Callable任务执行完毕的结果。
- Future接口常用方法
- V get();获得call方法执行完毕的返回值。
- 线程池提交Runnable任务步骤:
- 创建线程池对象并指定线程数量:ExecutorService tp=Executors.newFixedThreadpool(3)
- 创建实现Runnable接口,重写run方法:将线程任务相关的代码写在该方法中
- 创建实现类对象,调用线程池对象的submit方法传递实现类对象:tp.submit(new RunnableTask());
- 调用线程池对象的shutdown方法销毁线程池:tp.shutdown();
public class RunnableTask implements Runnable {
@Override
public void run() {
System.out.println("这里是线程任务..."+Thread.currentThread().getName());
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo01 {
public static void main(String[] args) {
//创建线程池对象
ExecutorService tp=Executors.newFixedThreadPool(3);
//提交Runnable任务
tp.submit(new RunnableTask());
tp.submit(new RunnableTask());
tp.submit(new RunnableTask());
tp.submit(new RunnableTask());
//销毁线程池,在实际开发中一般是不销毁的
tp.shutdown();
}
}
1.2.2 提交任务-Callable任务:
- 线程池提交Callable任务步骤:
- 创建线程池对象并指定线程数量:ExecutorService tp=Executors.newFixedThreadpool(3)
- 创建类实现Callable接口,重写call方法(有返回值):将线程任务相关的代码写在该方法中
- 创建实现类对象,调用线程池对象的submit方法传递实现类对象:tp.submit(new CallableTask());
- 调用线程池对象的shutdown方法销毁线程池:tp.shutdown();
import java.util.concurrent.Callable;
public class CallableTask implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("线程任务..."+Thread.currentThread().getName());
for (int i = 0; i < 10; i++) {
System.out.println("call+"+i);
}
return "abc";
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ThreadPoolDemo02 {
public static void main(String[] args) throws Exception {
//创建线程池对象
ExecutorService tp=Executors.newFixedThreadPool(3);
//提交Callable任务
Future<String> f= tp.submit(new CallableTask());
//获得call方法执行完毕的返回值
//get方法时阻塞的方式,会阻塞当前线程的执行,所以main方法线程的输出在后面
System.out.println(f.get());
for (int i = 0; i < 10; i++) {
System.out.println("main="+i);
}
//销毁线程池,在实际开发过程中一般是不销毁的
tp.shutdown();
}
}
- Callable和Runnable接口的选择:
- 如果任务执行完毕之后需要返回一个结果给调用者,则选择Callable。
- 如果任务执行完毕之后不需要返回结果给调用者,则随便选择。
- 需求说明:调用线程池对象方法提交线程任务,同时执行下面任务:
- 计算1-100的和
- 计算1-200的和
import java.util.concurrent.Callable;
public class countTask implements Callable<Integer> {
private int num;
public countTask(int num) {
this.num = num;
}
@Override
public Integer call() throws Exception {
int sum=0;
for (int i = 1; i <= num; i++) {
sum += i;
}
return sum;
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ThreadPoolDemo03 {
public static void main(String[] args) throws Exception {
//创建线程池对象
ExecutorService tp = Executors.newFixedThreadPool(2);
//提交Callable任务
Future<Integer> f1 = tp.submit(new countTask(100));
Future<Integer> f2 = tp.submit(new countTask(200));
System.out.println(f1.get());//5050
System.out.println(f2.get());//20100
//销毁线程池
tp.shutdown();
}
}