Java多线程编程概述
Java多线程的安全问题
Java多线程同步
Java多线程间的通信
Java线程Lock
Java多线程管理
保障线程安全的设计技术
Java锁的优化及注意事项
Java多线程集合
【Java多线程】单例模式与多线程

Java多线程常用的方法

currentThread()方法

Thread.currentThread()方法可以获得当前线程。

Java中的任何一段代码都是执行在某个线程当中的. 执行当前代码的线程就是当前线程。

同一段代码可能被不同的线程执行,因此当前线程是相对的,Thread.currentThread()方法的返回值是在代码实际运行时候的线程对象。

package com.wkcto.threadmehtod;

/**
 * 定义线程类
 *      分别在构造方法中和run方法中打印当前线程
 * Author : 蛙课网老崔
 */
public class SubThread1 extends Thread {
    public SubThread1(){
        System.out.println("构造方法打印当前线程的名称: " + Thread.currentThread().getName());
    }

    @Override
    public void run() {
        System.out.println("run方法打印当前线程名称:" + Thread.currentThread().getName());
    }
}
package com.wkcto.threadmehtod;

/**
 * 测试当前线程
 * Author : 蛙课网老崔
 */
public class Test01CurrentThread {
    public static void main(String[] args) {
        System.out.println("main方法中打印当前线程:"  + Thread.currentThread().getName());

        //创建子线程, 调用SubThread1()构造方法, 在main线程中调用构造方法,所以构造方法中 的当前线程就是main线程
        SubThread1 t1 = new SubThread1();
//        t1.start();         //启动子线程,子线程会调用run()方法,所以run()方法中 的当前线程就是Thread-0子线程

        t1.run();   //在main方法中直接调用run()方法,没有开启新的线程,所以在run方法中的当前线程就是main线程

    }
}
package com.wkcto.threadmehtod;

/**
 * 当前线程的复杂案例
 * Author : 蛙课网老崔
 */
public class SubThread2 extends  Thread {
    public SubThread2(){
        System.out.println("构造方法中,Thread.currentThread().getName() : " + Thread.currentThread().getName() );
        System.out.println("构造方法,this.getName() : " + this.getName());
    }

    @Override
    public void run() {
        System.out.println("run方法中,Thread.currentThread().getName() : " + Thread.currentThread().getName() );
        System.out.println("run方法,this.getName() : " + this.getName());
    }
}
package com.wkcto.threadmehtod;

/**
 * Author : 蛙课网老崔
 */
public class Test02CurrentThread {
    public static void main(String[] args) throws InterruptedException {
        //创建子线程对象
        SubThread2 t2 = new SubThread2();
        t2.setName("t2");       //设置线程的名称
        t2.start();

        Thread.sleep(500);      //main线程睡眠500毫秒

        //Thread(Runnable)构造方法形参是Runnable接口,调用时传递的实参是接口的实现类对象
        Thread t3 = new Thread(t2);
        t3.start();
    }
}

etName()/getName()

thread.setName(线程名称), 设置线程名称。

thread.getName()返回线程名称。

通过设置线程名称,有助于程序调试,提高程序的可读性, 建议为每个线程都设置一个能够体现线程功能的名称。

 isAlive()

thread.isAlive()判断当前线程是否处于活动状态。

活动状态就是线程已启动并且尚未终止。

package com.wkcto.threadmehtod.p2IsAlive;

/**
 * Author : 蛙课网老崔
 */
public class SubThread3 extends Thread {
    @Override
    public void run() {
        System.out.println("run方法, isalive = " + this.isAlive());   //运行状态,true
    }
}
package com.wkcto.threadmehtod.p2IsAlive;

/**
 * 测试线程的活动状态
 * Author : 蛙课网老崔
 */
public class Test {
    public static void main(String[] args) {
        SubThread3 t3 = new SubThread3();
        System.out.println("begin==" + t3.isAlive());       //false,在启动线程之前
        t3.start();
        System.out.println("end==" + t3.isAlive());     //结果不一定,打印这一行时,如果t3线程还没结束就返回true, 如果t3线程已结束,返回false
    }
}

