目录
Java 进程和线程
基础概念
启动进程的方法
僵尸进程和孤儿进程
多线程
线程的编程4种实现方法
1、继承Thread
2、实现Runnable接口
3、使用Callable和Future接口创建线程
泛型简述
4、使用线程池创建线程
线程进程的五个阶段
线程的优先级
Thread 方法
程序是为完成特定任务、用某种语言编写的一组指令的集合。指一段静态的代码,是一个静态的概念。
进程(Process)具有一定独立功能程序的运行过程,是系统进行资源分配和调度的一个独立单位,重点在系统调度和单独的单位,也就是说进程是可以独立运行的一段程序。
进程的特征:
package xiancheng;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class D06 {
public static void main(String[] args) throws IOException {
//启动进程方法一
ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "ipconfig/all"); // 用于构建进程的对象
Process p = pb.start();// 启动进程
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));// 获取进程的输出内容
String temp = null;
while ((temp = br.readLine()) != null)
System.out.println(temp);
//启动进程方法二
String cmd = "cmd " + "/c " + "ipconfig/all";
Process process = Runtime.getRuntime().exec(cmd);
BufferedReader br1 = new BufferedReader(new InputStreamReader(process.getInputStream()));
String temp1 = null;
while ((temp1 = br1.readLine()) != null)
System.out.println(temp1);
}
}
package xiancheng;
public class D07 {
public static void main(String[] args) {
Thread a = new Thread() {// 写个内部类默认继承Thread
public void run() {
// Thread.currentThread()获取当前正在运行的线程对象
// .getName()获取线程对象的标识名称
System.out.println(Thread.currentThread().getName());
}
};
a.start();// 启动子线程
for(int i =0;i<15;i++)
System.out.println(Thread.currentThread().getName()+i);// 输出的是主线程的
}
}
不一定,如果针对密集型计算的应用使用单线程避免多线程中的切换反而会提高代码的运行效率。资源限制的挑战,在并发编程时需要考虑到资源上的限制。如果受制于资源,整体程序的速度肯定会慢下来。
Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。
public class Thread implements Runnable@FunctionalInterface // 函数式接口,其中包含一个抽象方法 runpublic interface Runnable {public abstract void run ();}
package xiancheng;
public class D04 extends Thread {
private int i;
public void run() {
for (; i < 50; i++) {
System.out.println(getName() + " " + i);
}
}
public static void main(String[] args) {//主线程
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 20) {
new D04().start();//开启一个线程
}
if (i == 30) {
new D04().start();//开启一个线程
}
}
}
}
@FunctionalInterface // 函数式接口,简化定义方式为 lambda 表达式public interface Runnable {public abstract void run ();}
package xiancheng;
public class D03 implements Runnable {
private int i;
public void run() {
for (; i < 30; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
public static void main(String[] args) {
for (int i = 0; i < 30; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 20) {
D03 s = new D03();
new Thread(s, "新线程1").start();
new Thread(s, "新线程2").start();
}
}
}
}
可以使用lambda表示式进行定义,或者使用匿名内部类进行定义
package xiancheng;
public class D08 {
public static void main(String[] args) {
// 匿名内部类的写法
Thread a = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "--" + i);
}
}
});
// lambda表达式的写法---要求接口必须是函数式接口
Thread b = new Thread(() -> {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "--" + i);
}
});
a.start();//进入就绪态,调用run方法进入运行态
b.start();
}
}
@FuntionalInterface // 属于函数式接口,所以可以直接使用 lambda 表达式进行定义public interface Callable < V > { //<> 写法叫做泛型,用于定义返回的数据类型V call () throws Exception ;}
1. 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。
2. 创建 Callable 实现类的实例,使用 FutureTask 实现类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
3. 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
4. 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。
@FuntionalInterface // 属于函数式接口,所以可以直接使用 lambda 表达式进行定义public interface Callable < V > { //<> 写法叫做泛型,用于定义返回的数据类型V call () throws Exception ;}
Java泛型设计原则:只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常;
泛型:把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型;
泛型类/接口就是把泛型定义在类上,用户使用该类的时候,才把类型明确下来,这样的话,用户明确了什么类型,该类就代表着什么类型,用户在使用的时候就不用担心强转的问题,运行时转换异常的问题了。
在类或者接口上定义的泛型,在类的方法中也可以使用!
package xiancheng;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;//public class FutureTask implements RunnableFuture {}
import java.util.concurrent.RunnableFuture;
public class D09 implements Callable {// 实现泛型接口,在实现时候确定
public Integer call() throws Exception {
int i = 0;
for (; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
return i;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask ft = new FutureTask<>(new D09());
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 20) {
new Thread(ft, "新线程").start();// 启动一个子线程
}
}
System.out.println(ft.get());//获取对应的线程对象的执行结果。
}
}
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。
Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。
Thread 定义了其中 3 个常数:
(1) MAX_PRIORITY, 最大优先级(值为10);
(2)MIN_PRIORITY,最小优先级(值为1);
(3)NORM_PRIORITY,默认优先级,(值为5),注意是Java主线程默认的优先级是 5 。
默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。
具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。
package xiancheng;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.sun.corba.se.impl.orbutil.closure.Future;
public class D10 {
public static void main(String[] args) {
// 创建一个固定大小的连接池,经常使用少量线程以应对波峰请求
ExecutorService es = Executors.newFixedThreadPool(3);// 固定线程池
// 为了使用返回结果所以使用Callable
Future[] fs = new Future[10];
for (int i = 0; i < fs.length; i++) {
Callable caller = new MyCallable(i * 1000 + 1, (i + 1) * 1000);
//使用线程池执行任务并获取Future对象
fs[i] = (Future) es.submit(caller);
}
int res= 0;
for(int i = 0;i {
private int begin;
private int end;
public MyCallable(int begin, int end) {
this.begin = begin;
this.end = end;
}
public Integer call() throws Exception {
System.out.println(Thread.currentThread() + "----" + begin + ".." + end);
int res = 0;
for (int i = begin; i <= end; i++)
res += 1;
return res;
}
}