【Java并发专题之二】Java线程基础

使用线程更好的提高资源利用率,但也会带来上下文切换的消耗,频繁的内核态和用户态的切换消耗,如果代码设计不好,可能弊大于利。

一、线程
  进程是分配资源的最小单位,线程是程序执行的最小单位;线程是依附于进程的,一个进程可以生成多个线程,这些线程拥有共享的进程资源;

二、线程生命周期(相关API)
1、5个阶段6种状态
  5个阶段:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)。
  6种状态:

public enum State
{
       /**
        * Thread state for a thread which has not yet started.
        */
       NEW,  

       /**
        * Thread state for a runnable thread.  A thread in the runnable
        * state is executing in the Java virtual machine but it may
        * be waiting for other resources from the operating system
        * such as processor.
        */
       RUNNABLE,  

       /**
        * Thread state for a thread blocked waiting for a monitor lock.
        * A thread in the blocked state is waiting for a monitor lock
        * to enter a synchronized block/method or
        * reenter a synchronized block/method after calling
        * {@link Object#wait() Object.wait}.
        */
       BLOCKED,  

       /**
        * Thread state for a waiting thread.
        * A thread is in the waiting state due to calling one of the
        * following methods:
        * <ul>
        *   <li>{@link Object#wait() Object.wait} with no timeout</li>
        *   <li>{@link #join() Thread.join} with no timeout</li>
        *   <li>{@link LockSupport#park() LockSupport.park}</li>
        * </ul>
        *
        * <p>A thread in the waiting state is waiting for another thread to
        * perform a particular action.
        *
        * For example, a thread that has called <tt>Object.wait()</tt>
        * on an object is waiting for another thread to call
        * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
        * that object. A thread that has called <tt>Thread.join()</tt>
        * is waiting for a specified thread to terminate.
        */
       WAITING,  

       /**
        * Thread state for a waiting thread with a specified waiting time.
        * A thread is in the timed waiting state due to calling one of
        * the following methods with a specified positive waiting time:
        * <ul>
        *   <li>{@link #sleep Thread.sleep}</li>
        *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
        *   <li>{@link #join(long) Thread.join} with timeout</li>
        *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
        *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
        * </ul>
        */
       TIMED_WAITING,  

       /**
        * Thread state for a terminated thread.
        * The thread has completed execution.
        */
       TERMINATED;
}

1.1、新建--NEW
New并初始化。
四种方式:
(1)继承Thread类

package test;

public class MyThread extends Thread{

    @Override
    public void run() {
        System.out.println("MyThread is running...");
    }
}

package test;

public class Main {
    public static void main(String[] args){
        new MyThread().start();//创建并启动线程 MyThread is running...
    }
}

(2)实现Runnable接口

package test;

public class MyThread implements Runnable{

    @Override
    public void run() {
        System.out.println("MyThread is running...");
    }
}

package test;

public class Main {
    public static void main(String[] args){
        Thread thread=new Thread(new MyThread());
        thread.start();//MyThread is running...
        //或者new Thread(new MyThread()).start();
    }
}

(3)实现Callable接口

和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大,可以有返回值,可以声明抛出异常
Java5提供了Future接口来代表Callable接口里call()方法的返回值:
  类FutureTask既实现了Future接口和Runnable接口,因此可以作为Thread类的target。
  在Future接口定义了几个公共方法来控制它关联的Callable任务。

>boolean cancel(boolean mayInterruptIfRunning):试图取消该Future里面关联的Callable任务
>V get():返回Callable里call()方法的返回值,调用这个方法会导致程序阻塞,必须等到子线程结束后才会得到返回值
>V get(long timeout,TimeUnit unit):返回Callable里call()方法的返回值,最多阻塞timeout时间,经过指定时间没有返回抛出TimeoutException
>boolean isDone():若Callable任务完成,返回True
>boolean isCancelled():如果在Callable任务正常完成前被取消,返回True

举例:

package test;

