进程与线程的由来
进程:最开始计算机发展时期,所有的指令都是串行执行,如果遇到用户IO指令,则系统等待用户IO完成之后继续执行指令,极大的浪费CPU时间,为缓解该情况了,引入了指令集的概念,把需要执行的指令都写到一个磁盘上,让操作系统运行,这样批处理操作系统就诞生了,紧接着又遇到一个问题,若一个程序中有任务A与任务B,任务A执行需要大量的IO操作,按照当时的批处理操作系统,仍会等待A,浪费CPU资源,故而想在系统中引入多个程序,那程序之间如何区分,就引入了进程的概念,方便任务A等待IO的过程中,任务B可以抢占CPU,等任务A的IO操作完成,A继续执行,进程是分配资源的最小单位
线程:线程是为了进程内部并发,提高程序的执行效率
进程与线程的区别?
- 进程是系统分配资源的最小单位,线程是cpu调度的最小单位
- 进程拥有独立的地址空间,一个进程包含多个线程,多个线程共享地址空间
- 在java中一个进程对应一个JVM对象实例,多个线程运行在jvm中
线程的Start与Run方法的区别
Start方法会单独启动一个线程执行对应的Run方法
直接调用Run()方法,相当于在主线程中调用Run方法
package com.bdcloud.thread;
public class ThreadTest {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.run();
System.out.println("------------------------");
thread.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
//执行结果如下
Connected to the target VM, address: '127.0.0.1:6910', transport: 'socket'
main
------------------------
Thread-0
Disconnected from the target VM, address: '127.0.0.1:6910', transport: 'socket'
Process finished with exit code 0
实现多线程的三种方式
- 继承Thread实现run方法
- 实现Runable接口,实现run方法
- 实现Callable接口,借助FutureTask类,该类本质还是继承了Runable接口,间接的实现了Runable接口
//Callable的示例
public class MyCallbale implements Callable<String> {
public String call() throws Exception {
String value = "every thing";
System.out.println("Task is Reading");
Thread.sleep(5000);
System.out.println("Task Done!");
return value;
}
}
//使用Callable实现多线程
package com.bdcloud.thread;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class FeatureTaskDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask<String>(new MyCallbale());
new Thread(futureTask).start();
if (!futureTask.isDone()){
System.out.println("futureTask is not down1");
}
System.out.println(futureTask.get());
}
}
//通过线程池获取线程返回值
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ThreadPoolTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
Future<String> submit = executorService.submit(new MyCallbale());
if (!submit.isDone()){
System.out.println("futureTask is not down1");
}
System.out.println(submit.get());
}
}
线程的状态
- 新建状态(New):线程被创建,但是未执行start方法
- 运行(Runable):分为俩种,Running与Ready,Running状态为获取CPU使用权且在执行,ready为等待Cpu时间
- 无线等待(Waiting):不会被分配CPU执行时间,需要显示唤醒,例如在synchronize中调用Object.wait()方法
- 超时等待(Time Waiting):在一定时间内由系统自动唤醒,例如在Synchronized中调用Object.wait(1000)方法
- 阻塞状态(Blocked):等待获取排它锁
- 结束(Terminated):已终止线程的状态,线程已经结束执行
线程Sleep与Wait的区别
- Sleep()是Thread中的方法,Wait是Object中的方法
- Sleep可以在代码任何部分执行,而Wait仅可以在Synchronize修饰的方法或代码块中执行
- Sleep仅会让出Cpu时间,不会对锁有改变,Wait同时释放锁跟CPU
Wait与Sleep的Demo
package com.bdcloud.thread;
import lombok.Synchronized;
import javax.sound.midi.Soundbank;
/**
* 测试Sleep与Wait方法
*/
public class SleepWaitDemo {
//锁对象
private static Object lock = new Object();
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程A开始执行,等待获取锁lock");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock) {
try {
System.out.println("线程A获取到锁lock");
lock.wait(100);
System.out.println("线程A执行结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程B开始执行,等待获取锁lock");
synchronized (lock){
try {
System.out.println("线程B开始执行,已经获取锁lock");
Thread.sleep(100);
System.out.println("线程B执行完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
如何获取线程的返回值
第一种,采用主线程等待法
package com.bdcloud.thread;
public class CycleWait implements Runnable {
private String value;
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
value = "循环等待赋值value";
}
public static void main(String[] args) {
CycleWait cycleWait = new CycleWait();
new Thread(cycleWait).start();
//循环等待
while (cycleWait.value == null){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("value值="+cycleWait.value);
}
}
第二种,只用Thread的join方法,阻塞主线程等待子线程执行结束,缺点控制力度不够细
package com.bdcloud.thread;
public class CycleWait implements Runnable {
private String value;
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
value = "循环等待赋值value";
}
public static void main(String[] args) throws InterruptedException {
CycleWait cycleWait = new CycleWait();
Thread thread = new Thread(cycleWait);
thread.start();
//阻塞主线程等待子线程执行结束
thread.join();
System.out.println("value值="+cycleWait.value);
}
}
第三种,通过Callable接口实现,通过FutureTask或者线程池获取
package com.bdcloud.thread;
import java.util.concurrent.Callable;
/***
* 定义Callable
*/
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("task is ready to work!");
Thread.sleep(100);
System.out.println("task is Done!");
return "Hello MyCallable!";
}
}
/***
* Future 接收Callable的返回值
*/
package com.bdcloud.thread;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class FutureTaskTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable callable = new MyCallable();
//使用FutureTask接收Callable返回的结果
FutureTask<String> task = new FutureTask<String>(callable);
//初始化线程
Thread thread = new Thread(task);
thread.start();
while (!task.isDone()){
System.out.println("task is not finished");
}
System.out.println(task.get());
}
}
package com.bdcloud.thread;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/***
* 使用线程池接收Callbale返回值
*/
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(1);
//通过线程池提交任务创建子线程执行
Future<String> submit = executorService.submit(new MyCallable());
if (!submit.isDone()){
System.out.println("子线程尚未返回Callable的值");
}
try {
System.out.println(submit.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}finally {
executorService.shutdown();
}
}
}
notify与notyfyAll的区别
首先引入锁池与等待池的概念
-
锁池
锁池是当前线程处于等待锁的状态,例如执行到synchronized的方法块时,该锁被另一个线程占用,则自动把该线程放入锁池中,等待竞争CPU资源执行
-
等待池
等待池是指在Synchronized代码块中调用lock.wait()方法,线程进入无限等待状态,不会竞争CPU资源
notify方法:其会从等待池中随机唤醒一个线程
notifyAll方法:唤醒等待池中的所有线程进入锁池中
yield方法
暗示Cpu当前线程愿意让出Cpu时间,但是调度器不一定会中断此线程,yield方法不会释放锁
package com.bdcloud.thread;
/***
* yield测试,运行结果线程A执行为yield()方法后并未让出CPU
*/
public class yieldDemo {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+i);
if (i == 5){
//暗示调度器该线程愿意让出Cpu
Thread.yield();
}
}
}
};
Thread a = new Thread(runnable,"A");
Thread b = new Thread(runnable,"B");
a.start();
b.start();
}
}
interrupt方法
之前终止一个线程,调用stop方法,但是该方法调用容易导致资源尚未释放完整就中断线程 ,现在推荐使用
interrupt方法,该方法调用后会设置该线程的中断标志为true,但是具体中断是由线程自主决定。若当前线程处于等待状态,则抛出异常,线程终止,若线程设置中断标志为true的时候,但还会继续执行