多线程问题
进程和线程
1. 多线程是干什么的?
1)发挥多核cpu的作用,真正的让多段逻辑同时工作 (2)防止阻塞 (3)便于设计(建模)
2. 什么是进程和线程(进程和线程的区别)
进程就是一整个程序,例如,打开某个app,相当于开启了一个进程 线程是技术实现的,在代码实现中开启多个线程
区别: 调度:线程是进行调度和分配的基本单位,进程是拥有资源的基本单位 (内存为一个程序分配一部分空间,由真正执行程序的线程去调度) 并发性:进程和线程都可以并发执行 拥有资源:进程是拥有资源的独立单位,线程可以访问隶属于进程的资源 系统开销:在创建和撤销进程时,系统都要为进程分配和回收资源,导致系统的开销明显大于创建和撤销线程时的开销
3.创建线程的方式有哪些?
(1)继承thread类
step 1 定义一个类继承自 Thread 类,然后重写该类的 run 方法,这个方法的内容表示线程要完成的任务 step 2 创建线程对象,即创建 Thread 类子类的实例 step 3 调用步骤二中创建出来的对象的 start 方法来启动线程
/**
* @author mghio
* @date: 2019-12-07
* @version: 1.0
* @description: 通过继承 Thread 类的方式创建线程
* @since JDK 1.8
*/
public class CreateThreadByExtendsThread extends Thread {
@Override
public void run() {
IntStream.rangeClosed(1, 10).forEach(i -> System.out.println(Thread.currentThread().getName() + " " + i));
}
public static void main(String[] args) {
CreateThreadByExtendsThread threadOne = new CreateThreadByExtendsThread();
CreateThreadByExtendsThread threadTwo = new CreateThreadByExtendsThread();
CreateThreadByExtendsThread threadThree = new CreateThreadByExtendsThread();
threadOne.start();
threadTwo.start();
threadThree.start();
}
}
(2)实现Runnable接口
step 1 定义一个类实现 Runnable 接口,然后实现该接口的 run 方法,这个方法的内容同样也表示线程要完成的任务 step 2 创建 Runnable 接口实现类的实例,并使用该实例作为 Thraed 构造方法的参数创建 Thread 类的对象,该对象才是真正的线程对象 step 3 调用线程对象的 start 方法来启动该线程
/**
* @author mghio
* @date: 2019-12-07
* @version: 1.0
* @description: 通过实现 Runnable 接口的方式创建线程
* @since JDK 1.8
*/public class CreateThreadByImplementsRunnable implements Runnable {
@Override
public void run() {
IntStream.rangeClosed(1, 10).forEach(i -> System.out.println(Thread.currentThread().getName() + " " + i));
}
public static void main(String[] args) {
CreateThreadByImplementsRunnable target = new CreateThreadByImplementsRunnable();
new Thread(target, "thread-one").start();
new Thread(target, "thread-two").start();
new Thread(target, "thread-three").start();
}
}
(3).实现Callable接口
step 1 定义一个类实现 Callable 接口,然后实现该接口的 call 方法,这个方法的内容同样也表示线程要完成的任务,并且有返回值 step 2 创建 Callable 接口实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了 Callable 对象的 call 方法的返回值 step 3 并使用 FutureTask 对象作为 Thraed 构造方法的参数创建 Thread 对象,并调用该对象的 start 方法启动线程 step 4 调用 FutureTask 对象的 get 方法获取线程执行结束后的返回值
/**
* @author mghio
* @date: 2019-12-07
* @version: 1.0
* @description: 通过实现 Callable 接口的方式创建线程
* @since JDK 1.8
*/public class CreateThreadByImplementsCallable implements Callable<Integer> {
@Override
public Integer call() {
AtomicInteger count = new AtomicInteger();
IntStream.rangeClosed(0, 10).forEach(i -> {
System.out.println(Thread.currentThread().getName() + " " + i);
count.getAndIncrement();
});
return count.get();
}
public static void main(String[] args) {
CreateThreadByImplementsCallable target = new CreateThreadByImplementsCallable();
FutureTask<Integer> futureTask = new FutureTask<>(target);
IntStream.rangeClosed(0, 10).forEach(i -> {
System.out.println(Thread.currentThread().getName() + " 的循环变量 i 的值" + i);
if (i == 8) {
new Thread(futureTask, "有返回值的线程").start();
}
});
try {
System.out.println("有返回值线程的返回值:" + futureTask.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
4、Runnable接口和Callable接口的区别
Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。
5.Thread 类中的start() 和 run() 方法有什么区别?
1.start()方法来启动线程,真正实现了多线程运行。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码;通过调用Thread类的start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。然后通过此Thread类调用方法run()来完成其运行操作的, 这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。然后CPU再调度其它线程。 2.run()方法当作普通方法的方式调用。程序还是要顺序执行,要等待run方法体执行完毕后,才可继续执行下面的代码;程序中只有主线程——这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的。
7.java中Lock接口比synchronized块的优势
8.java中的wait和sleep方法的不同
wait会释放锁,常被用于线程间的交互 sleep会一直持有锁,sleep通常被用于暂停执行
9.java多线程基础概念
java的多线程锁是挂在对象上的,并不是在方法上的,即每个对象都有 一个锁,当遇到synchronized的同步需要时,就会监视每个想使用本对象的线程按照一定的规则来访问,规则就是同一时间内只能有一个线程能访问此对象
java中获取锁的单位是线程,当线程a获取对象b的锁,也就是对象b的持有标记