import jdk.nashorn.internal.codegen.CompilerConstants;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Main {
    public static void main(String[] args) {
        //1】创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。
        //2】使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
        FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>(){
            @Override
            public Integer call() throws Exception{
                System.out.println("FutureTask1 call ...");
                return 100;
            }
        });
        //从jdk1.8开始可以使用Lambda表达式创建Callable对象
        FutureTask<Integer> task2 = new FutureTask<Integer>((Callable<Integer>)()->{
            System.out.println("FutureTask2 call ...");
            return 200;
        });

        //3】使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
        new Thread(task,"有返回值的线程task").start();//实质上还是以Callable对象来创建并启动线程
        new Thread(task2,"有返回值的线程task").start();//实质上还是以Callable对象来创建并启动线程

        try {
            //4】调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
            System.out.println("子线程的返回值:" + task.get());//子线程的返回值:100
            System.out.println("子线程的返回值:" + task2.get());//子线程的返回值:200
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}

(4)使用线程池

1.2 就绪--Runnable
当线程对象调用了start()方法之后,该线程处于就绪状态,JVM会为其创建方法调用栈和程序计数器,等待系统为其分配CPU时间片。

调用start()方法与run()方法,对比如下:
(1)调用start()方法来启动线程,系统会把该run()方法当成线程执行体来处理。但如果直接调用线程对象的run()方法,则run()方法立即就会被执行,而且在run()方法返回之前其他线程无法并发执行。也就是说,系统把线程对象当成一个普通对象,而run()方法也是一个普通方法,而不是线程执行体;
(2)调用了线程的run()方法之后,该线程已经不再处于新建状态,不要再次调用线程对象的start()方法。只能对处于新建状态的线程调用start()方法,否则将引发IllegaIThreadStateExccption异常;

1.3 运行--Running
线程获得了CPU时间片才得以真正开始执行run()方法的线程执行体

1.4 阻塞--Blocked
处于运行状态的线程在某些情况下,让出CPU并暂时停止自己的运行,进入阻塞状态。

阻塞状态分类:
等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
同步阻塞:线程在获取synchronized同步锁失败(因为锁被其它线程占用),它会进入到同步阻塞状态;
其他阻塞:通过调用线程的sleep()或join()或发出I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕 时,线程重新转入就绪状态;

(1)WAITING:等待状态
线程处于 无限制等待状态,等待一个特殊的事件来重新唤醒,如:

通过wait()方法进行等待的线程等待一个notify()或者notifyAll()方法;
通过join()方法进行等待的线程等待目标线程运行结束而唤醒;

(2)TIMED_WAITING:时限等待状态
线程进入了一个 时限等待状态,如:sleep(3000),等待3秒后线程重新进行 就绪(RUNNABLE)状态 继续运行。

1.5 死亡--Dead
线程会以如下3种方式结束,结束后就处于死亡状态:
(1)run()或call()方法执行完成,线程正常结束;
(2)线程抛出一个未捕获的Exception或Error;
(3)直接调用该线程stop()方法来结束该线程—该方法不安全,通常不推荐使用;

终止(TERMINATED)状态,线程执行完毕后,进入终止(TERMINATED)状态。

package test;

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        Runnable interruptTask = new Runnable() {
            int i = 0;
            @Override
            public void run() {
                try {
                    //在正常运行任务时,经常进行本线程的中断标志,如果被设置了终端标志就自行停止线程
                    while (!Thread.currentThread().isInterrupted()){
                        //休眠100ms
                        Thread.sleep(100);
                        i++;
                        System.out.println(Thread.currentThread().getName() + " 状态(" + Thread.currentThread().getState() + ") loop " + i);
                    }
                }catch (InterruptedException e){
                    //在调用阻塞方法时正确处理InterruptedException异常。(例如,catch异常后就结束线程。)
                    System.out.println(Thread.currentThread().getName() + " 状态(" + Thread.currentThread().getState()
                            + ") catch InterruptedException.");
                }
            }
        };
        Thread t1 = new Thread(interruptTask,"t1");
        System.out.println(t1.getName() +" 状态("+t1.getState()+") is new.");

        // 启动“线程t1”
        t1.start();
        System.out.println(t1.getName() +" 状态("+t1.getState()+") is started.");

        // 主线程休眠300ms,然后主线程给t1发“中断”指令。
        Thread.sleep(300);
        t1.interrupt();
        System.out.println(t1.getName() +" 状态("+t1.getState()+") is interrupted.");

        // 主线程休眠300ms,然后查看t1的状态。
        Thread.sleep(300);
        System.out.println(t1.getName() +" 状态("+t1.getState()+") is interrupted now.");
    }

}

结果:

t1 状态(NEW) is new.
t1 状态(RUNNABLE) is started.
t1 状态(RUNNABLE) loop 1
t1 状态(RUNNABLE) loop 2
t1 状态(TIMED_WAITING) is interrupted.
t1 状态(RUNNABLE) catch InterruptedException.
t1 状态(TERMINATED) is interrupted now.

原文地址:https://www.cnblogs.com/cac2020/p/12048379.html

时间: 2024-12-31 03:06:05

【Java并发专题之二】Java线程基础的相关文章

