Java 多线程常用方法总结

Java多线程常用方法

方法一:Thread类的静态方法sleep()
作用:让当前线程停止执行,把CPU让给其他线程执行,但不会释放对象锁和监控的状态,也就是如果有Synchronized同步块,其他线程仍然不同访问共享数据。到了指定时间后线程又会自动恢复运行状态。
线程状态变化:运行 --》 等待 。 sleep时间到后线程自动由 等待 --》 就绪
注意:线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行
/**
 * @description: 线程休眠方法
 */
public class ThreadSleep implements Runnable {
  
    @Override
    public void run() {

        // 循环打印
        for (int i = 0 ; i < 100 ; i++ ){

            // 线程休眠
            try {
                Thread.sleep(i*10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 打印内容
            System.out.println(Thread.currentThread().getId()+"======threadSleep======"+i);
        }
    }
}

public static void main(String[] args) {

        /**
         * 线程常用方法 sleep()
         * 作用是让当前线程停止执行,把cpu让给其他线程执行,但不会释放对象锁和监控的状态,到了指定时间后线程又会自动恢复运行状态。
         *
         * 注意:线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。
         * 因此,sleep()方法不能保证该线程睡眠到期后就开始执行
         */
        Runnable runnableThreadSleep = new ThreadSleep();
        Thread threadSleep = new Thread(runnableThreadSleep);
        threadSleep.start();

        Runnable runnableThreadSleep1 = new ThreadSleep();
        Thread threadSleep1 = new Thread(runnableThreadSleep1);
        threadSleep1.start();
}
方法二:Thread类的静态方法yield()
作用:暂停当前正在执行的线程对象(及放弃当前拥有的cup资源),并执行其他线程。yield()做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。
线程状态变化:运行 --》 就绪
注意:使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
/**
 * @description: 线程让行方法
 */
public class ThreadYield implements Runnable {

    @Override
    public void run() {

        // 循环打印
        for (int i = 0 ; i < 100 ; i++ ){

            // 线程让行
            Thread.yield();

            // 打印内容
            System.out.println(Thread.currentThread().getId()+"======threadYield======"+i);
        }
    }
}

public static void main(String[] args) {

        /**
         * 作用:暂停当前正在执行的线程对象(及放弃当前拥有的cup资源),并执行其他线程。yield()做的是让当前运行线程回到可运行状态,
         *      以允许具有相同优先级的其他线程获得运行机会。
         * 线程状态变化:运行 --》 就绪
         * 注意:使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。
         *      但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
         */
        Runnable runnableThreadYield = new ThreadYield();
        Thread threadYield = new Thread(runnableThreadYield);
        threadYield.start();

        Runnable runnableThreadYield1 = new ThreadYield();
        Thread threadYield1 = new Thread(runnableThreadYield1);
        threadYield1.start();
}

通过以上两个例子可以明显感觉到线程频繁切换会导致性能明显下降。

方法三:Thread类的join()方法
作用:主线程(生成子线程的线程)等待子线程(生成的线程)的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行。
线程状态变化:
主线程:运行 --》 等待/超时等待(取决于调用的是join()还是join(long millis))  调用join方法的子线程执行完后会 等待/超时等待 --》 运行/阻塞(对象被锁)
子线程:就绪 --》 运行 --》 终止
注意:主线程不会释放已经持有的对象锁。
// 第一个测试实例 main方法为主线程
public static void main(String[] args) {

        // 线程一 子线程
        Thread thread01 = new Thread(new Runnable() {

            @Override
            public void run() {
                // 循环打印
                for (int i = 0 ; i < 100 ; i++ ){

                    // 打印内容
                    System.out.println(Thread.currentThread().getId()+"======thread01======"+i);
                }
            }
        });
        thread01.start();

        // 线程二 子线程
        Thread thread02 = new Thread(new Runnable() {

            @Override
            public void run() {
                // 循环打印
                for (int i = 0 ; i < 100 ; i++ ){

                    // 打印内容
                    System.out.println(Thread.currentThread().getId()+"======thread02======"+i);
                }
            }
        });
        thread02.start();
}

// 第二个测试实例 main方法为主线程
public static void main(String[] args) {

        // 线程一 子线程
        Thread thread01 = new Thread(new Runnable() {

            @Override
            public void run() {
                // 循环打印
                for (int i = 0 ; i < 100 ; i++ ){

                    // 打印内容
                    System.out.println(Thread.currentThread().getId()+"======thread01======"+i);
                }
            }
        });
        thread01.start();

        // 执行线程插入
        try {
            thread01.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 线程二 子线程
        Thread thread02 = new Thread(new Runnable() {

            @Override
            public void run() {
                // 循环打印
                for (int i = 0 ; i < 100 ; i++ ){

                    // 打印内容
                    System.out.println(Thread.currentThread().getId()+"======thread02======"+i);
                }
            }
        });
        thread02.start();
}

// 通过上述两个测试实例,第一个实例输出内容线程一和线程二会交替输出,第二个实例输入内容会线程一全部输出完后线程二才会输出,由此可以证明线程一使用了join方法后
// 主线程会等到子线程(线程一)执行完后再继续执行后面的代码。
方法四:synchronized 关键字
作用:用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。
注意:
修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
public class SynchronizedObject {

    private static String info = "info";

    /**
     * 同步代码块
     * @param name
     */
    public void getName(String name){

        // 此时锁的是括号内的对象
        synchronized (name){
            System.out.println("getName name = " + name);
        }
    }

    /**
     * 同步方法
     * 此时锁的是当前实例的对象。
     * @param name
     */
    public synchronized void setName(String name){
        System.out.println("setName name = " + name);
    }

    /**
     * 静态同步方法
     */
    public static synchronized void staticSynchronized(){
        System.out.println("class info = " + info);
    }

}
方法五:Object类的wait()方法
作用:让当前线程进入等待状态。
线程状态:运行 --》 等待/超时等待
注意:
1、当前线程必须是此对象的监视器所有者,否则还是会发生 IllegalMonitorStateException 异常。所以wait方法和synchronized 关键字组合使用
2、如果当前线程在等待之前或在等待时被任何线程中断,则会抛出 InterruptedException 异常。
/**
  * 简单的人员对象
  */
private static Person person = new Person();

public static void main(String[] args) {

    // 线程一
    Thread thread01 = new Thread(new Runnable() {

        @Override
        public void run() {

            // 获取对象锁
            synchronized (person){

                // 设置名称
                person.setName("Engraver");

                try {

                    // 释放 person 对象锁 并进入等待其他获取该对象锁的线程唤醒
                    person.wait();

                    // 使线程休眠
                    Thread.sleep(200);

                    System.out.println("thread01 person name " + person.getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    });
    thread01.start();

    // 线程二
    Thread thread02 = new Thread(new Runnable() {

        @Override
        public void run() {

            synchronized (person){

                System.out.println("thread02 set person name before" + person.getName());
                person.setName("甜甜");
                System.out.println("thread02 set person name after" + person.getName());

                // 唤醒当前对象上的等待线程
                person.notify();
            }

        }
    });
    thread02.start();
}
方法六:Object类的notify()方法
作用:唤醒等待该锁的其中一个线程。
线程状态:运行 --》 等待/结束
注意:notify只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会
/**
  * 简单的人员对象
  */
private static Person person = new Person();

public static void main(String[] args) {

    // 线程一
    Thread thread01 = new Thread(new Runnable() {

        @Override
        public void run() {

            // 获取对象锁
            synchronized (person){

                // 设置名称
                person.setName("Engraver");

                try {

                    // 释放 person 对象锁 并进入等待其他获取该对象锁的线程唤醒
                    person.wait();

                    // 使线程休眠
                    Thread.sleep(200);

                    System.out.println("thread01 person name " + person.getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    });
    thread01.start();

    // 线程二
    Thread thread02 = new Thread(new Runnable() {

        @Override
        public void run() {

            synchronized (person){

                System.out.println("thread02 set person name before" + person.getName());
                person.setName("甜甜");
                System.out.println("thread02 set person name after" + person.getName());

                // 唤醒当前对象上的等待线程
                person.notify();
            }

        }
    });
    thread02.start();
}
方法七:Object类的notifyAll()方法
作用:唤醒等待该锁的所有线程。
线程状态:运行 --》 等待/结束
注意:notify只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会。
/**
 * 简单的人员对象
 */
private static Person person = new Person();

public static void main(String[] args) {

    // 线程一
    Thread thread01 = new Thread(new Runnable() {

        @Override
        public void run() {

            // 获取对象锁
            synchronized (person){

                // 设置名称
                person.setName("Engraver");

                try {

                    // 释放 person 对象锁 并进入等待其他获取该对象锁的线程唤醒
                    person.wait();

                    // 使线程休眠
                    Thread.sleep(200);

                    System.out.println("thread01 person name " + person.getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    });
    thread01.start();

    // 线程二
    Thread thread02 = new Thread(new Runnable() {

        @Override
        public void run() {

            synchronized (person){

                System.out.println("thread02 set person name before" + person.getName());
                person.setName("甜甜");
                System.out.println("thread02 set person name after" + person.getName());

                // 释放 person 对象锁 并进入等待其他获取该对象锁的线程唤醒
                try {
                    person.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    });
    thread02.start();

    // 线程三
    Thread thread03 = new Thread(new Runnable() {

        @Override
        public void run() {

            synchronized (person){

                System.out.println("thread03 set person name before" + person.getName());
                person.setName("小橘子");
                System.out.println("thread03 set person name after" + person.getName());

                // 唤醒当前对象上所有等待的线程
                person.notifyAll();
            }

        }
    });
    thread03.start();
}

notify() 和 notifyAll() 方法的区别

锁池
假设线程A已经拥有了某个对象(不是类)的锁,而其它线程B、C想要调用这个对象的某个synchronized方法(或者代码块), 由于B、C线程在进入对象的synchronized方法(或者块)之前必须先获得该对象锁的拥有权,而恰巧该对象的锁目前正被线程A所占用,此时B、C线程就会被阻塞,进入一个地方去等待锁的释放,这个地方便是该对象的锁池

等待池
假设线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁,同时线程A就进入到了该对象的等待池中,进入到等待池中的线程不会去竞争该对象的锁。

notifyAll会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会
notify只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会
方法八:Thread类静态方法interrupt()
作用:中断线程。将会设置该线程的中断状态位,即设置为true,中断的结果线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序本身。
线程状态:无状态改变,改变的是线程的一个标记。
注意:
1、只是改变中断状态,不会中断一个正在运行的线程。
2、如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。仅此而已。
3、如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true,仅此而已。被设置中断标志的线程将继续正常运行,不受影响。
public static void main(String[] args) {

    String name = "Engraver";

    // 线程一
    Thread thread01 = new Thread(new Runnable() {

        @Override
        public void run() {

            // 获取对象锁
            synchronized (name){

                // 进入等待
                try{
                    name.wait();
                }catch (InterruptedException e){
                    System.out.println(" name.wait() InterruptedException");
                }

                // 判断是否中断 可以看到当条件为true时线程并未终止而是继续执行输出语句
                while (Thread.interrupted()){
                    System.out.println("thread01 interrupted true");
                }
            }

        }
    });
    thread01.start();

    // 线程二
    Thread thread02 = new Thread(new Runnable() {

        @Override
        public void run() {

            // 获取对象锁
            synchronized (name){

                // 唤醒等待name对象的线程
                name.notify();

                /**
                     * 执行线程1中断
                     * name.notify();
                     * thread01.interrupt();
                     * 先释放name锁后中断会正常执行thread01线程后面的代码
                     * thread01.interrupt();
                     * name.notify();
                     * 先中断后执行释放name锁thread01会抛出异常后面语句不会执行
                     */
                thread01.interrupt();
                
                System.out.println("测试线程是否立即中断");
            }

        }
    });
    thread02.start();

}
posted @ 2021-04-27 15:19  BK_ENGRAVER  阅读(282)  评论(0)    收藏  举报