sleep()

Thread.sleep(millis); 让当前线程休眠指定的毫秒数。

当前线程是指Thread.currentThread()返回的线程。

package com.wkcto.threadmehtod.p3sleep;

/**
 * 子线程休眠
 * Author : 蛙课网老崔
 */
public class SubThread4 extends Thread {
    @Override
    public void run() {
        try {
            System.out.println("run, threadname=" + Thread.currentThread().getName()
                        + " ,begin= " + System.currentTimeMillis());
            Thread.sleep(2000);     //当前线程睡眠2000毫秒
            System.out.println("run, threadname=" + Thread.currentThread().getName()
                    + " ,end= " + System.currentTimeMillis());
        } catch (InterruptedException e) {
            //在子线程的run方法中, 如果有受检异常(编译时异常)需要处理,只有选择捕获处理,不能抛出处理
            e.printStackTrace();
        }

    }
}
package com.wkcto.threadmehtod.p3sleep;

/**
 * Author : 蛙课网老崔
 */
public class Test {
    public static void main(String[] args) {
        SubThread4 t4 = new SubThread4();
        System.out.println("main__begin = " + System.currentTimeMillis());
//        t4.start();       //开启新的线程
        t4.run();       //在main线程中调用实例方法run(),没有开启新的线程
        System.out.println("main__end = " + System.currentTimeMillis());
    }
}
package com.wkcto.threadmehtod.p3sleep;

/**
 * 使用线程休眠Thread.sleep完成一个简易的计时器
 * Author : 蛙课网老崔
 */
