线程的创建
1 认识线程
1.1 什么是线程
从操作系统角度来看,线程是系统调度的最小单位
从用开发者的角度来看,线程是一个分担任务的角色
1.2 线程和进程的区别
- 进程是系统分配资源的最小单位,线程是系统调度的最小单位,也是进程执行的最小单位
- 多进程是不能共享资源的,一个进程内的线程之间是可以共享资源的
- 线程必须依附在进程之中,进程好比工厂,线程好比流水线
优点:创建的时候占用更少的资源,并且多个线程之间可以共享资源。
线程可以共享资源:打开的文件、内存(对象)
线程不可共享资源:上下文、记账信息、状态、优先级、线程的栈空间
线程的数量
线程的数量不是越多越好,因为线程之间的争抢和 CPU 的过度调度是需要消耗系统资源的
那多少线程是最好的?
要看具体的场景,当使用的场景是密集型计算 CPU 任务时,线程的数量等于CPU 是最好的, IO(文件读写)型任务是越多越好
1.3 线程的创建
- 继承 Thread 类是实现线程创建
缺点: Java 语言的设计中只能实现单继承,如果继承了 Thread 类也就代表不能继承其他类了
static class MyThread extends Thread {
@Override
public void run() {
// 线程执行任务
System.out.println("线程名称:" +
Thread.currentThread().getName());
}
}
public static void main(String[] args) {
Thread t1 = new MyThread();
t1.start();
}
使用匿名类创建 Thread 子类对象
public static void main(String[] args) {
Thread thread = new Thread() {
@Override
public void run() {
// 线程执行任务
System.out.println("线程名称:" +
Thread.currentThread().getName());
}
};
thread.start();
}
- 实现 Runnable 接口的方式(Java不能多继承,但可以实现多个接口)
static class MyRunnable implements Runnable {
@Override
public void run() {
// 线程执行任务
System.out.println("线程名称:" +
Thread.currentThread().getName());
}
}
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start();
}
使用匿名内部类方式实现线程创建
public static void main(String[] args) {
// 匿名内部类
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程名称:" +
Thread.currentThread().getName());
}
});
thread.start();
}
lambda(jdk 1.8) + 匿名 runnable 的实现
public static void main(String[] args) {
// lambda(jdk 1.8) + 匿名 runnable 的实现
Thread thread = new Thread(() -> {
System.out.println("线程名称:" +
Thread.currentThread().getName());
});
thread.start();
}
- 实现 Callable 接口(可以得到线程执行之后的结果)
// 实现 Callable
static class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int num = new Random().nextInt(10);
System.out.println("子线程:" + Thread.currentThread().getName() +
", 随机数:" + num);
return num;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable();
// FuntureTask 容器接受返回值 FuntureTask 是 Runnable 的子类
FutureTask<Integer> futureTask = new FutureTask<>(myCallable);
Thread thread = new Thread(futureTask);
thread.start();
int res = futureTask.get();
System.out.println(String.format("线程名: %s ,数字: %d", Thread.currentThread().getName(), res));
}
使用匿名内部类方式实现线程创建:
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable();
FutureTask<Integer> futureTask = new FutureTask<>(new Callable() {
@Override
public Object call() throws Exception {
int num = new Random().nextInt(10);
System.out.println("子线程:" + Thread.currentThread().getName() +
", 随机数:" + num);
return num;
}
});
Thread thread = new Thread(futureTask);
thread.start();
int res = futureTask.get();
System.out.println(String.format("线程名:%s, 线程返回数字: %d", Thread.currentThread().getName(), res));
}
2. 例子
2.1 对比单线程和多线程 1000w 次循环使用的时间
public class ThreadDemo2 {
private static final Long count = 100_000_000L;
public static void main(String[] args) throws InterruptedException {
// 对比单线程和多线程 1000w 次循环使用的时间
concorrency();
serial();
}
// 单线程的方法
private static void serial() {
Long sTime = System.currentTimeMillis();
//System.nanoTime();// ns
int a = 0;
for (int i = 0; i < count * 3; i++) {
a++;
}
Long eTime = System.currentTimeMillis();
System.out.println("单线程运行时间:" + (eTime - sTime));
}
// 多线程的方法
private static void concorrency() throws InterruptedException {
Long sTime = System.currentTimeMillis();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
int a = 0;
for (int i = 0; i < count; i++) {
a++;
}
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
int b = 0;
for (int i = 0; i < count; i++) {
b++;
}
}
});
运行结果:
因为多线程本身所占用资源开销,所以 3个线程所花费的时间大于 单个线程花费时间是 1/3 倍
2.2 使用两个线程打印按顺序 AABBCCDD
public class ThreadDemo3 {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
String data = "ABCD";
for (char ch : data.toCharArray()) {
System.out.print(ch);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread t1 = new Thread(runnable);
t1.start();
Thread t2 = new Thread(runnable);
t2.start();
}
}
运行结果: