1、实现Runnable类
2、重写run方法
3、创建线程对象
4、调用start方法启动线程
/**
* @ClassName RunnableDemo
* @Description TODO
* @Author Hai
* @Date 2020/12/12 11:29
* @Version 1.0
**/
//第一步:继承Thread
public class ThreadDemo extends Thread {
//票数
private static Integer ticketNums = 0;
@Override
//第二步:重写run方法
public void run(){
for (int i = 0; i < 1000; i++) {
System.out.println("当前正在运行子线程---第"+i+"次");
}
}
public static void main(String[] args){
//第三步:创建一个线程对象
ThreadDemo threadDemo = new ThreadDemo();
//第四步:调用start()启动线程
threadDemo.start();
for (int i = 0; i < 1000; i++) {
System.out.println("正在运行主线程---第"+i+"次");
}
}
}
点击运行主方法:
当线程开启以后,并不会立即执行,而是由cpu 调度执行
可以看出两个for循环是并行执行的
注意!注意!
调用start()方法启动线程,直接调用run()方法启动就跟普通调用方法一样,不能启动线程,而是相当于只调用了一个普通的方法!
1、实现Runnable接口
2、重写run方法
3、创建Thread对象
4、调用start方法启动线程
/**
* @ClassName RunnableDemo
* @Description TODO
* @Author Hai
* @Date 2020/12/12 11:29
* @Version 1.0
**/
//第一步:继承Thread
public class RunnableDemo implements Runnable{
//票数
private static Integer ticketNums = 0;
@Override
//第二步:重写run方法
public void run(){
while (true){
//一共一百张票,抢完则跳出循环
if(ticketNums>=100){
break;
}
//获取当前线程名称并输出
System.out.println(Thread.currentThread().getName()+"---拿到了第"+ ++ticketNums +"票");
}
}
public static void main(String[] args){
RunnableDemo runnableDemo = new RunnableDemo();
//第三步:创建Thread对象 (为方便理解,以下不采用简写)
Thread t1 = new Thread(runnableDemo ,"小明");
Thread t2 = new Thread(runnableDemo ,"小红");
Thread t3 = new Thread(runnableDemo ,"黄牛");
//第四步:调用start方法启动线程
t1.start();
t2.start();
t3.start();
}
}
点击运行主方法:
当线程开启以后,并不会立即执行,而是由cpu 调度执行
注意!注意!再次声明:
调用start()方法启动线程,直接调用run()方法启动就跟普通调用方法一样,不能启动线程,而是相当于只调用了一个普通的方法!
1、实现Callable接口
2、重写call()方法
3、创建执行服务
4、提交执行
5、获取结果
6、关闭服务
/**
* @ClassName CallableDemo
* @Description TODO
* @Author Hai
* @Date 2020/12/12 11:29
* @Version 1.0
**/
//第一步:实现Callable接口
public class CallableDemo implements Callable<Boolean> {
private String name;
public CallableDemo(String name) {
this.name = name;
}
//第二部:重写call()方法
@Override
public Boolean call() {
demo();
return true;
}
public void demo(){
for (int i = 0; i < 1000; i++) {
System.out.println("当前正在运行--"+ name +" 号线程--第"+i+"次");
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableDemo setname1 = new CallableDemo("小明");
CallableDemo setname2 = new CallableDemo("小红");
//第三步:创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(2);
//第四步:提交执行
Future<Boolean> t1 = ser.submit(setname1);
Future<Boolean> t2 = ser.submit(setname2);
//第五步:获取结果
Boolean b1 = t1.get();
Boolean b2 = t2.get();
//关闭服务
ser.shutdownNow();
System.out.println(b1);
System.out.println(b2);
}
}
点击运行主方法:
当线程开启以后,并不会立即执行,而是由cpu 调度执行
注意!注意!
调用start()方法启动线程,直接调用run()方法启动就跟普通调用方法一样,不能启动线程,而是相当于只调用了一个普通的方法!
切记!切记!
实现Callable接口创建线程,当线程结束后一定要关闭线程服务!最好用try/catch放入finally中,以免程序异常导致服务未能关闭。
如下图所示,多个线程操作同一数据的时候,就会出现线程不安全的情况。
本博客主要讲解多线程的几种实现方式,线程安全方面咱们后面再讲。
何为线程池,顾名思义线程池就是一个 专门存放线程的一个池子
提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回线程池中,这样可以避免频繁创建,销毁线程,实现重复利用。就像共享单车,当你使用完后别人还能使用
能提高响应速度(减少了创建新线程的时间)
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
便于线程的管理(...)
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @ClassName TestPool
* @Description TODO
* @Author Hai
* @Date 2020/12/12 11:29
* @Version 1.0
**/
public class TestPool {
public static void main(String[] args) {
//3,创建服务,创建线程池
//newFixedThreadPool 参数为:线程池大小
ExecutorService service = Executors.newFixedThreadPool(10);
//4,执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//5,关闭链接
service.shutdown();
}
}
//1,实现 Runnable接口
class MyThread implements Runnable{
//2,重写run方法编写线程体
@Override
public void run() {
//输出当前线程的名字
System.out.println(Thread.currentThread().getName());
}
}
直接运行方法得出:
执行了6个线程,正常的打印出了各个线程的名字
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @ClassName TestPool
* @Description TODO
* @Author Hai
* @Date 2020/12/12 11:29
* @Version 1.0
**/
//回顾总结线程的创建
public class TheradNew {
public static void main(String[] args) {
//调用各个线程
//继承Thread类启动方法
new MyThread1().start();
//实现Runnable接口启动方法
new Thread(new MyThread2()).start();
//实现Callable接口启动方法
FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());
new Thread(futureTask).start();
try {
//不要忘了实现Callable接口方式创建线程会有返回值
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
//1丶继承Thread类
class MyThread1 extends Thread{
@Override
public void run(){
System.out.println("继承Thread类创建线程");
}
}
//2丶实现Runnable接口
class MyThread2 implements Runnable{
@Override
public void run(){
System.out.println("实现Runnable接口创建线程");
}
}
//3丶实现Callable接口
class MyThread3 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("实现Callable接口创建线程");
return 100;
}
}
这个问题暂时没有准确的答案,各种方式都有自己的优缺点,根据应用场景的不同而使用不同的创建方式才是最好的,相对高并发的情况下,线程池的创建就比较适合,它能提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回线程池中,这样可以避免频繁创建,销毁线程影响资源与效率,实现重复利用。当然这只是相对高并发的特定场景而言,并不能说线程池的创建方式就是最好。的这个需要各位读者结合应用的场景以及对各种创建方式的理解而自行定夺。没有最好的方式,只有最合适的方式。