public class SimpleTimer {
    public static void main(String[] args) {
        int remaining = 60;         //从60秒开始计时
        //读取main方法的参数
        if (args.length == 1){
            remaining = Integer.parseInt(args[0]);
        }

        while(true){
            System.out.println("Remaining: " + remaining);
            remaining--;
            if (remaining < 0 ){
                break;
            }
            try {
                Thread.sleep(1000);         //线程休眠
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("Done!!");
    }
}

getId()

thread.getId()可以获得线程的唯一标识。

注意:

某个编号的线程运行结束后,该编号可能被后续创建的线程使用。

重启的JVM后,同一个线程的编号可能不一样。

package com.wkcto.threadmehtod.p4getid;

/**
 * Author : 蛙课网老崔
 */
public class SubThread5 extends Thread {
    @Override
    public void run() {
        System.out.println("thread name = " + Thread.currentThread().getName()
                + ", id == " + this.getId() );
    }
}
package com.wkcto.threadmehtod.p4getid;

/**
 * Author : 蛙课网老崔
 */
public class Test {
    public static void main(String[] args) {
        System.out.println( Thread.currentThread().getName() + " , id = " + Thread.currentThread().getId());

        //子线程的id
        for(int i = 1; i <= 100; i++){
             new SubThread5().start();
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

yield()

Thread.yield()方法的作用是放弃当前的CPU资源。

package com.wkcto.threadmehtod.p5yield;

/**
 * 线程让步
 * Author : 蛙课网老崔
 */
public class SubThread6 extends  Thread {
    @Override
    public void run() {
        long begin = System.currentTimeMillis();
        long sum = 0;
        for(int i = 1; i <= 1000000; i++){
            sum += i;
            Thread.yield();         //线程让步, 放弃CPU执行权
        }
        long end = System.currentTimeMillis();
        System.out.println("用时: " + (end - begin));
    }
}
package com.wkcto.threadmehtod.p5yield;

/**
 * Author : 蛙课网老崔
 */
public class Test {
    public static void main(String[] args) {
        //开启子线程,计算累加和
        SubThread6 t6 = new SubThread6();
        t6.start();

        //在main线程中计算累加和
        long begin = System.currentTimeMillis();
        long sum = 0;
        for(int i = 1; i <= 1000000; i++){
            sum += i;
        }
        long end = System.currentTimeMillis();
        System.out.println("main方法 , 用时: " + (end - begin));
    }
}

setPriority()

thread.setPriority( num ); 设置线程的优先级。

java线程的优先级取值范围是 1 ~ 10 , 如果超出这个范围会抛出异常IllegalArgumentException。

在操作系统中,优先级较高的线程获得CPU的资源越多。

线程优先级本质上是只是给线程调度器一个提示信息,以便于调度器决定先调度哪些线程. 注意不能保证优先级高的线程先运行。

Java优先级设置不当或者滥用可能会导致某些线程永远无法得到运行,即产生了线程饥饿。

线程的优先级并不是设置的越高越好,一般情况下使用普通的优先级即可,即在开发时不必设置线程的优先级。

线程的优先级具有继承性, 在A线程中创建了B线程,则B线程的优先级与A线程是一样的。

package com.wkcto.threadmehtod.p6priority;

/**
 * Author : 蛙课网老崔
 */
public class ThreadA extends  Thread {
    @Override
    public void run() {
        long begin = System.currentTimeMillis();
        long sum = 0 ;
        for(long i = 0 ; i<= 10000000000L; i++){
            sum += i;
        }
        long end = System.currentTimeMillis();
        System.out.println("thread a : " + (end - begin));
    }
}
package com.wkcto.threadmehtod.p6priority;

/**
 * Author : 蛙课网老崔
 */
public class Test {
    public static void main(String[] args) {
        ThreadA threadA = new ThreadA();
        threadA.setPriority(1);
        threadA.start();

        ThreadB threadB = new ThreadB();
        threadB.setPriority(10);
        threadB.start();
        
    }
}

interrupt()

中断线程。

注意调用interrupt()方法仅仅是在当前线程打一个停止标志,并不是真正的停止线程。

package com.wkcto.threadmehtod.p7interrupt;

/**
 * Author : 蛙课网老崔
 */
public class SubThread2 extends  Thread {
    @Override
    public void run() {
        super.run();
        for(int i = 1; i <= 10000; i++){
            //判断线程的中断标志,线程有 isInterrupted()方法,该方法返回线程的中断标志
            if ( this.isInterrupted() ){
                System.out.println("当前线程的中断标志为true, 我要退出了");
//                break;      //中断循环, run()方法体执行完毕, 子线程运行完毕
                return;         //直接结束当前run()方法的执行
            }

            System.out.println("sub thread --> " + i);
        }
    }
}
package com.wkcto.threadmehtod.p7interrupt;

/**
 * Author : 蛙课网老崔
 */
public class Test02 {
    public static void main(String[] args) {
        SubThread2 t1 = new SubThread2();
        t1.start();         ///开启子线程

        //当前线程是main线程
        for(int i = 1; i<=100; i++){
            System.out.println("main ==> " + i);
        }
        //中断子线程
        t1.interrupt();         ////仅仅是给子线程标记中断,
    }
}

setDaemon()

Java中的线程分为用户线程与守护线程。

守护线程是为其他线程提供服务的线程,如垃圾回收器(GC)就是一个典型的守护线程。

守护线程不能单独运行, 当JVM中没有其他用户线程,只有守护线程时,守护线程会自动销毁, JVM会退出。

package com.wkcto.threadmehtod.p8daemon;

/**
 * Author : 蛙课网老崔
 */
public class SubDaemonThread extends Thread {
    @Override
    public void run() {
        super.run();
        while(true){
            System.out.println("sub thread.....");
        }
    }
}
package com.wkcto.threadmehtod.p8daemon;

/**
 * 设置线程为守护线程
 * Author : 蛙课网老崔
 */
public class Test {
    public static void main(String[] args) {
        SubDaemonThread thread = new SubDaemonThread();
        //设置线程为守护线程
        thread.setDaemon(true);         //设置守护线程的代码应该在线程启动前
        thread.start();

        //当前线程为main线程
        for(int i = 1; i <= 10 ; i++){
            System.out.println("main== " + i);
        }
        //当main线程结束, 守护线程thread也销毁了
    }
}