3、多线程基础

烟雨 5年前 (2021-06-13) 阅读数 325 #Java并发
文章标签 并发Java

一、线程与进程

1.1、进程(Process) 

    是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。 在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。

1.2、线程(thread) 

    是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

1.3、简单总结

进程
指在系统中正在运行的一个应用程序;程序一旦运行就是进程。
进程——资源分配的最小单位。
线程

系统分配处理器时间资源的基本单元(线程是一种轻量级的进程,与进程相比,线程给操作系统带来侧创建、维护、和管理的负担要轻,意味着线程的代价或开销比较小。),或者说进程之内独立执行的一个单元执行流。

线程——程序执行的最小单位。

1.4、区别

  1. 线程与资源分配无关,它属于某一个进程,并与进程内的其他线程一起共享进程的资源。

  2. 每个进程都有自己一套独立的资源(数据),供其内的所有线程共享。

  3. 不论是大小,开销线程要更“轻量级”。

  4. 一个进程内的线程通信比进程之间的通信更快速,有效。(因为共享变量)。

二、线程的生命周期

线程是一个动态执行的过程,它也有一个从产生到死亡的过程。下图显示了一个线程完整的生命周期:

image.png

1,初始化状态

当使用new关键字、Thread 类、其子类建立一个线程对象后,该线程对象就处于初始化状态。

2,就绪状态

当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

3,运行状态

就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。

4,超时等待状态

运行状态中的线程执行sleep()方法,此时线程进入到超时等待状态。

5,等待状态

运行状态中的线程执行wait()方法,此时线程进入到等待状态。

6,阻塞状态

 运行状态中的线程遇到I/O 请求、同步代码块,此时线程进入到阻塞状态。

7,死亡状态

一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

线程中有一个枚举类型,包括了所有的线程状态:

public enum State {
    //NEW状态表示线程刚刚被定义,还未实际获得资源以启动,也就是还未调用start()方法。
    NEW,
    //RUNNABLE表示线程当前处于运行状态,当然也有可能由于时间片使用完了而等待CPU重新的调度。
    RUNNABLE,
    //BLOCKED表示线程在竞争某个锁失败时被置于阻塞状态。
    BLOCKED,
    //WAITING和TIMED_WAITING表示线程在运行中由于缺少某个条件而不得不被置于条件等待队列,等待需要的条件或资源。
    WAITING,
    TIMED_WAITING,
    //TERMINATED表示线程运行结束,当线程的run方法结束之后,该线程就会是TERMINATED状态。
    TERMINATED;
}
在代码中我们可以通过调用线程的getState()方法获取当前线程的状态。

2.1、线程状态之间的切换

public class NewThread implements Runnable {

	@Override
    //synchronized关键字,其底层是对对象加锁
	public synchronized void run() {
		while(true) {
			try {
                //让线程处于等待状态,让当前线程失去对锁的拥有权
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("自定义的线程执行了....");
		}
	}
	
	public static void main(String[] args) {
		NewThread n = new NewThread();
        // 创建线程,并指定线程任务,线程进入初始化状态
		Thread thread = new Thread(n); 
		// 启动线程
		thread.start(); 
		
		while(true) {
			synchronized (n) {
				System.out.println("主线程执行了...");
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
                //通知所有线程,释放掉锁,进入就绪状态notify()和notifyAll()的调用最好是在synchronized方法的最后一步
				n.notifyAll();
			}
		}
	}
}

三,创建线程的多种方式(8种)

3.1、继承Thread类

创建一个新的类,该类继承 Thread 类,然后创建一个该类的实例。在java.lang包中定义, 继承Thread类必须重写run()方法,该方法是新线程的入口点。它也必须调用 start() 方法才能执行。

/**
 * 通过继承Thread来实现多线程.
 */
public class Thread1 extends Thread{
    @Override
    public void run() {
        System.err.println("通过继承Thread来实现多线程.");
    }
}

其本质上是实现了 Runnable 接口的一个实例:

image.png

3.2、实现Runnable接口

/**
 * 通过实现Runnable来实现多线程.
 */
public class Thread2 implements Runnable{
    public void run() {
        System.err.println("通过实现Runnable来实现多线程.");
    }
}

3.3、通过匿名内部类的方式创建线程

匿名内部类创建的线程只会执行一次

public static void main(String[] args) {
    //第一种
    /*new Thread() {
        public void run() {
            System.out.println("thread start ..");
        };
    }.start();*/
    
    //第二种
    /*new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("thread start ..");
        }
    }).start();*/
    
    //第三种
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("runnable");
        }
    })
    }.start();
}

3.4、通过 Callable 和 Future 创建线程(带返回值的线程)

  1. 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。

  2. 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。

  3. 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。

  4. 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

