线程的创建与使用
在Java中,创建多线程有多种方式,JDK1.5之前,有两种创建线程的方式:
- 继承Thread
- 实现Runnable接口
JDK1.5之后有新增了两种创建多线程的方式:
- 实现Callable
- 使用线程池
Thread
Thread类的特性
-
每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为 线程体
-
通过该Thread对象的start()方法来启动这个线程,而非直接调用run()
构造器
- Thread() :创建新的Thread对象
- Thread(String threadname): :创建线程并指定线程实例名
- Thread(Runnable target) :指定创建线程的目标对象,它实现了Runnable接口中的run方法
- Thread(Runnable target, String name) :创建新的Thread对象
创建线程
通过继承Thread创建线程步骤:
- 创建Thread的子类
- 重写run方法
- 主线程创建Thread子类的实例
- 执行start方法
class Capture extends Thread
{
@Override
public void run()
{
System.out.println("我是线程" + Thread.currentThread().getName() + ",我正在执行爬虫任务");
}
}
public class T1 {
public static void main(String[] args) {
Capture capture = new Capture();
capture.start();
// 再开线程跑爬虫程序,需要再重新创建capture实例
// 并执行start方法
Capture capture1 = new Capture();
capture1.start();
}
}
注意:一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上的异常“IllegalThreadStateException”。
Thread类常用方法
Modifier and Type | Method | Description |
---|---|---|
static Thread |
currentThread() |
Returns a reference to the currently executing thread object. |
String |
getName() |
Returns this thread’s name. |
int |
getPriority() |
Returns this thread’s priority. |
boolean |
isAlive() |
Tests if this thread is alive. |
static boolean |
interrupted() |
Tests whether the current thread has been interrupted. |
void |
join() |
Waits for this thread to die. |
void |
join(long millis) |
Waits at most millis milliseconds for this thread to die. |
void |
join(long millis, int nanos) |
Waits at most millis milliseconds plus nanos nanoseconds for this thread to die. |
void |
setName(String name) |
Changes the name of this thread to be equal to the argument name . |
void |
setPriority(int newPriority) |
Changes the priority of this thread. |
static void |
sleep(long millis) |
Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers. |
void |
start() |
Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread. |
static void |
yield() |
A hint to the scheduler that the current thread is willing to yield its current use of a processor. |
线程优先级
Java线程调度有两种方法:时间片、抢占式(高优先级的线程抢占CPU)
Java 的调度方法:
- 同优先级线程组成先进先出队列(先到先服务),使用时间片策略;
- 对高优先级,使用优先调度的抢占式策略
线程的优先级等级(默认为5,最小为1,最大为10)
- MAX_PRIORITY :10
- MIN _PRIORITY :1
- NORM_PRIORITY :5
线程创建时继承父线程的优先级;低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用。
Thread1 thread1 = new Thread1();
thread1.setPriority(1);
Thread2 thread2 = new Thread2();
thread2.setPriority(10);
thread1.start();
thread2.start();
Runnable
使用继承Thread的方法来创建线程,但该方法有单继承的局限性。
使用实现Runnable接口的方式要优于使用继承Thread,下面看看如何使用实现Runnable接口的方式创建线程:
- 定义子类:实现Runnable接口
- 子类重写run方法
- 通过Thread类含参构造器创建线程对象。
- 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
- 调用Thread类的start方法:开启线程
// 窗口卖票,总有100张
// 共开启三个窗口
class Window implements Runnable
{
private static int ticket = 100;
@Override
public void run() {
while (true) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ":" + ticket--);
} else {
break;
}
}
}
}
public class WindowTest {
public static void main(String[] args) {
Window window = new Window();
Thread thread1 = new Thread(window);
Thread thread2 = new Thread(window);
Thread thread3 = new Thread(window);
thread1.setName("window1");
thread2.setName("window2");
thread3.setName("window3");
thread1.start();
thread2.start();
thread3.start();
}
}
Callable
与使用Runnable相比, Callable功能更强大些
- 相比run()方法,可以有返回值
- 方法可以抛出异常
- 支持泛型的返回值
- 需要借助FutureTask类,比如获取返回结果
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V>
FutureTask是Future接口的唯一实现类。Future接口可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。 FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
使用实现Callable创建线程的步骤:
- 创建一个实现Callable接口的类
- 创建Callable接口的实现类的对象
- 将此Callable接口实现类的对象传递到FutureTask构造器中,创建FutureTask的对象
- 将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
- 如果需要获取Callable中call方法的返回值,则调用FutureTask的get方法获取。
class Window3 implements Callable
{
private static int ticket = 100;
@Override
public Object call() {
while (true) {
if (ticket > 0) {
// 模拟卖票业务,每次卖票耗时0.1s
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":票号" + ticket--);
} else {
break;
}
}
return null;
}
}
public class Window3Test {
public static void main(String[] args) {
FutureTask[] tasks = new FutureTask[3];
for (int i = 0; i < 3; i++)
{
Callable callable = new Window3();
tasks[i] = new FutureTask(callable);
Thread t = new Thread(tasks[i]);
t.start();
}
}
}
线程池
经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。提前 创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
优点:
提高响应速度(减少了创建新线程的时间)
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
便于线程管理
- corePoolSize:核心池的大小
- maximumPoolSize:最大线程数
- keepAliveTime:线程没有任务时最多保持多长时间后会终止
在实际开发中,需要建立多线程的情况下,基本都是使用创建线程池方法
JDK 5.0起提供了线程池相关API:ExecutorService 和 Executors
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
- void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable
-
Future submit(Callable task):执行任务,有返回值,一般用来执行Callable - void shutdown() :关闭连接池
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
- Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
- Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池
- Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池
- Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
class ThreadNum implements Runnable
{
@Override
public void run() {
for (int i = 2; i <= 100 ; i ++) {
int j;
for ( j = 2; j <=(int) Math.sqrt(i); j++) {
if (i % j == 0) {
break;
}
}
if (j > (int) Math.sqrt(i)) {
System.out.println(i);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
//1. 提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
//设置线程池的属性
// System.out.println(service.getClass());
// service1.setCorePoolSize(15);
// service1.setKeepAliveTime();
//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
service.execute(new ThreadNum());//适合适用于Runnable
service.execute(new ThreadNum());//适合适用于Runnable
// service.submit(Callable callable);//适合使用于Callable
//3.关闭连接池
service.shutdown();
}
}