1.线程创建的2种方式及其区别
第一种方式:实现Runnable接口
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("执行的第"+(i+1)+'次');
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
System.out.println("主线程开始执行了");
MyRunnable mr = new MyRunnable();
Thread thread = new Thread(mr);
thread.start();
System.out.println("主线程结束执行了");
}
}
第二种方式:继承Thread类
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("打印第"+i+"次");
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
System.out.println("主线程开始执行了");
MyThread mt = new MyThread();
mt.start();
System.out.println("主线程结束执行了");
}
}
区别:多个线程需要共享同一数据时候需要使用Runnable接口形式;比如,多个窗口同时卖电影票案例(涉及线程安全,需要同步).
2.线程中断(线程中断,设置标记)
1.线程中断:使用interrupt方法中断线程:
注意事项:1)如果判断的线程代码里面有sleep(),那么中断就会失效,因为sleep里面有InterruptedException;
/*--------------- 使用interrupt方法中断线程 -------------------------*/
public class Main {
public static void main(String[] args) {
ThreadInterrupt ti = new ThreadInterrupt();
ti.start();
/*--------------- 需求:主线程睡2000ms后结束ti线程 -----------------------*/
try {
Thread.sleep(30L);
ti.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadInterrupt extends Thread {
private Long a = 0L;
@Override
public void run() {
for (int i = 0; i < 6000; i++) {
if (isInterrupted())return;
System.out.println("打印第"+(i+1)+"次");
//假如下面的sleep没有被注释,那么上面的if (isInterrupted())return;就不会起作用
/* try {
Thread.sleep(500L);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
/* while (true){
if (isInterrupted())return;
System.out.println("哒哒哒"+a);
a++;
}*/
}
}
2.线程中断:自定义标记中断线程:
注意事项:1)如果线程里面有sleep()方法,那么就自定义标记来中断线程
步骤:1.在继承类或者实现类中定义一个旗帜变量(Boolean类型);
2.增加该变量的set方法;
3.在main方法中,调用该变量set方法,置为true或者false,再在子类的线程中进行判断然后return;
/*------------------- 自定义标记中断线程 --------------------------------*/
public class Main {
public static void main(String[] args) {
ThreadInterrupt ti = new ThreadInterrupt();
ti.start();
/*------------------需求:主线程睡2000ms后结束ti线程------------------------*/
try {
Thread.sleep(3000L);
ti.setNeedInterrupt(true);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadInterrupt extends Thread {
private Boolean needInterrupt = false;
public void setNeedInterrupt(Boolean needInterrupt) {
this.needInterrupt = needInterrupt;
}
@Override
public void run() {
for (int i = 0; i < 6000; i++) {
if (needInterrupt) return;
System.out.println("打印第" + (i + 1) + "次");
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3.线程join
join:就是将并行线程合并为串行线程.
public class ThreadJoin {
public static void main(String[] args) {
System.out.println("主线程开始");
MyThread mt = new MyThread();
mt.start();
try {
mt.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程结束");
}
}
//注意观察程序运行结果
4.守护线程
setDaemon():随着主线程的死亡而死亡(不会立即死亡,具体死亡时间不可控)
注意:因为守护线程会跟着主线程的死亡而死亡.如果不想守护线程死亡太早,可以让主线程睡眠,或者将守护线程串行到主线程!
public class main {
public static void main(String[] args) {
System.out.println("主线程开始执行");
MyThread mt = new MyThread();
mt.setDaemon(true);
mt.start();
/*try {
mt.join();
} catch (InterruptedException e) {
e.printStackTrace();
}*/
/*try {
Thread.sleep(2000L);//主线程睡
} catch (InterruptedException e) {
e.printStackTrace();
}*/
System.out.println("主线程结束执行");
}
}
5.线程池
程序启动一个线程的成本比较高,因为涉及到要与操作系统进行交互,而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生命周期很短的线程时,更应该考虑线程池,线程池每个线程结束后,并不会死,而是到线程池中进入等待状态;
根据核数定义线程池中线程数量:Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1)
public class MainApp {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
ExecutorService service = Executors.newFixedThreadPool(5);
//定义线程池数量的一种方式:使用核数加减1;
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1);
service.execute(mr);//无返回值
service.submit(mr);//有返回值
Future<?> submit = service.submit(mr);
System.out.println(submit);//java.util.concurrent.FutureTask@7ea987ac
}
}
6.线程创建方式3:CallAble方式
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallAble mca = new MyCallAble();
ExecutorService service = Executors.newFixedThreadPool(3);
service.execute(callAble);
//错误,execute()方法不能执行实现CallAble的类对象
Future<String> submitmca = service.submit(mca);
String strmca = submitmca.get();//返回CallAble接口中call()方法内的返回值
System.out.println(strmca);
//注意:通过实现CallAble的线程对象,只能放进线程池中执行!
//Thread th = new Thread(mca);CallAble
}
}
public class MyCallAble implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 0; i < 16; i++) {
System.out.println("执行的第"+i+"次");
Thread.sleep(100L);
}
return "执行了MyCallAble接口方法";
}
}
7.创建第一个协程
1.配置gradle环境]
group = "pers.lansir"
version = "1.0-SNAPSHOT"
plugins{
kotlin("jvm")
}
repositories{
mavenCentral()
jcenter()
}
dependencies{
compile(kotlin("stdlib"))
compile("org.jetbrains.kotlinx:kotlinx-coroutines-core:0.22.5")
}
2.协程创建:launch{}
import kotlinx.coroutines.experimental.launch
fun main(args: Array<String>) {
println("主线程开始执行")
launch {
(1..10).forEach {
println("打印第"+it+"次")
}
}
println("主线程结束执行")
Thread.sleep(300L)
}
8.launch函数分析
1.查看如下源代码,可知launch是一个函数,有四个参数,前三个是默认参数,第4个参数是函数类型;并且返回值类型是Job类型.
2.查看源码,可知CoroutineContext = DefaultDispatcher,也就是等价于=>CommonPool
3.查看commonPool发现,这其实就是一个线程池,并且是一个单例,继承CoroutineDispatcher(抽象类),ForkJoinPool又是CoroutineDispatcher的子类,因此,commonPool也就是ForkJoinPool线程池.
//1.lanuch函数查看
public expect fun launch(
context: CoroutineContext = DefaultDispatcher,
start: CoroutineStart = CoroutineStart.DEFAULT,
parent: Job? = null,
block: suspend CoroutineScope.() -> Unit
): Job
//2.lanuch函数的第一个参数
public actual val DefaultDispatcher:CoroutineDispatcher = CommonPool
9.ForkJoinPool和协程的正确启动方式
1.ForkJoinPool的Description : ForkJoinPool里面的线程时守护线程,跟着主线程的死亡而死亡
2.协程启动2种方式,1种join()串行,1种主线程sleep()
//1.ForkJoinPool举例说明
fun main(args: Array<String>) {
/*------------------------普通线程方式实现-------------------------*/
println("主线程开始执行")
val mr = MyRunnable()
/*val thread = Thread(mr)
thread.start()*/
//println("主线程结束执行")
/*------------------------ForkJoinPool--------------------------*/
val pool = ForkJoinPool(5)
pool.execute(mr)
println("主线程结束执行")
}
class MyRunnable:Runnable{
override fun run() {
(1..10).forEach {
println("打印"+it)
}
}
}
//2.正确启动协程的方法
fun main(args: Array<String>) = runBlocking{
//ForkJoinPool 每一个线程都是守护线程
val job = launch {
// println("hello")
(1..10).forEach {
println("打印"+it)
Thread.sleep(1000L)
}
}
/*-----------第一种方法: 让主线程睡眠一段时间--------------*/
// Thread.sleep(1000L)
/*------------------ 第二种方法:线程join()--------*/
job.join()
}
10.协程原理
1.遇到耗时任务,线程直接回到线程池中,如果有其它任务就执行其他任务;
2.耗时任务结束之后,从线程池中空闲的线程恢复执行(那个线程时处于空闲状态的,就那个线程去执行耗时后的任务)
3.耗时任务必须是挂起函数(比如delay()),如果是Thread的阻塞式任务,线程也不会回到线程池中执行其它任务(比如遇到sleep())
fun main(args: Array<String>) = runBlocking{
//是不是同一个线程池?是
val job = launch {
println("协程执行前线程:${Thread.currentThread().name}")
//耗时任务
// delay(2000)
Thread.sleep(2000L)
println("协程执行后线程:${Thread.currentThread().name}")
}
launch {
//耗时任务
// delay(2000)
Thread.sleep(2000L)
}
launch {
//耗时任务
// delay(2000)
Thread.sleep(2000L)
}
launch {
//耗时任务
// delay(2000)
Thread.sleep(2000L)
}
launch {
//耗时任务
// delay(2000)
Thread.sleep(2000L)
}
launch {
//耗时任务
// delay(2000)
Thread.sleep(2000L)
}
launch {
//耗时任务
// delay(2000)
Thread.sleep(2000L)
}
launch {
//耗时任务
// delay(2000)
Thread.sleep(2000L)
}
Thread.sleep(3000L)
}
11.挂起函数
1.suspend修饰的函数就是挂起函数,标记这个函数可以被挂起执行;
2.2.挂起函数只能在协程或者挂起函数里面执行;
3.标记成挂起函数后,这个函数可以被挂起执行,但是如果函数里面结果可以直接返回,自动决定不用再挂起;
fun main(args: Array<String>) = runBlocking{//主协程
val job = launch {
println("挂起前逻辑")
delay(1000)
println("挂起后逻辑")
}
job.join()
}
/**
* 挂起函数
*/
suspend fun myDelay(){
println("执行")
Thread.sleep(2000L)//不能挂起执行的
}
12.协程和线程效率对比
协程效率比线程高很多(因为协程有非阻塞模式,也就是遇到了耗时任务,可以不用一直等待,去执行其他任务)
fun main(args: Array<String>) = runBlocking{
val startTime = System.currentTimeMillis()
/*---------------------------- 线程 ----------------------------*/
//10万条线程 打印.
// val threadList = List<Thread>(100000){
// MyThread()
// }
// threadList.forEach { it.start() }
// threadList.forEach { it.join() }
// val endTime = System.currentTimeMillis()
// println("线程耗时:${endTime-startTime}")//9442
/*---------------------------- 协程 ----------------------------*/
//10万条协程 打印.
val coroutineList = List(100000){
launch {
println(".")
}
}
coroutineList.forEach { it.join() }
val endTime = System.currentTimeMillis()
println("线程耗时:${endTime-startTime}")//934
//后台开发
}
class MyThread:Thread(){
override fun run() {
println(".")
}
}
13.协程取消
fun main(args: Array<String>) = runBlocking {
//2000之后协程停下来
val job = launch {
(1..10).forEach {
println("打印:"+it)
delay(500L)
// Thread.sleep(500L)//cancle就失效了
}
}
Thread.sleep(2000L)
//协程取消
job.cancel()
job.join()
}
14.线程取消实例
class Main {
public static void main(String[] args) {
/*-------------- 使用interrupt方法中断线程 ------------------*/
/**
* 使用interrupt中断线程 需要在线程执行代码中通过isInterrupted进行线程状态的判断
* 线程里面有sleep 中断实效了 因为sleep会抛出InterruptedException
*/
MyThread thread = new MyThread();
thread.setName("线程1");
thread.start();
try {
Thread.sleep(3000L);
//停止子线程
// thread.stop();//太暴力
// thread.interrupt();
/*----------------- 自定义标记中断线程 -------------------*/
thread.setNeedInterrupt(true);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyThread extends Thread {
private int a = 0;
private boolean needInterrupt = false;
public void setNeedInterrupt(boolean needInterrupt) {
this.needInterrupt = needInterrupt;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if (needInterrupt) return;
System.out.println("打印" + i);
try {
sleep(1000L);
} catch (InterruptedException e) {
System.out.println("出现异常" + e.getMessage());
e.printStackTrace();
}
}
// while (true){
// //如果线程被中断就不用再执行了
// if(isInterrupted())return;
// System.out.println("打印数据了"+a);
// a++;
// }
}
}
15.线程取消失效的处理
通过isActive来判断
fun main(args: Array<String>) = runBlocking {
//2000之后协程停下来
val job = launch {
(1..10).forEach {
if(!isActive)return@launch
println("打印:"+it)
Thread.sleep(500L)
}
}
Thread.sleep(2000L)
println("协程取消之前的状态:${job.isActive}")
//协程取消
job.cancel()//setInterrupt
println("协程取消之后的状态:${job.isActive}")
job.join()
}
16.async启动协程
launch启动协程,不能获取协程执行的结果
async可以获取协程中执行的结果
//1
fun main(args: Array<String>)= runBlocking {
println("主线程开始执行")
var async = async {
println("async执行开始")
delay(200L)
println("async执行结束")
}
async.await()//相当于把线程串到主线程上了
println("主线程结束执行")
}
//2
fun main(args: Array<String>) = runBlocking {
//启动协程
val defer1 = async { job1() }
val defer2 = async { job2() }
val result1 = defer1.await()
val result2 = defer2.await()
println(result1)
println(result2)
}
//挂起函数定义任务
suspend fun job1():String{
println("job1开始执行了")
delay(2000L)
println("job1执行结束了")
return "job1的返回值"
}
suspend fun job2():String{
println("job2开始执行了")
delay(2000L)
println("job2执行结束了")
return "job2的返回值"
}
17..协程的上下文
fun main(args: Array<String>)= runBlocking {
println("主线程开始执行")
launch {
(1..10).forEach {
println("默认:执行的第$it 次,${Thread.currentThread()}")
//Thread[ForkJoinPool.commonPool-worker-1,5,main]
}
}.join()
launch(CommonPool) {
(1..10).forEach {
println("CommonPool:执行的第$it 次,${Thread.currentThread()}")
//Thread[ForkJoinPool.commonPool-worker-1,5,main]
}
}.join()
//运行在主线程里面
launch(Unconfined) {
(1..10).forEach {
println("Unconfined:执行的第$it 次,${Thread.currentThread()}")
//Thread[main,5,main]
}
}.join()
//运行在父协程的上下文
launch(coroutineContext) {
(1..10).forEach {
println("coroutineContext:执行的第$it 次,${Thread.currentThread()}")
//Thread[main,5,main]
}
}.join()
//运行在父协程的上下文
launch(newFixedThreadPoolContext(3,"LanSir")) {
(1..10).forEach {
println("3,LanSir:执行的第$it 次,${Thread.currentThread()}")
//Thread[LanSir-1,5,main]
}
}.join()
println("主线程结束执行")
}
网友评论