这次我们先来看下Thread 方法。下表列出了Thread类的一些重要方法:
序号 | 方法描述 |
1 | public void start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。 |
2 | public void run() 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。 |
3 | public final void setName(String name) 改变线程名称,使之与参数 name 相同。 |
4 | public final void setPriority(int priority) 更改线程的优先级。 |
5 | public final void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。 |
6 | public final void join(long millisec) 等待该线程终止的时间最长为 millis 毫秒。 |
7 | public void interrupt() 中断线程。 |
8 | public final boolean isAlive() 测试线程是否处于活动状态。 |
测试线程是否处于活动状态。 上述方法是被Thread对象调用的。下面的方法是Thread类的静态方法:
序号 | 方法描述 |
1 | public static void yield() 暂停当前正在执行的线程对象,并执行其他线程。 |
2 | public static void sleep(long millisec) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 |
3 | public static boolean holdsLock(Object x) 当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。 |
4 | public static Thread currentThread() 返回对当前正在执行的线程对象的引用。 |
5 | public static void dumpStack() 将当前线程的堆栈跟踪打印至标准错误流。 |
如下的ThreadClassDemo 程序演示了Thread类的一些方法:
// 文件名 : DisplayMessage.java
// 通过实现 Runnable 接口创建线程
public class DisplayMessage implements Runnable {
private String message;
public DisplayMessage(String message) {
this.message = message;
}
public void run() {
while(true) {
System.out.println(message);
}
}
}
// 文件名 : GuessANumber.java
// 通过继承 Thread 类创建线程
public class GuessANumber extends Thread {
private int number;
public GuessANumber(int number) {
this.number = number;
}
public void run() {
int counter = 0;
int guess = 0;
do {
guess = (int) (Math.random() * 100 + 1);
System.out.println(this.getName() + " guesses " + guess);
counter++;
} while(guess != number);
System.out.println("** Correct!" + this.getName() + "in" + counter + "guesses.**");
}
}
// 文件名 : ThreadClassDemo.java
public class ThreadClassDemo {
public static void main(String [] args) {
Runnable hello = new DisplayMessage("Hello");
Thread thread1 = new Thread(hello);
thread1.setDaemon(true);
thread1.setName("hello");
System.out.println("Starting hello thread...");
thread1.start();
Runnable bye = new DisplayMessage("Goodbye");
Thread thread2 = new Thread(bye);
thread2.setPriority(Thread.MIN_PRIORITY);
thread2.setDaemon(true);
System.out.println("Starting goodbye thread...");
thread2.start();
System.out.println("Starting thread3...");
Thread thread3 = new GuessANumber(27);
thread3.start();
try {
thread3.join();
}catch(InterruptedException e) {
System.out.println("Thread interrupted.");
}
System.out.println("Starting thread4...");
Thread thread4 = new GuessANumber(75);
thread4.start();
System.out.println("main() is ending...");
}
}
运行结果如下,每一次运行的结果都不一样:
Starting hello thread...
Starting goodbye thread...
Hello
Hello
Hello
Hello
Hello
Hello
Goodbye
Goodbye
Goodbye
Goodbye
Goodbye
.......
这些完事了之后,我们就来看下线程池。
1、线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。(是什么)。
2、那么,我们为什么需要用到线程池呢?每次用的时候手动创建不行吗?在java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。为了防止资源不足,需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务。(为什么)。
线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快;另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。(什么用)
3、线程池都是通过线程池工厂创建,再调用线程池中的方法获取线程,再通过线程去执行任务方法:
- Executors:线程池创建工厂类
- public static ExecutorServicenewFixedThreadPool(int nThreads):返回线程池对象
- ExecutorService:线程池类
- Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行
- Future 接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
4、这里介绍两种使用线程池创建线程的方法,首先是使用Runnable接口创建线程池。使用线程池中线程对象的步骤如下:
- 1、创建线程池对象
- 2、创建 Runnable 接口子类对象
- 3、提交 Runnable 接口子类对象
- 4、关闭线程池
Test.java 代码如下:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] args) {
//创建线程池对象 参数5,代表有5个线程的线程池
ExecutorService service = Executors.newFixedThreadPool(5);
//创建Runnable线程任务对象
TaskRunnable task = new TaskRunnable();
//从线程池中获取线程对象
service.submit(task);
System.out.println("----------------------");
//再获取一个线程对象
service.submit(task);
//关闭线程池
service.shutdown();
}
}
TaskRunnable.java 接口文件如下:
public class TaskRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("自定义线程任务在执行"+i);
}
}
}
其次我们来使用Callable接口创建线程池。Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常:
ExecutorService:线程池类
<T> Future<T> submit(Callable<T> task):获取线程池中的某一个线程对象,并执行线程中的 call() 方法
Future 接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
使用线程池中线程对象的步骤如下:
- 1、创建线程池对象
- 2、创建 Callable 接口子类对象
- 3、提交 Callable 接口子类对象
- 4、关闭线程池
Test.java 代码如下:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test{
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(3);
TaskCallable c = new TaskCallable();
//线程池中获取线程对象,调用run方法
service.submit(c);
//再获取一个
service.submit(c);
//关闭线程池
service.shutdown();
}
}
TaskCallable.java 接口文件如下:
import java.util.concurrent.Callable;
public class TaskCallable implements Callable<Object>{
@Override
public Object call() throws Exception {
for (int i = 0; i < 1000; i++) {
System.out.println("自定义线程任务在执行"+i);
}
return null;
}
}
这些完事之后,我们来看下线程池练习,就是返回两个数相加的结果。要求就是通过线程池中的线程对象,使用Callable接口完成两个数求和操作。
Future 接口就是用来记录线程任务执行完毕后产生的结果。线程池创建与使用,就是使用get() 获取 Future对象中封装的数据结果。ThreadPoolDemo.java 文件代码如下:
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ThreadPoolDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//创建线程池对象
ExecutorService threadPool = Executors.newFixedThreadPool(2);
//创建一个Callable接口子类对象
//MyCallable c = new MyCallable();
MyCallable c = new MyCallable(100, 200);
MyCallable c2 = new MyCallable(10, 20);
//获取线程池中的线程,调用Callable接口子类对象中的call()方法, 完成求和操作
//<Integer> Future<Integer> submit(Callable<Integer> task)
// Future 结果对象
Future<Integer> result = threadPool.submit(c);
//此 Future 的 get 方法所返回的结果类型
Integer sum = result.get();
System.out.println("sum=" + sum);
//再演示
result = threadPool.submit(c2);
sum = result.get();
System.out.println("sum=" + sum);
//关闭线程池(可以不关闭)
}
}
MyCallable.java 接口文件代码如下:
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
//成员变量
int x = 5;
int y = 3;
//构造方法
public MyCallable(){
}
public MyCallable(int x, int y){
this.x = x;
this.y = y;
}
@Override
public Integer call() throws Exception {
return x+y;
}
}
练习完事之后就来看下进程和线程的区别。进程就是应用程序的执行实例,有独立的内存空间和系统资源。线程呢,就是CPU调度和分派的基本单位,进程中执行运算的最小单位,可完成一个独立的顺序控制流程。再来看下进程和线程的关系:
(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程是操作系统可识别的最小执行和调度单位。
(2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。 同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。
(3)处理机分给线程,即真正在处理机上运行的是线程。
(4)线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
好啦,这次分享到这里就结束了。如果感觉不错的话,请多多点赞支持哦。。。