线程的实现方法
我自己对这个概念当时记得,长时间不用,然后又忘了,在这里做笔记给自己回顾使用
通过集成java.lang.Thread线程类来创建一个线程
package com.jay.Thread;
/**
* @version 0.0.1
* @program: spring-poi-demo
* @description: 通过集成java.lang.Thread线程类来创建一个线程
* @author: huangzq
* @create: 2020-09-04 08:21
*/
public class TestThread {
public static void main(String[] args) {
/**
* 控制台输出结果:
* 主线程ID是: 1
* 名称线程2的线程ID是:1
* 名称线程1的线程ID是:11
* 结论:
* 1、主线程和线程2的线程ID相同,说明直接调用run()方法不会创建新的线程,而是在主线程中直接调用run()方法,普通的方法调用
* 2、线程1先调用start()方法,而后线程2调用run()方法,最终却线程2先于线程1输出,说明新建的线程并不会影响主线程的执行顺序
*/
System.out.println("主线程ID是: " + Thread.currentThread().getId());
Thread t1 = new MyThread("线程1");
t1.start();
Thread t2 = new MyThread("线程2");
/*直接调用run()方法*/
t2.run();
}
/**
* 自定义线程
*/
static class MyThread extends Thread {
/*线程名称*/
private String name;
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("名称:" + name + "的线程ID是:" + Thread.currentThread().getId());
}
}
}
通过实现java.lang.Runnable接口来创建一个线程
package com.jay.Thread;
/**
* @version 0.0.1
* @program: spring-poi-demo
* @description: 通过实现java.lang.Runnable接口来创建一个线程
* @author: huangzq
* @create: 2020-09-04 08:28
*/
public class TestRunnable {
/**
* 其实不管是通过继承Thread类,还是实现Runnable接口的方式,都可以创建一个线程,其结果都是一样的。
* 区别在于:
*
* 实现Runnable的方式需要将实现Runnable接口的类作为参数传递给Thread,然后通过Thread类调用Start()方法来创建线程,
* 直接继承Thread类的话代码更简洁,也更容易理解,但是由于JAVA被设计为只支持单继承,所以如果要继承其他类的同时需要实现线程那就只能实现Runnable接口了,
* 这里更推荐实现Runnable接口,实现Runnable的方式更为灵活一些。
*
* @param args
*/
public static void main(String[] args) {
System.out.println("主线程的ID是: " + Thread.currentThread().getId());
MyRunnable r1 = new MyRunnable("线程1");
Thread t1 = new Thread(r1);
t1.start();
MyRunnable r2 = new MyRunnable("线程2");
Thread t2 = new Thread(r2);
/*直接调用run()方法,并不会创建新线程*/
t2.run();
}
static class MyRunnable implements Runnable{
private String name;
public MyRunnable(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("名字:" + name + "的线程ID是: " + Thread.currentThread().getId());
}
}
}
带返回值的线程接口Callable
package com.jay.Thread;
import java.util.concurrent.*;
/**
* @version 0.0.1
* @program: spring-poi-demo
* @description: 带返回值的线程接口Callable
* @author: huangzq
* @create: 2020-09-04 08:57
*/
public class TestCallable {
/**
* 线程1返回了东西...
* 线程2返回了东西...
* 线程3返回了东西...
* @param args
*/
public static void main(String[] args) {
//创建一个线程池
ExecutorService threadPool = Executors.newFixedThreadPool(3);
//创建三个有返回值的任务
MyCallable c1 = new MyCallable("线程1");
MyCallable c2 = new MyCallable("线程2");
MyCallable c3 = new MyCallable("线程3");
Future f1 = threadPool.submit(c1);
Future f2 = threadPool.submit(c2);
Future f3 = threadPool.submit(c3);
try {
System.out.println(f1.get().toString());
System.out.println(f2.get().toString());
System.out.println(f3.get().toString());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}finally {
threadPool.shutdown();
}
}
/**
* 实现带返回值的接口
*/
static class MyCallable implements Callable {
private String name;
public MyCallable(String name) {
this.name = name;
}
@Override
public Object call() throws Exception {
return name + "返回了东西...";
}
}
}
Java多线程之Callable接口的实现
package com.jay.Thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @version 0.0.1
* @program: spring-poi-demo
* @description: Java多线程之Callable接口的实现
* @author: huangzq
* @create: 2020-09-04 09:06
*/
/*
* 一、创建执行线程的方式三:实现 Callable 接口。 相较于实现 Runnable 接口的方式,方法可以有返回值,并且可以抛出异常。
*
* 二、执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。 FutureTask 是 Future 接口的实现类
*/
public class TestCallableTwo {
/**
* 例子可以看到: Callable 和 Runnable接口的区别
*
* (1)Callable规定的方法是call(),而Runnable规定的方法是run().
* (2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
* (3)call()方法可抛出异常,而run()方法是不能抛出异常的。
* (4)运行Callable任务可拿到一个Future对象, Future表示异步计算的结果。
* 它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。
* 通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。
* Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。
* @param args
*/
public static void main(String[] args) {
ThreadDemo td = new ThreadDemo();
//1.执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。
FutureTask<Integer> result = new FutureTask<>(td);
new Thread(result).start();
//2.接收线程运算后的结果
try {
//FutureTask 可用于 闭锁 类似于CountDownLatch的作用,在所有的线程没有执行完成之后这里是不会执行的
Integer sum = result.get();
System.out.println(sum);
System.out.println("------------------------------------");
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
static class ThreadDemo implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= 100000; i++) {
sum += i;
}
return sum;
}
}
}
join()加入线程,会让主线程等待
package com.jay.Thread;
/**
* @version 0.0.1
* @program: spring-poi-demo
* @description: join()加入线程
* @author: huangzq
* @create: 2020-09-04 08:37
*/
public class TestJoin {
/**
* 执行结果:
* main主线程: main ====> 0
* main主线程: main ====> 1
* 当前线程: 线程1 ===> 0
* main主线程: main ====> 2
* 当前线程: 线程1 ===> 1
* 当前线程: 线程1 ===> 2
* 当前线程: 线程1 ===> 3
* 当前线程: 线程1 ===> 4
* main主线程: main ====> 3
* main主线程: main ====> 4
* 当前线程: 线程2 ===> 0
* 当前线程: 线程2 ===> 1
* 当前线程: 线程2 ===> 2
* 当前线程: 线程2 ===> 3
* 当前线程: 线程2 ===> 4
* main主线程: main ====> 5
* main主线程: main ====> 6
* main主线程: main ====> 7
* main主线程: main ====> 8
* main主线程: main ====> 9
*
* 结论:
* 1、使用了join()方法之后,主线程会等待子线程结束之后才会结束,join在代码中可以注释来测试看结果
*/
public static void main(String[] args) throws InterruptedException {
Thread t1 = new MyJoinThread("线程1");
t1.start();
t1.join();
for (int i = 0; i < 10; i++){
if(i == 5){
Thread t2 = new MyJoinThread("线程2");
t2.start();
t2.join();
}
System.out.println("main主线程: " + Thread.currentThread().getName() + " ====> " + i);
}
}
static class MyJoinThread extends Thread{
public MyJoinThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 5; i++){
System.out.println("当前线程: " + Thread.currentThread().getName() + " ===> " + i);
}
}
}
}
sleep()线程休眠
package com.jay.Thread;
/**
* @version 0.0.1
* @program: spring-poi-demo
* @description: sleep()线程休眠
* @author: huangzq
* @create: 2020-09-04 08:32
*/
public class TestSleep {
private int i = 10;
private Object object = new Object();
/**
* 线程: Thread-0开始执行,i的值为:11
* 线程: Thread-0准备进入休眠状态...
* 线程: Thread-0休眠结束...
* 线程: Thread-0继续执行,i的值为===========>:12
* 线程: Thread-1开始执行,i的值为:13
* 线程: Thread-1准备进入休眠状态...
* 线程: Thread-1休眠结束...
* 线程: Thread-1继续执行,i的值为===========>:14
* 结论:
* 1、当Thread-0进入休眠状态,Thread-1并没有马上执行,而是等待Thread-0休眠结束释放了对象锁才继续执行
* 2、当调用sleep()方法时,必须捕获异常或者向上抛出异常,当线程休眠结束并不会马上执行,而是进入就绪状态,等待CPU的再次调度,调用Sleep()方法相当于是进入了阻塞状态
* 3、补充:这个时候可考察sleep和wait的区别,他们所属对象不同,sleep这个时候不会释放锁,而wait会释放,不会阻塞,synchronized我在上篇的文章中详细介绍了
*/
public static void main(String[] args) {
TestSleep testSleep = new TestSleep();
Thread t1 = testSleep.new MyTestThread();
t1.start();
Thread t2 = testSleep.new MyTestThread();
t2.start();
}
class MyTestThread extends Thread{
@Override
public void run() {
synchronized (object){
i++;
System.out.println("线程: " + Thread.currentThread().getName() + "开始执行,i的值为:" + i);
System.out.println("线程: " + Thread.currentThread().getName() + "准备进入休眠状态...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程: " + Thread.currentThread().getName() + "休眠结束...");
i++;
System.out.println("线程: " + Thread.currentThread().getName() + "继续执行,i的值为===========>:" + i);
}
}
}
}
yeild()让出CPU执行权限
package com.jay.Thread;
/**
* @version 0.0.1
* @program: spring-poi-demo
* @description: yeild()让出CPU执行权限
* @author: huangzq
* @create: 2020-09-04 08:48
*/
public class TestYield {
/**
* 执行结果:
* 名字: 线程1执行
* 名字: 线程2执行
* 线程2结果num: 1065788928计算耗时: 8毫秒
* 线程1结果count: 1784293664计算耗时: 138毫秒
* 结论:
* 1、调用yield()方法是为了让当前线程让出CPU执行权限,从而可以让CPU去执行其他线程,它和sleep()方法类似同样是不会释放对象锁,
* 但是yield()不会控制具体的交出CPU权限的时间,同时也只能让具有相同优先级的线程获得CPU执行时间的机会
* 2、调用yield()方法并不会让当前线程进入阻塞状态,而只是进入就绪状态,只需要等待重新获取CPU的时间片,而Sleep()则会进入阻塞状态
*/
public static void main(String[] args) {
MyYieldThread t = new MyYieldThread("线程1");
t.start();
MyYieldThread2 t2 = new MyYieldThread2("线程2");
t2.start();
System.out.println("主线程名称:"+Thread.currentThread().getName());
}
/**
* 线程1的
*/
static class MyYieldThread extends Thread{
private String name;
public MyYieldThread(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("名字: " + name + "执行");
long start = System.currentTimeMillis();
int count = 0;
for (int i = 0; i < 1000000; i++){
count = count + (i + 1);
//执行到这里就让了cup给线程2执行了
Thread.yield();
}
long end = System.currentTimeMillis();
System.out.println(name + "结果count: " + count + ",计算耗时: " + (end - start) + "毫秒");
}
}
/**
* 线程2的
*/
static class MyYieldThread2 extends Thread{
private String name;
public MyYieldThread2(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("名字: " + name + "执行");
long start = System.currentTimeMillis();
int num = 0;
for (int i = 0; i < 10000000; i++){
num += i * 8;
}
long end = System.currentTimeMillis();
System.out.println(name + "结果num: " + num + ",计算耗时: " + (end - start) + "毫秒");
}
}
}
从下面的代码例子得到结论
1、Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回结果的异常,而Callable功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值。
2、通常在使用Callable的时候,也会涉及到Future,一般配合一起使用,一个产生结果,一个拿到结果。
3、假设有一个很耗时的返回值需要计算,并且这个返回值不是立刻需要的话,那么就可以使用这个组合,用另一个线程去计算返回值,而当前线程在使用这个返回值之前可以做其它的操作,等到需要这个返回值时,再通过Future得到最终计算结果
参考:https://www.jianshu.com/p/94c1c34053d0