精华内容
下载资源
问答
  • 使用 Deprecated 来标记方法@Deprecated//用来判断ip是否合法public boolean checkIp(String tempIp) {String regex = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)){3}";// ...

    使用 Deprecated 来标记方法

    @Deprecated

    //用来判断ip是否合法

    public boolean checkIp(String tempIp) {

    String regex = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)){3}";

    // String regex2 = "([1-9]|[1-9]\\\\d|1\\\\d{2}|2[0-4]\\\\d|25[0-5])(\\\\.(\\\\d|[1-9]\\\\d|1\\\\d{2}|2[0-4]\\\\d|25[0-5])){3}";

    Pattern p = Pattern.compile(regex);

    Matcher m = p.matcher(tempIp);

    return m.matches();

    }

    之后在使用该方法时,方法名会有一根删除线标记

    boolean isIP = cv.checkIp(ipString);

    :boolean isIP = cv.

    checkIp(ipString);

    展开全文
  • 上篇详细研究了Java中的反射操作以及Class类相关内容,但在Java开发过程中,除了反射,往往还有泛型、注解等相关特性操作组合使用来实现一些高级技术,如Spring中就大量使用了反射和注解,实现了诸如Bean容器管理...

    1beff6c71af414c77be16f8bc55eaf45.gif

    上篇详细研究了Java中的反射操作以及Class类相关内容,但在Java开发过程中,除了反射,往往还有泛型、注解等相关特性操作组合使用来实现一些高级技术,如Spring中就大量使用了反射和注解,实现了诸如Bean容器管理机制等操作,SpringMvc框架中大量使用了注解,实现了servlet容器的简易操作等,现在我们开始详细的学习Java中的注解机制

    注解是什么

    日常开发中经常提到注解,那么注解是什么呢?在Java中,注解就是给程序添加一些信息,用字符@开头,可以用来修饰后续的其他代码元素,比如类、接口、字段、方法、参数、构造方法等,往往注解还搭配编译器、程序运行时以及其他工具或者插件使用,用于实现代码功能的增强或者修改程序的行为等操作

    Java内置注解

    在Java中内置了一些注解,用来在类、方法申明使用,从而实现编译器检查、避免编译器检查等操作,同时也提高了java逻辑的严谨性,而在Java中内置的常见注解莫过于@Override、@Deprecated、@SuppressWarnings三个,下面分别介绍这三个常见的java内置注解的作用

    @Override

    @Override注解修饰于方法上,表明当前方法不是当前类首先申明的,而是由父类或者接口中继承来的方法,并且当前类进行了方法重写操作,比如:

    public class Base {

    public void action() {};

    }

    现在有一个父类Base,申明一个方法--action,现在有一个子类继承了Base类,并且重写了action方法的内部实现,如下:

    public class Child extends Base {

    @Override

    public void action(){

    System.out.println("child action");

    }

    }

    可以看到在Child类的action方法上有一个@Override注解,代表着此方法是Child类重新实现并且继承于Base类,但是当我们把这个注解删除,发现工程无论是编译还是运行,和之前一般无二,那么编译器为什么还要给我们默认添加这个注解呢?其实我们可以反过来思考,如果我们的类中有一个方法,并且使用了@Override注解进行修饰,但是当前类并没有从别的类或者接口继承来这个方法,或者此类本身就是独立的类,这个时候会发生什么呢?

    public class Child extends Base {

    @Override

    public void action(){

    System.out.println("child action");

    }

    //action1方法在Base中不存在

    @Override

    public void action1(){

    System.out.println("child class");

    }

    }

    很明显当我们加上注解的瞬间,编译器就会直接提示异常,要求我们删除当前注解。从这可以看出来,@Override注解有助于帮助编译器检查语法错误,执行更严格的代码检查。

    @Deprecated

    @Deprecated注解可以修饰的范围很广,不仅可以作用在方法上,还可以修饰类、字段以及参数等所有注解可以修饰的范围,此注解代表被修饰的元素已经过时,并且警告使用者建议使用其他方法,不再使用当前方法。例如,Date类中就有很多方法被标识已经过时了:

    @Deprecated

    public Date(int year, int month, int date){

    .....

    }

    @Deprecated

    public int getYear(){

    .....

    }

    当我们在使用这些方法的时候,ide往往会给我们加上一个删除线用来辅助提示调用者此方法已经过时,随时都可能在下个版本删除,不建议使用。而在Java9中,Deprecated注解又多了可填的属性,分别是sinceforRemoval,since属性类型为String类型,表示被修饰的元素从哪一个版本号开始过时,不建议使用,而forRemoval属性为Boolean类型,表示将来在过时的时候是否会删除当前元素,标记为true代表将来会删除此元素,使用者请慎用。而在Java9中,Integer包装类的构造方法中就有使用此特性标识的,如下:

    @Deprecated(since="9")

    public Integer(int value) {

    this.value = value;

    }

    @SuppressWarnings

    @SuppressWarnings注解则是表示压制Java中的编译警告措施,使得编译器放弃被修饰元素的编译警告,此注解有一个必填参数,表示压制的是哪种类型的警告,此注解也可以使用在几乎所有元素中。比如我们在开发中使用了Date类中的一些过期或者有异常抛出风险的方法,ide往往就会添加一个警告的黄线,而我们在方法上添加@SuppressWarnings注解,则会发现警告消失不见,如下:

    //添加在方法上,取消掉编译器的严格检查警告机制

    @SuppressWarnings({"deprecation","unused"})

    public static void main(String[] args) {

    Date date = new Date(2019, 10, 12);

    int year = date.getYear();

    }

    常见的库中的注解

    日常开发使用的库中也有着大量的注解,例如Jackson、SpringMvc等,下面就简单介绍下常见库中的常见注解使用

    Jackson

    Jackson是一个通用的序列化库,程序员使用过程中可以使用它提供的注解机制对序列化进行定制化操作,比如:

    .使用@JsonIgnore和@JsonIgnoreProperties配置序列化的过程中忽略部分字段

    .使用@JsonManagedReference和@JsonBackReference可以配置实例之间的互相引用

    .使用@JsonProperty和@JsonFormat配置序列化的过程中字段名称和属性字段的格式等

    Servlet3.0

    随着web开发技术的发展,Java web已经发展到了Servlet3.0,在早期使用Servlet的时候,我们只能在web.xml中配置,但是当我们使用Servlet3.0的时候开始,已经开始支持注解了,比如我们可以使用@WebServlet配置一个类为Servlet类,如下:

    @WebServlet(urlPatterns = "/async", asyncSupported = true)

    public class AsyncDemoServlet extends HttpServlet {

    //..............

    }

    SpringMvc

    同样的,在web开发中,我们往往还会使用SpringMvc框架来简化开发,其框架的大量注解可以帮助我们减少大量的业务代码,例如一个请求的参数和字段/实例之间的映射关系,一个方法使用的是Http的什么请求方法,对应请求的某个路径,同样的请求如何解析,返回的响应报文格式定义等,这些都可以使用注解来简化实现,一个简单的Mvc操作如下:

    @Controller

    @RequestMapping("/hello")

    public class HelloController {

    @GetMapping("/test")

    @ResponseBody

    public String test(){

    return "hello test";

    }

    }

    其中@Controller注解标明当前的类是SpringMvc接管的一个Bean实例,@RequestMapping("/hello")则是代表当前Bean的前置请求路径比如是/hello开头, @GetMapping("/test")则是表示test方法被访问必须是Http请求的get请求,并且路径必须是/hello/test为路径前置,@ResponseBody注解则是标明了当前请求的相应信息按照默认的格式返回(根据后缀名来确定格式)

    注解的创建

    从上面我们可以看到使用注解的确可以很方便的简化我们开发过程,因此很多库和开发过程中,也会使用大量的注解简化开发,那么这些注解我们如何实现呢?首先我们先看看最常见的注解@Override的创建:

    @Target(ElementType.METHOD)

    @Retention(RetentionPolicy.SOURCE)

    public @interface Override {

    }

    从上面我们可以看到,Override类中使用了@Target元注解和@Retention元注解来定义整个注解,@Target表示需要注解的目标,@Retention则是标明当前注解的信息可以保留到Java的什么阶段,而除了这两个元注解以外,还有两个元注解@Documented和@Inherited,@Documented用来表示当前的注解信息是否包含到文档中,@Inherited注解则是和注解之间的继承有对应的关系,那么这些元注解具体有什么作用,以及具体有哪些参数可以选择呢?接下来我们便分别学习一下

    @Target

    @Target注解表示当前注解可以使用在什么类型的元素上,这里的值可以多选,即一个注解可以作用在多种不同类型的元素上,具体的可选值在ElementType枚举类中,值如下:

    取值解释
    TYPE表示作用在类、接口上
    FIELD表示作用在字段,包括枚举常量中
    METHOD表示作用在方法中
    PARAMETER表示作用在方法中的参数中
    CONSTRUCTOR表示作用在构造方法中
    LOCAL_VARIABLE表示作用在本地常量中
    MODULE表示作用在部分模块中(Java9引入的概念)
    ANNOTATION_TYPE表示当前注解作用在定义其他注解中,即元注解
    PACKAGE表示当前注解使用在包的申明中
    TYPE_PARAMETER表明当前注解使用在类型参数的申明中(Java8新增)
    TYPE_USE表明当前注解使用在具体使用类型中(Java8新增)

    当使用多个作用域范围的时候,使用{}包裹多个参数,比如@SuppressWarnings注解的Target就有多个,在Java7中的定义为:

    @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})

    @Retention(RetentionPolicy.SOURCE)

    public @interface SuppressWarnings {

    String[] value();

    }

    即代表SuppressWarnings注解均可以作用在这七种元素上

    @Retention

    @Retention注解则是表明了当前注解可以保留到Java多个阶段的哪一个阶段,参数类型为RetentionPolicy枚举类,可取值如下:

    取值解释
    SOURCE此注解仅在源代码阶段保留,编译后即丢失注解部分
    CLASS表示编译后依然保留在Class字节码中,但是加载时不一定会在内存中
    RUNTIME表示不仅保留在Class字节码中,一直到内存使用时仍然存在

    此注解有默认值,即当我们没有申明@Retention的时候,默认则是Class取值范围

    @Documented

    @Documented注解没有具体的参数,使用此元注解,则表示带有类型的注解将由javadoc记录

    @Inherited

    @Inherited注解与注解的继承有关系,具体关系为如果使用了当前的元注解,则表示此注解可以被其他的注解的子类直接继承,但是需要注意的是对已实现接口上的注解将没有作用。

    我们通过一个案例来了解@Inherited注解的作用,首先我们定义一个Test注解:

    @Inherited

    @Retention(RetentionPolicy.RUNTIME)

    public @interface Test {

    }

    Test注解上有@Inherited元注解修饰,则表明Test注解会被继承,接着我们在Base类上使用Test注解:

    @Test

    public class Base {

    }

    这个时候实现一个子类Child,我们来通过反射类Class中的isAnnotationPresent方法打印,看此类中的Test是否存在且是来自于父类继承而来:

    public class Child extends Base {

    }

    -----------------------------------

    public static void main(String[] args) {

    System.out.println(Child.class.isAnnotationPresent(Test.class));//true

    }

    最后输出的结果为true,则表明Child类中存在@Test注解,并且是由继承父类而来

    使用注解实现简单定制序列化

    上面我们已经学习了注解定义和创建相关的内容,接下来我们利用注解简单实现通用格式化输出的类SimpleFormatter,类中有一个方法format,方法参数为Object类型的实例对象,即表明了对象的序列化方式,类定义如下:

    /**

    * 通用格式转换输出类

    */

    public class SimpleFormatter {

    /**

    * 通用格式化方法==>将obj对象输出为String

    * @param obj

    * @return

    */

    public static String format(Object obj){

    try{

    Class> cls = obj.getClass();

    StringBuilder builder = new StringBuilder();

    for (Field field : cls.getDeclaredFields()) {

    if(!field.isAccessible()){

    field.setAccessible(true);//放弃java安全检测,设置可以访问私有字段

    }

    //获取Label注解-输出的字段名称

    Label label = field.getAnnotation(Label.class);

    String name = null == label ? field.getName() : label.value();

    //获取字段对应的value

    Object value = field.get(obj);

    //如果是Date类型,走时间格式化

    if(null != value && field.getType() == Date.class){

    value = formatter(field,value);

    }

    builder.append(name + "?" + value + "\n");

    }

    return builder.toString();

    }catch (Exception e){

    e.printStackTrace();

    throw new RuntimeException("格式化输出失败:"+e.getMessage());

    }

    }

    /**

    * 针对时间类型字段进行格式化的方法

    */

    private static Object formatter(Field field, Object value) {

    Format format = field.getAnnotation(Format.class);

    if(null == format){

    return value;

    }

    String pattern = format.pattern();

    String timezone = format.timezone();

    SimpleDateFormat sdf = new SimpleDateFormat(pattern);

    sdf.setTimeZone(TimeZone.getTimeZone(timezone));

    return sdf.format(value);

    }

    }

    而除了格式化的类以外,我们还定义了两个注解,一个用来表明格式化的时候的字段名称,一个用来针对时间格式字段输出的格式,如下:

    /**

    * Labl表明当前字段输出的名称,仅作用在字段上

    */

    @Retention(RUNTIME)

    @Target(FIELD)

    public @interface Label {

    String value() default "";

    }

    --------------------------------

    /**

    * Format注解作用在字段上,针对时间字段类型的输出格式

    */

    @Retention(RUNTIME)

    @Target(FIELD)

    public @interface Format {

    String pattern() default "yyyy-MM-dd HH:mm:ss";

    String timezone() default "GMT+8";

    }

    除此之外,我们还需要一个实例类:

    public class Student {

    @Label("姓名")

    private String name;

    @Label("出生日期")

    @Format(pattern="yyyy/MM/dd")

    private Date born;

    @Label("分数")

    private double score;

    public String getName() {

    return name;

    }

    public void setName(String name) {

    this.name = name;

    }

    public Date getBorn() {

    return born;

    }

    public void setBorn(Date born) {

    this.born = born;

    }

    public double getScore() {

    return score;

    }

    public void setScore(double score) {

    this.score = score;

    }

    }

    定义完毕后,我们可以这么来使用:

    public static void main(String[] args) throws ParseException {

    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    Student zhangsan = new Student();

    zhangsan.setName("张三");

    zhangsan.setBorn(format.parse("1990-12-12"));

    zhangsan.setScore(655);

    System.out.println(SimpleFormatter.format(zhangsan));

    }

    输出的结果为:

    姓名?张三

    出生日期?1990/12/12

    分数?655.0

    至此,一个简单的格式化输出类就完成了

    fbd84626a8bf1c5e0efb776bec3aa175.png

    万水千山总是情,点个在看行不行498cc9bffe4647106a195505ac0f4a35.png

    展开全文
  • 一、什么是Java线程状态在Java程序中,用于描述Java线程的六种状态:新建(NEW):当前线程,刚刚新建出来,尚未启动。运行(RUNNABLE):当前线程,处于竞争CPU时间分片或已经获得CPU时间片的状态。等待(WAITTING):...

    一、什么是Java线程状态

    在Java程序中,用于描述Java线程的六种状态:

    • 新建(NEW):当前线程,刚刚新建出来,尚未启动。

    • 运行(RUNNABLE):当前线程,处于竞争CPU时间分片或已经获得CPU时间片的状态。

    • 等待(WAITTING):当前线程,处于休眠,不参与CPU时间片竞争的状态。

    • 定时等待(TIMED_WAITTING):当前线程,处于定时休眠,暂时不参与CPU时间片竞争的状态。

    • 阻塞(BLOCKED):当前线程,处于阻塞,不参与CPU时间片竞争的状态。

    • 终止(TERMINATED):当前线程,处于最终停止的状态。

    新建状态,只能进入运行状态。而终止状态无法再转为其他状态。

    等待/定时等待与阻塞,差别就是后者需要一个事件信号(如其他线程放弃当前线程需要的排他锁),才可以进行状态切换。当然,强行关闭也是可以的。

    Java线程的实现并不受JVM规范约束,故不同虚拟机的实现,往往不同。目前主流的HotSpot是将每个Java线程直接映射到一个操作系统的原生线程,从而由操作系统完成一系列的线程调度

    二、哪里看Java线程状态

    查看Java线程状态,主要存在三种方式:

    • java.lang.Thread.State下可以直接看到Java的六种线程状态

    • Java运行时,程序内部可以通过Thread.getState()获取目标线程状态

    • Java运行时,程序外部可以通过jstack等工具,查看线程状态

    有关jstack等工具等使用,后续会有博客,专门阐述。

    三、什么时候变换Java线程状态

    Java线程状态的切换嘛。不啰嗦,直接上图。d88e671a1a0cb2b74c19659f76cc1907.png这张图涵盖了Java线程状态切换的各类方法。相较网上一些图片,更为详尽一些。
    如果有所遗漏,可以告诉我,我会及时填补上。

    四、谁在使用Java线程状态

    日常开发中,我们并不会直接与线程状态进行交互。
    我们往往直接使用JDK包装好的工具,如JUC包下的各类工具等。

    举个栗子

    线程池中的应用

    位置:com.sun.corba.se.impl.orbutil.threadpool.ThreadPoolImpl#close


        // Note that this method should not return until AFTER all threads have died.
        public void close() throws IOException {

            // Copy to avoid concurrent modification problems.
            List copy = null;
            synchronized (workersLock) {
                copy = new ArrayList<>(workers);
            }for (WorkerThread wt : copy) {
                wt.close();while (wt.getState() != Thread.State.TERMINATED) {
                    try {
                        wt.join();
                    } catch (InterruptedException exc) {
                        wrapper.interruptedJoinCallWhileClosingThreadPool(exc, wt, this);
                    }
                }
            }
            threadGroup = null;
        }

    实际查看JDK后发现,JDK中其实也没有辣么多的实例,并且大多数直接关联线程状态的,也是一些状态查看的东东。
    这在文章开头就说,Java线程操作,是很底层的,甚至其实现都不包含在虚拟机规范中。
    主流的HotSpot,也是直接将Java线程映射到系统线程,由系统进行一系列的线程调度处理。
    所以,在JDK中,是直接对线程状态进行处理的部分是很少的。

    五、为什么需要线程状态

    1.为什么需要线程状态这一概念

    这个问题,可以从两个角度来说明:生命周期与资源管理

    • 一方面,线程状态很好地刻画了线程的整个生命周期,对生命周期中不同阶段进行了有效划分。

    • 另一方面,资源是有限的,需求是无限的。所以需要将系统资源有意识地进行调度,合理利用比较优势,追求帕累托最优。

    实现后者的,就是利用线程在生命周期的不同阶段这一天然属性带来的状态刻画,进行的分组。CPU调度只需要关注运行状态的线程。而阻塞,等待等线程,都有着属于自己的一套处理方式。最终获得资源(开发时对复杂性的应对,运行时对系统资源对消耗,应用者心智模型的成长等)的优化分配。

    2.JDK中为什么需要定义Java线程状态

    前文有说到,Java中很少直接使用到线程状态。那么为什么还要在JDK中定义Java的六种线程状态呢?
    一方面,通过信息透明的方式,降低使用者使用时建立心智模型的成本。如,现在的我们可以通过打印日志,打断点,快速了解Java的各个线程状态,并清楚了解产生的原因。从而大大提高了我们对Java线程的认识,进而更为愉快地拥抱JUC包下诸如线程池等工具。
    另一方面,通过可以直接应用的状态枚举,我们可以很好地对现有工具进行二次开发等。如我们可以通过扩展AQS,并在其中添加线程状态的校验,从而得到定制化的线程同步工具。

    六、如何使用线程状态

    使用的方式,我已经在上文说了:学习Java线程,定制线程相关工具开发。
    这里给出一个有关线程学习的demo:


    /**
     * @program: learning
     * @description: 用于确认线程状态问题
     * @author: Jarry
     * @create: 2020-10-26 22:25
     **/
    public class ThreadState {

        public static void main(String[] args) {
            threadStateTest();
    //        threadStateTest2();
    //        threadStateWithBlocked();
    //        threadStateWithException();
    //        threadStateWithSuspend();
        }

        /**
         * 实践证明:Thread.suspend()与Thread.resume()不会改变线程状态
         * 线程状态该是Waiting,就Waiting。该Timed_Waiting就Timed_Waiting
         * Thread.suspend()与Thread.resume()只是挂起目标线程(并且不会释放锁资源)
         */
        private static void threadStateWithSuspend() {
            Thread thread1 = new Thread(() -> {
    //            LockSupport.park();
                LockSupport.parkNanos(2000000000);
            });

            thread1.start();
            printThreadState(thread1);
            LockSupport.parkNanos(500000000);
            printThreadState(thread1);
            thread1.suspend();
            printThreadState(thread1);
            LockSupport.parkNanos(500000000);
            printThreadState(thread1);
            thread1.resume();
            LockSupport.parkNanos(500000000);
            printThreadState(thread1);

    //        LockSupport.unpark(thread1);
        }

        /**
         * 展现线程阻塞状态
         */
        private static void threadStateWithBlocked() {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    synchronized (ThreadState.class) {
    //                    LockSupport.parkNanos(2000000000);
                        LockSupport.park();
                    }
                }
            };

            Thread thread1 = new Thread(runnable);
            Thread thread2 = new Thread(runnable);

            thread1.start();
            LockSupport.parkNanos(500000000);
            thread2.start();
            // 加上以下时间间隔,则结果:Runnable->Blocked
            // 推论:Thread.start()会将线程状态设置为Runnable,然后在遇到sync的锁,再切换为Blocked状态
    //        LockSupport.parkNanos(500000000);

            printThreadState(thread2);
            LockSupport.parkNanos(500000000);
            printThreadState(thread2);

            LockSupport.parkNanos(500000000);
            LockSupport.unpark(thread1);
            LockSupport.unpark(thread2);

        }

        /**
         * 由于底层实现机制的不同(相较于其他waiting的方法),无法直接进行Object.wait(),否则会抛出以下异常
         * @exception java.lang.IllegalMonitorStateException
         * object.wait()进行wait的方法,是直接对其wait_set进行操作
         */
        private static void threadStateWithException() {
            Thread thread1 = new Thread(() -> {
                try {
                    ThreadState.class.wait(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            });

            thread1.start();
            LockSupport.parkNanos(1000000000);
            printThreadState(thread1);

        }

        /**
         * Object.wait()的使用
         */
        private static void threadStateTest3() {
            Thread thread1 = new Thread(() -> {
                synchronized (ThreadState.class) {
                    try {
                        ThreadState.class.wait(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });

            thread1.start();
            LockSupport.parkNanos(1000000000);
            printThreadState(thread1);

        }

        /**
         * 确定LockSupport.parkNacos()是否可以生成Time_Waiting状态
         */
        private static void threadStateTest2() {
            Thread thread1 = new Thread(() -> {
                LockSupport.parkNanos(2000000000);
            });

            thread1.start();
            printThreadState(thread1);
            LockSupport.parkNanos(1000000000);

            printThreadState(thread1);
        }

        /**
         * 查看到除Blocked以外的所有线程状态
         */
        private static void threadStateTest() {
            Thread thread1 = new Thread(() -> {
                synchronized (ThreadState.class) {
                    // Runnable
                    printThreadState(Thread.currentThread());

                    // 1.Thread.sleep(time)
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                    // 2.Object.wait(time)
    //                try {
    //                    ThreadState.class.wait(2000);
    //                } catch (InterruptedException e) {
    //                    e.printStackTrace();
    //                }
                    // 3.Thread.join(time)
    //                try {
    //                    Thread.currentThread().join(2000);
    //                } catch (InterruptedException e) {
    //                    e.printStackTrace();
    //                }
                    // 4.LockSupport.parkNanos(time);
    //            LockSupport.parkNanos(2000000000);
                    // 5.LockSupport.parkUntil(timeStamp);
    //            LockSupport.parkUntil(System.currentTimeMillis()+2000);

                    LockSupport.park();
                }

            });
            thread1.setName("test_thread");

            // New
            printThreadState(thread1);

            thread1.start();
            LockSupport.parkNanos(1000000000);
            // Timed_waiting
            printThreadState(thread1);

            LockSupport.parkNanos(2000000000);
            // Waiting
            printThreadState(thread1);

            LockSupport.unpark(thread1);

            LockSupport.parkNanos(1000000000);
            // Terminated
            printThreadState(thread1);


        }

        private static void printThreadState(Thread thread) {
            System.out.println("current Thread(" + thread.getName()+":" + thread.getId() + ") state:" + thread.getState());
        }

    }

    代码中有一些细碎的知识点,就不在这里赘述了。感兴趣的小伙伴,可以自己看看注释,自行验证。

    七、扩展:系统状态(三态&五态)

    操作系统就包含进程管理,作业管理,文件管理等。其中进程管理,就涉及系统状态的三态模型与五态模型。
    其中,三态模型包含以下三种状态:

    • 就绪状态

    • 运行状态

    • 阻塞状态

    而五态模型则包含以下五种状态:

    • 运行状态

    • 静止就绪态

    • 活跃就绪态

    • 静止阻塞态

    • 活跃阻塞态

    具体状态切换等,可以看我之前写的一篇博客:
    系统架构设计师-操作系统

    最后,愿与诸君共进步。

    八、附录

    补充:WAITTING/TIMED_WAITTING与BLOCKED的区别

    其实,我之前也挺纠结这个问题的。
    当时的想法是WAITTING/TIMED_WAITTING是由JVM自己维持,而BLOCKED是由系统维持的。后面看到主流的HotSpot是将线程映射到系统原生线程的,所以这个想法大概率是错误的。
    那么两者区别是什么呢?
    直到我在上文的示例代码中,BLOCKED状态的线程,在其他线程释放其所需的锁时,该线程是先转为RUNNING状态,再变为其他状态。这引起我的注意。
    最后在stackOverFlow的一篇文章(Difference between WAIT and BLOCKED thread states)中,看到这样的解释:

    The important difference between the blocked and wait states is the impact on the scheduler. A thread in a blocked state is contending for a lock; that thread still counts as something the scheduler needs to service, possibly getting factored into the scheduler's decisions about how much time to give running threads (so that it can give the threads blocking on the lock a chance).
    Once a thread is in the wait state the stress it puts on the system is minimized, and the scheduler doesn't have to worry about it. It goes dormant until it receives a notification. Except for the fact that it keeps an OS thread occupied it is entirely out of play.
    This is why using notifyAll is less than ideal, it causes a bunch of threads that were previously happily dormant putting no load on the system to get woken up, where most of them will block until they can acquire the lock, find the condition they are waiting for is not true, and go back to waiting. It would be preferable to notify only those threads that have a chance of making progress.
    (Using ReentrantLock instead of intrinsic locks allows you to have multiple conditions for one lock, so that you can make sure the notified thread is one that's waiting on a particular condition, avoiding the lost-notification bug in the case of a thread getting notified for something it can't act on.)

    简单说,就是CPU时间片不会考虑WAITTING/TIMED_WAITTING状态。
    但是,虽然BLOCKED状态的线程无法获得CPU时间片,但是系统调度时,依旧会考虑BLOCKED状态的线程,将其置于调度计算中。

    如果哪位小伙伴对这方面有了解,希望可以聊一聊。

    651116726916eb19b4f9a61341cba4d3.png

    展开全文
  • t.suspend()是过时方法,使用suspend()导致线程进入停滞状态,该线程会一直持有对象的监视器,suspend()容易引起死锁问题。 object.wait()使当前线程出于“不可运行”状态,和sleep()不同的是wait是object的方法而...

    f9ce5bbc84cbfe963ee348e8d075e7eb.png

    121,什么是线程?

    线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒。

    122,线程和进程有什么区别?

    线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。每个线程都拥有单独的栈内存用来存储本地数据。

    123,如何在Java中实现线程?

    两种方式:java.lang.Thread 类的实例就是一个线程但是它需要调用java.lang.Runnable接口来执行,由于线程类本身就是调用的Runnable接口所以你可以继承java.lang.Thread 类或者直接调用Runnable接口来重写run()方法实现线程。

    124,Java 关键字volatile 与 synchronized 作用与区别?

    1,volatile 它所修饰的变量不保留拷贝,直接访问主内存中的。
    在Java内存模型中,有main memory,每个线程也有自己的memory (例如寄存器)。为了性能,一个线程会在自己的memory中保持要访问的变量的副本。这样就会出现同一个变 量在某个瞬间,在一个线程的memory中的值可能与另外一个线程memory中的值,或者main memory中的值不一致的情况。 一个变量声明为volatile,就意味着这个变量是随时会被其他线程修改的,因此不能将它cache在线程memory中。
    2,synchronized

    当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

    一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

    二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。

    三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

    四、当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。

    五、以上规则对其它对象锁同样适用.

    125,有哪些不同的线程生命周期?

    当我们在Java程序中新建一个线程时,它的状态是New。当我们调用线程的start()方法时,状态被改变为Runnable。线程调度器会为Runnable线程池中的线程分配CPU时间并且讲它们的状态改变为Running。其他的线程状态还有Waiting,BlockedDead

    126,你对线程优先级的理解是什么?

    每一个线程都是有优先级的,一般来说,高优先级的线程在运行时会具有优先权,但这依赖于线程调度的实现,这个实现是和操作系统相关的(OS dependent)。我们可以定义线程的优先级,但是这并不能保证高优先级的线程会在低优先级的线程前执行。线程优先级是一个int变量(从1-10),1代表最低优先级,10代表最高优先级。

    127,什么是死锁(Deadlock)?如何分析和避免死锁?

    死锁是指两个以上的线程永远阻塞的情况,这种情况产生至少需要两个以上的线程和两个以上的资源。

    分析死锁,我们需要查看Java应用程序的线程转储。我们需要找出那些状态为BLOCKED的线程和他们等待的资源。每个资源都有一个唯一的id,用这个id我们可以找出哪些线程已经拥有了它的对象锁。

    避免嵌套锁,只在需要的地方使用锁和避免无限期等待是避免死锁的通常办法。

    128,什么是线程安全?Vector是一个线程安全类吗?

    如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。一个线程安全的计数器类的同一个实例对象在被多个线程使用的情况下也不会出现计算失误。很显然你可以将集合类分成两组,线程安全和非线程安全的。Vector 是用同步方法来实现线程安全的, 而和它相似的ArrayList不是线程安全的。

    129,Java中如何停止一个线程?

    Java提供了很丰富的API但没有为停止线程提供API。JDK 1.0本来有一些像stop(), suspend() 和 resume()的控制方法但是由于潜在的死锁威胁因此在后续的JDK版本中他们被弃用了,之后Java API的设计者就没有提供一个兼容且线程安全的方法来停止一个线程。当run() 或者 call() 方法执行完的时候线程会自动结束,如果要手动结束一个线程,你可以用volatile 布尔变量来退出run()方法的循环或者是取消任务来中断线程

    130,什么是ThreadLocal?

    ThreadLocal用于创建线程的本地变量,我们知道一个对象的所有线程会共享它的全局变量,所以这些变量不是线程安全的,我们可以使用同步技术。但是当我们不想使用同步的时候,我们可以选择ThreadLocal变量。

    每个线程都会拥有他们自己的Thread变量,它们可以使用get()set()方法去获取他们的默认值或者在线程内部改变他们的值。ThreadLocal实例通常是希望它们同线程状态关联起来是private static属性。

    131,Sleep()、suspend()和wait()之间有什么区别?

    Thread.sleep()使当前线程在指定的时间处于“非运行”(Not Runnable)状态。线程一直持有对象的监视器。比如一个线程当前在一个同步块或同步方法中,其它线程不能进入该块或方法中。如果另一线程调用了interrupt()方法,它将唤醒那个“睡眠的”线程。

    注意:sleep()是一个静态方法。这意味着只对当前线程有效,一个常见的错误是调用t.sleep(),(这里的t是一个不同于当前线程的线程)。即便是执行t.sleep(),也是当前线程进入睡眠,而不是t线程。t.suspend()是过时的方法,使用suspend()导致线程进入停滞状态,该线程会一直持有对象的监视器,suspend()容易引起死锁问题。

    object.wait()使当前线程出于“不可运行”状态,和sleep()不同的是wait是object的方法而不是thread。调用object.wait()时,线程先要获取这个对象的对象锁,当前线程必须在锁对象保持同步,把当前线程添加到等待队列中,随后另一线程可以同步同一个对象锁来调用object.notify(),这样将唤醒原来等待中的线程,然后释放该锁。基本上wait()/notify()与sleep()/interrupt()类似,只是前者需要获取对象锁。

    132,什么是线程饿死,什么是活锁?

    当所有线程阻塞,或者由于需要的资源无效而不能处理,不存在非阻塞线程使资源可用。JavaAPI中线程活锁可能发生在以下情形:

    1,当所有线程在程序中执行Object.wait(0),参数为0的wait方法。程序将发生活锁直到在相应的对象上有线程调用Object.notify()或者Object.notifyAll()。

    2,当所有线程卡在无限循环中。

    133,什么是Java Timer类?如何创建一个有特定时间间隔的任务?

    java.util.Timer是一个工具类,可以用于安排一个线程在未来的某个特定时间执行。Timer类可以用安排一次性任务或者周期任务。

    java.util.TimerTask是一个实现了Runnable接口的抽象类,我们需要去继承这个类来创建我们自己的定时任务并使用Timer去安排它的执行。

    134,Java中的同步集合与并发集合有什么区别?

    同步集合与并发集合都为多线程和并发提供了合适的线程安全的集合,不过并发集合的可扩展性更高。

    在Java1.5之前程序员们只有同步集合来用且在多线程并发的时候会导致争用,阻碍了系统的扩展性。

    Java5介绍了并发集合像ConcurrentHashMap,不仅提供线程安全还用锁分离和 内部分区等现代技术提高了可扩展性。

    135,同步方法和同步块,哪个是更好的选择?

    同步块是更好的选择,因为它不会锁住整个对象(当然你也可以让它锁住整个对象)。同步方法会锁住整个对象,哪怕这个类中有多个不相关联的同步块,这通常会导致他们停止执行并需要等待获得这个对象上的锁。

    136,什么是线程池? 为什么要使用它?

    创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限。

    为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程。

    从JDK1.5开始,Java API提供了Executor框架让你可以创建不同的线程池。比如单线程池,每次处理一个任务;数目固定的线程池或者是缓存线程池(一个适合很多生存期短的任务的程序的可扩展线程池)。

    137,Java中invokeAndWait 和 invokeLater有什么区别?

    这两个方法是Swing API 提供给Java开发者用来从当前线程而不是事件派发线程更新GUI组件用的。InvokeAndWait()同步更新GUI组件,比如一个进度条,一旦进度更新了,进度条也要做出相应改变。如果进度被多个线程跟踪,那么就调用invokeAndWait()方法请求事件派发线程对组件进行相应更新。而invokeLater()方法是异步调用更新组件的。

    138,多线程中的忙循环是什么?

    忙循环就是程序员用循环让一个线程等待,不像传统方法wait(), sleep() 或 yield() 它们都放弃了CPU控制,而忙循环不会放弃CPU,它就是在运行一个空循环。这么做的目的是为了保留CPU缓存。

    在多核系统中,一个等待线程醒来的时候可能会在另一个内核运行,这样会重建缓存。为了避免重建缓存和减少等待重建的时间就可以使用它了。

    展开全文
  • C&num;应用Newtonsoft&period;Json操作jsonNewtonsoft.Json是一个开源的C#操作json的项目,应用起来非常简单.... 下面的代码演示了如何应用Newtonsoft.Json序列号和反序列化. using Newt ...checking it the ...
  • Java使用注释标记过时方法

    千次阅读 2019-08-15 10:40:24
    package Test; public class Test { @Deprecated ... //过时方法示例 } public void newMethod() { //正常方法 } public static void main(String[] args) { Test test = new Test...
  • 话不多说,先上图,先看看Thread类中的public方法。Thread类所有public方法打扰了。。。构造器常用的构造器有Thread()、Thread(String name)、Thread(Runnable target),看构造器不难发现,Thread类实例化都是调用了...
  • Java一些过时方法与替换(不定时更新) 1. MediaType jdk8已过时 /** @deprecated */ @Deprecated public static final MediaType APPLICATION_JSON_UTF8; /** @deprecated */ @Deprecated public static ...
  • 但可惜的是这两个方法都存在缺陷而被java所弃用(印象中好像是Java SE 1.2之后就被弃用,有点记不清了),还在傻傻使用的同学们需要注意了。Stop方法Stop被弃用是因为“天生”就不安全,它会终止所有未结束的方法,...
  • 该子类应重写 Thread 类的 run 方法方式二:声明实现 Runnable 接口的类。该类然后实现 run 方法推荐方式二,因为接口方式比继承方式更灵活,也减少程序间的耦合。3、获取当前线程信息?Thread.currentThread()4、...
  • package org.apache.poi.hssf.util HSSFColor.BLACK.index -> IndexedColors.BLACK.index package org.apache.http.client HttpClient httpClient = new DefaultHttpClient() -> CloseableHttpClient ...
  • 为了让大家更清晰读懂关于Java多线程用法,由以下几点入手学,帮助你更透彻地理解Java多线程的原理以及使用方法:创建线程线程的生命周期多线程安全问题1.创建线程在Java中创建线程有两种方法:使用Thread类和使用...
  • 如何使用C#将方法标记为过时过时?#1楼最简单的方法是将ObsoleteAttribute作为方法的属性添加。 确保包括适当的解释:[Obsolete("Method1 is deprecated, please use Method2 instead.")]public void Method1(){ ...
  • DateFormat ddf = DateFormat.getDateInstance();DateFormat dtf = DateFormat.getTimeInstance();DateFormat ddtf = DateFormat.getDateTimeInstance();Date date = new Date();System.out.println("日期:" + ddf....
  • java方法调用提示此方法过时

    千次阅读 2020-04-20 15:33:51
    写代码的时候用到了new Date(String s)被提示方法已过时 点进去看到了@Deprecated注解 被标注已经...作为调用方来说,有义务去考证过时方法的新实现是什么。 此注解可用于类上、方法上、属性上。暂时可用,后期...
  • t.suspend()是过时方法,使用suspend()导致线程进入停滞状态,该线程会一直持有对象的监视器,suspend()容易引起死锁问题。 object.wait()使当前线程出于“不可运行”状态,和sleep()不同的是wait是object的方法而...
  • 过时方法java.awt.Component.action(Event, Object)从 JDK version 1.1 开始,应该将此组件作为组件(激发动作事件)上的 ActionListener 来注册。java.awt.List.allowsMultipleSelections()从 JDK version 1.1 ...
  • 1.并发编程基础1.1 基本概念CPU核心与线程数关系 Java中通过多线程的手段来实现并发,对于单处理器机器上来讲,宏观上的多线程并行执行是通过CPU的调度来实现的,微观上CPU在某个时刻只会运行一个线程。事实上,如果...
  • 切记我说的不要花太多时间,是指你在短时间想要找到工作,如果你有足够的时间,比如一年以上,请你每天坚持刷算法,编程语言也许会过时,但是算法与数据结构却不会过时,而且越好的公司越看重算法。 第四部分:提升 ...
  • 作者|Java极客技术|微信公众号面试官:你不是精通 Java 并发吗?从基础的 Java 线程生命周期开始讲讲吧。好的,面试官。吧啦啦啦...如果要说 Java 线程的生命周期的话,那我觉得就要先说说操作系统的线程生命周期...
  • java 标志一个方法为过时方法

    万次阅读 2015-04-16 11:11:12
    使用 Deprecated 来标记方法 @Deprecated //用来判断ip是否合法 public boolean checkIp(String tempIp) { String regex = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)
  • 过时date.toLocaleString()的解决方法 Java代码 收藏代码 System.out.println(new java.util.Date()); 输出:Thu Jan 27 14:43:28 CST 2011 Java代码 收藏代码 System.out.println(new java.util.Date()....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,822
精华内容 728
关键字:

java过时方法

java 订阅