/**
 * 通过 Callable 和 Future 创建线程.
 */
public class Thread3 implements Callable<Integer>{
    //该方法将作为线程执行体,并且有返回值。
    @Override
    public Integer call() throws Exception {
        int i = 100;
        System.err.println("通过 Callable 和 Future 创建线程.i=" + i);
        return i;
    }
}
public class TestMain {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        Thread3 thread3 = new Thread3();
        FutureTask<Integer> ft = new FutureTask<Integer>(thread3);
        new Thread(ft).start();
        System.out.println("线程的返回值:" + ft.get());  
    }
}

image.png

3.5、通过定时器创建线程

不延迟的每秒钟执行一次指定的线程

public static void main(String[] args) {
    Timer timer = new Timer();
    //Timer.schedule(TimerTask task,long delay,long period)
    //安排指定的任务(task)从指定延迟后的时间(delay)开始进行重复且固定的时间(period)执行.
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            // 实现定时任务
            System.out.println("timertask is run");
        }
    }, 0, 1000);
}

image.png

3.6、通过线程池创建线程

public static void main(String[] args) {
    //创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
    ExecutorService threadPool = Executors.newCachedThreadPool();
    for (int i = 0; i < 100; i++) {
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        });
    }
    //关闭线程池
    threadPool.shutdown();
}

3.7、Lambda创建线程

public static void main(String[] args) {
    List<Integer> values = Arrays.asList(10,20,30,40);
    int res = new Test().add(values);
    System.out.println(res);
}

public int add (List<Integer> values) {
    return values.parallelStream().mapToInt( i -> i++).sum();
}

3.8、Spring对多线程的支持

//标记是Spring的配置类
@Configuration
//扫描包
@ComponentScan("com.zender.thread")
// 启用异步任务
@EnableAsync
public class ThreadConfig {
    /**
     * 返回一个Executor线程池
     */
    @Bean
    public Executor getExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(25);
        executor.initialize();
        return executor;
    }
}
@Service
public class ThreadService {
    /**
     * 这里进行标注为异步任务,在执行此方法的时候,会单独开启线程来执行f1方法。
     */
    @Async
    public void test1() {
        System.out.println("test1 : " + Thread.currentThread().getName() + "   " + UUID.randomUUID().toString());
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    /**
     * 这里进行标注为异步任务,在执行此方法的时候,会单独开启线程来执行f1方法。
     */
    @Async
    public void test2() {
        System.out.println("test2 : " + Thread.currentThread().getName() + "   " + UUID.randomUUID().toString());
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class TestMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ThreadConfig.class);
        ThreadService service = context.getBean(ThreadService.class);
        for (int i = 0; i < 5; i++) {
            // 执行异步任务
            service.test1();
            service.test2();
        }
    }
}

image.png

四,Thread常用方法

下表列出了Thread类的一些重要方法:

public void start()

使该线程开始执行;

public void run()

如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。

public final void setName(String name)

改变线程名称。

public final void setPriority(int priority)

更改线程的优先级。

public final void setDaemon(boolean on)

将该线程标记为守护线程或用户线程。

public final void join(long millisec)

等待该线程终止的时间最长为 millis 毫秒。

public void interrupt()

中断线程。

public final boolean isAlive()

测试线程是否处于活动状态。

public static void yield()

暂停当前正在执行的线程对象,并执行其他线程。

public static void sleep(long millisec)

在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。

public static boolean holdsLock(Object x)

当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。

public static Thread currentThread()

返回对当前正在执行的线程对象的引用。

public static void dumpStack()

将当前线程的堆栈跟踪打印至标准错误流。

五,4种线程池

在Java 5之后,并发编程引入了一堆新的启动、调度和管理线程的API。Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。

image.png

Executor框架包括:线程池,Executor,Executors,ExecutorService,CompletionService(异步任务),Future,Callable等。

5.1、Executor接口

Executor接口是Executor框架中最基础的部分,定义了一个用于执行Runnable的execute方法,它没有实现类只有另一个重要的子接口ExecutorService。

public interface Executor {
    void execute(Runnable command);
}

5.2,ExecutorService接口

ExecutorService接口继承自Executor接口,定义了终止、提交,执行任务、跟踪任务返回结果等方法。

public interface ExecutorService extends Executor {
    //关闭线程池,当此方法被调用时,ExecutorService停止接收新的任务并且等待已经提交的任务(包含提交正在执行和提交未执行)执行完成。
    //当所有提交任务执行完毕,线程池即被关闭。
    void shutdown();
    //立即停止,将暂停所有等待处理的任务并返回这些任务的列表
    List<Runnable> shutdownNow();
    //判断执行器是否已经关闭
    boolean isShutdown();
    //判断关闭后所有任务是否都已完成 
    boolean isTerminated();
 