Java并发编程(二)线程任务的中断(interrupt)

使用interrupt()中断线程 当一个线程运行时,另一个线程可以调用对应的Thread对象的interrupt()方法来中断它,该方法只是在目标线程中设置一个标志,表示它已经被中断,并立即返回. package com.csdhsm.concurrent; import java.util.concurrent.TimeUnit; /** * @Title: InterruptDemo.java * @Package: com.csdhsm.concurrent * @Description

【Java并发专题之三】Java线程同步

从JDK5引入CAS原子操作,但没有对synchronized关键字做优化,而是增加了J.U.C.concurrent,concurrent包有更好的性能:从JDK6对synchronized的实现机制进行了较大调整,包括使用JDK5引进的CAS自旋之外,还增加了自适应的CAS自旋.锁消除.锁粗化.偏向锁.轻量级锁这些优化策略. 优化后的锁有四种状态:无锁状态.偏向锁状态.轻量级锁状态.重量级锁状态,每种锁是只能升级,不能降级,即由偏向锁->轻量级锁->重量级锁,而这个过程就是开销逐渐加大的过

Java并发学习之二——获取和设置线程信息

本文是学习网络上的文章时的总结,感谢大家无私的分享. Thread类的对象中保存了一些属性信息能够帮助我们辨别每一个线程,知道它的一些信息 ID:每个线程的独特标示: Name:线程的名称: Priority:线程对象的优先级.优先级别在1-10之间,1是最低级,10是最高级. Status:线程状态.在java中,线程只有6种状态:new,runnable,blocked,waiting,time waiting 或terminated. 现在写一个程序,将线程的信息保存到文件中方便查看 pa

Java 并发专题 :闭锁 CountDownLatch 之一家人一起吃个饭

最近一直整并发这块东西,顺便写点Java并发的例子,给大家做个分享,也强化下自己记忆. 每天起早贪黑的上班,父母每天也要上班,话说今天定了个饭店,一家人一起吃个饭,通知大家下班去饭店集合.假设:3个人在不同的地方上班,必须等到3个人到场才能吃饭,用程序如何实现呢? 作为一名资深屌丝程序猿,开始写代码实现: package com.zhy.concurrency.latch; public class Test1 { /** * 模拟爸爸去饭店 */ public static void fath

Java 并发专题 :FutureTask 实现预加载数据 在线看电子书、浏览器浏览网页等

继续并发专题~ FutureTask 有点类似Runnable,都可以通过Thread来启动,不过FutureTask可以返回执行完毕的数据,并且FutureTask的get方法支持阻塞. 由于:FutureTask可以返回执行完毕的数据,并且FutureTask的get方法支持阻塞这两个特性,我们可以用来预先加载一些可能用到资源,然后要用的时候,调用get方法获取(如果资源加载完,直接返回:否则继续等待其加载完成). 下面通过两个例子来介绍下: 1.使用FutureTask来预加载稍后要用的的

【转】Java并发编程:如何创建线程?

一.Java中关于应用程序和进程相关的概念 在Java中,一个应用程序对应着一个JVM实例(也有地方称为JVM进程),一般来说名字默认是java.exe或者javaw.exe(windows下可以通过任务管理器查看).Java采用的是单线程编程模型,即在我们自己的程序中如果没有主动创建线程的话,只会创建一个线程,通常称为主线程.但是要注意,虽然只有一个线程来执行任务,不代表JVM中只有一个线程,JVM实例在创建的时候,同时会创建很多其它的线程(比如垃圾收集器线程). 由于Java采用的是单线程编

Java并发编程:进程和线程

.title { text-align: center } .todo { font-family: monospace; color: red } .done { color: green } .tag { background-color: #eee; font-family: monospace; padding: 2px; font-size: 80%; font-weight: normal } .timestamp { color: #bebebe } .timestamp-kwd

Java 并发专题 : Executor详细介绍 打造基于Executor的Web服务器

适配器模式,将一个类的接口转换成客户希望的另外一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 应用场景:系统的数据和行为都正确,但接口不符时,我们应该考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配.适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况. 代码实现: //Adapter.h #include "stdafx.h" #include <iostream> class Adaptee

和朱晔一起复习Java并发(一):线程池

和我之前的Spring系列文章一样,我们会以做一些Demo做实验的方式来复习一些知识点. 本文我们先从Java并发中最最常用的线程池开始. 从一个线程池实验开始 首先我们写一个方法来每秒一次定时输出线程池的基本信息: private void printStats(ThreadPoolExecutor threadPool){ Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> { log.info("