    //接收timeout和TimeUnit(日期工具类)两个参数,用于设定超时时间及单位。
    //当等待超过设定时间时,会监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false。
    //一般情况下会和shutdown方法组合使用。
    boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;
    //提交一个Callable任务
    <T> Future<T> submit(Callable<T> task);
 
    //提交一个Runable任务,result是要返回的结果
    <T> Future<T> submit(Runnable task, T result);
 
    //提交一个任务 
    Future<?> submit(Runnable task);
 
    //执行所有给定的任务,当所有任务完成,返回保持任务状态和结果的Future列表 
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException;
 
    //执行给定的任务,当所有任务完成或超时期满时(无论哪个首先发生),返回保持任务状态和结果的 Future 列表。 
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                  long timeout, TimeUnit unit)
        throws InterruptedException;
 
    //执行给定的任务,如果某个任务已成功完成(也就是未抛出异常),则返回其结果。
    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException;
    
    //执行给定的任务,如果在给定的超时期满前某个任务已成功完成(也就是未抛出异常),则返回其结果。 
    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

5.3、Executors

可以Executors来创建的线程池的类型,可以创建5种类型的线程池。

NewFixedThreadPool(numberOfThreads:int)(固定线程池)ExecutorService 创建一个固定线程数量的线程池,并行执行的线程数量不变,线程当前任务完成后,可以被重用执行另一个任务
NewCachedThreadPool():(可缓存线程池)ExecutorService 创建一个线程池,按需创建新线程,就是有任务时才创建,空闲线程保存60s,当前面创建的线程可用时,则重用它们
newSingleThreadExecutor();(单线程执行器)线程池中只有一个线程,依次执行任务这个线程池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去
newScheduledThreadPool();线程池按时间计划来执行任务,允许用户设定执行任务的时间来执行。
newSingleThreadExecutor();线程池中只有一个线程,它按规定顺序(FIFO, LIFO, 优先级)来执行任务

newFixedThreadPool类型

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

public static void main(String[] args) {
    ExecutorService threadPool = Executors.newFixedThreadPool(3);
    for (int i = 0; i < 12; i++) {
        int index = i;
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println(index);
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }
    //关闭线程池
    threadPool.shutdown();
}

因为线程池大小为3,每个任务输出后间隔2秒,所以每两秒打印3个数字。

GIF_3.gif

newCachedThreadPool类型

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

public static void main(String[] args) {
    ExecutorService threadPool = Executors.newCachedThreadPool();
    for (int i = 0; i < 4; i++) {
        int index = i;
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("threadName="+ Thread.currentThread().getName() + " index=" + index);
            }
        });
    }
    //关闭线程池
    threadPool.shutdown();
}

线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。

GIF_3.gif

newSingleThreadExecutor类型

创建一个线程池(这个线程池只有一个线程),这个线程池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去。

public class Thread4 extends Thread{
    @Override
    public void run() {
        int temp = 0;  
        int i = 0;  
        Random random =new Random();  
        while(true){  
            int j =random.nextInt(100);
            System.err.println("threadName="+ Thread.currentThread().getName() + "  temp="+ temp +"  i="+ (i++) + "  j=" + j);  
            try{  
                if(temp == 0 && j > 60 ) {
                    temp = 7/0;
                }  
                Thread.sleep(100);
            }catch(Exception e){  
                e.printStackTrace();  
                temp = 1;  
            }  
        }
    }
    
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        Thread4 thread4 = new Thread4();
        threadPool.execute(thread4);
    }
}

image.png

newScheduledThreadPool类型

创建一个定长线程池,支持定时及周期性任务执行。

延迟3秒执行:

public static void main(String[] args) {
    ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
    threadPool.schedule(new Runnable() {
        @Override
        public void run() {
            System.out.println("delay 3 seconds");
        }
    }, 3, TimeUnit.SECONDS);
    //关闭线程池
    threadPool.shutdown();
}

GIF_3.gif

延迟1秒后每3秒执行一次:

public static void main(String[] args) {
    ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
    threadPool.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            System.out.println("delay 3 seconds");
        }
    },1 ,3 , TimeUnit.SECONDS);
}

GIF_3.gif

newSingleThreadExecutor类型

public static void main(String[] args) {
    ExecutorService threadPool = Executors.newSingleThreadExecutor();
    for (int i = 0; i < 10; i++) {
        final int index = i;
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println(index);
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

结果依次输出,相当于顺序执行各个任务。

image.png

版权声明

非特殊说明,本文由Zender原创或收集发布,欢迎转载。

上一篇:2、volatile关键字 下一篇:4、Synchronized

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

作者文章
热门