线程yield,start,run,join,sleep 傻傻分不清楚

线程:cpu调度的最小单位
进程:操作系统分配资源的最小单位(线程共享进程内部的资源)

java 中提供了Thread 类,而这个类有几个方法

yield,start,run(来自接口runnable),sleep,

yield: 欲罢能否?

告诉调度器自己当前可以让出资源(但是不一定有效)

 A hint to the scheduler that the current thread is willing to yield
     * its current use of a processor. The scheduler is free to ignore this
     * hint.

测试代码:

(测试效果基本看不出)

    Thread[] threads = new Thread[10];
        threads[0] = new Thread(() -> {
            // ReadThreadLocal readThreadLocal2 = new ReadThreadLocal();
            //readThreadLocal2.begin();
            try {
                Thread.currentThread().sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
        });
        threads[0].setName("-1");
        threads[0].start();
        for (int i = 1; i < 10; i++) {
            final Thread pre = threads[i - 1];
            final int index = i;
            threads[i] = new Thread(() -> {
                try {
                    // 这里尝试1,2,号线程让出cpu
                    if (index < 3) {
                        Thread.yield();
                    }
                    // 以下逻辑 可以保证  6,7,8,9 线程是有序执行
                    else if (index > 6) {
                        pre.join();
                    }
                    //   ReadThreadLocal readThreadLocal2 = new ReadThreadLocal();
                    //   readThreadLocal2.begin();
                    Thread.sleep(500);
                    System.out.println(Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            });
            threads[i].setName(i + "");
            threads[i].start();

        }

效果:(这三次输出只是看到了join 接力棒的效果,始终保证6,7,8,9之间是严格有序的)
第一次:

-1 1 3 2 5 4 6 7 8 9

第二次

-1 1 6 5 2 4 3 7 8 9

第三次

-1 3 1 4 2 6 5 7 8 9

start:线程启动入口

启动线程(线程进入就绪态:(也就是等待被cpu调度,并不是说先调用start,就先执行run方法)
run: 线程真正的执行体,线程需要处理的业务逻辑全在这里

join:接力棒

join(othrerThread)

Waits for this thread to die// 等待到参数中的thread 死亡,自己才开始

(可以类比为线程进行接力赛,下一棒需要等上一棒到了才能开始跑),也就是可以达到有序执行的目的

代码测试

看例子程序:

Thread[] threads = new Thread[10];
        threads[0] = new Thread(() -> {
            
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() );
        });
        threads[0].setName("-1");
        threads[0].start();
        for (int i = 1; i < 10; i++) {
            final Thread pre = threads[i - 1];
            threads[i] = new Thread(() -> {
                try {
                    pre.join();
                   
                   
                    Thread.sleep(500);
                    System.out.println(Thread.currentThread().getName() );
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            });
            threads[i].setName(i + "");
            threads[i].start();

        }

在这里插入图片描述

sleep: 抱锁入睡

注意不会丢失monitors,监视器,也就是不会释放锁

Causes the currently executing thread to sleep (temporarily cease
* execution) for the specified number of milliseconds, subject to
* the precision and accuracy of system timers and schedulers. The thread
* does not lose ownership of any monitors.

代码测试:

线程1,线程2 都去获取同一个对象的monitor,各自都在获取到之后输出 “sleep start”+id;sleep(500);输出"sleep end "+id
如果不释放锁的话,要么是线程1 连续输出两条,然后线程2连续输出两条
否则是反过来
代码使用10个线程


public class TestSleep {


    public static void main(String[] args) {
        final Object lock=new Object();
        for (int i=0;i<10;i++) {
            Thread t=new Thread(()->{
                synchronized (lock){

                    try {
                        System.out.println("sleep start"+Thread.currentThread().getName());
                        Thread.sleep(500);
                        System.out.println("sleep end"+Thread.currentThread().getName());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            t.setName(i+"");
            t.start();
        }
    }

}

效果:

在这里插入图片描述

Object 的方法:wait VS notify

wait:等待,会释放锁,将当前位置保存,暂停执行
norify:唤醒,会释放锁,但是会将synchronized修饰代码块执行完之后才会释放

伪代码描述

以下列伪代码为例:

 第一块代码块
 synchonized(helloObject){// 这里相当于是尝试获取对象helloObject 的锁
   输出:我是hello1
   helloObject.wait();
   输出:我是hello2
}

第二块代码块
synchronized (helloObject){
输出:我是word1:
helloObject.notifyAll();
输出:我是word2

}
  1. 假设上述代码:线程1进入到第一块代码块,此时线程2阻塞到第二行代码块的synchronized 处,线程2没有获取到锁
  2. 线程1调用helloObject.wait();挂起线程,进入waiting 状态;// 已经输出 hello1
  3. 线程2获取到helloObject 的锁, //输出我是word1
  4. 线程2调用helloObject.notifyAll() //唤醒所有处于该对象的waiting 状态的线程
  5. 线程2退出synchronized 同步块;//已输出 我是word2
  6. 线程2释放helloObject 的monitor(看成锁)
  7. 线程1继续// 输出我是hello2
    以下附上java并发编程艺术中的一个例子:等待-唤醒机制
/**
 * @author wangwei
 * @date 2019/3/10 9:15
 * @classDescription 场景:一个线程修改了一个对象的值,而另一个线程感觉到了变化,然后进行相应的操作
 * 整个过程开始于一个线程,作用于另一个线程`,前者是生产者,后者是消费者
 * <p>
 * 这种生产者消费者模式隔离了"做什么" 和"怎么做"
 * java 中的实现方式
 * while(value!=desire){
 * Thread.sleep(1000);
 * }
 * doSomething();
 * <p>
 * 上述代码:在条件不满足时,sleep一段时间,避免过快的"无效尝试"
 * <p>
 * 存在的问题:
 * 1.难以确保及时性:睡眠时基本不占用处理器资源,但是如果睡眠过久,难以及时发现条件已经变化
 * 2.难以降低开销:如果降低睡眠时间,消费者能及时发现条件变化,但是,却可能需要浪费更多的资源
 * <p>
 * 两点矛盾,但是java内置了等待/通知机制,可以解决这个问题
 */
public class WaitNotify {
    static boolean flag = true;
    static Object lock = new Object();


    public static void main(String[] args) throws InterruptedException {
        Thread waitThread = new Thread(new Wait(), "WaitThread");
        waitThread.start();
        TimeUnit.SECONDS.sleep(1);
        Thread notifyThread = new Thread(new Notify(), "NotifyThread");
        notifyThread.start();
    }

    static class Wait implements Runnable {

        @Override
        public void run() {
            synchronized (lock) {
                // 当条件不满足时,继续wait,但是会释放锁lock
                while (flag) {
                    try {
                        System.out.println(Thread.currentThread() + " flag is true,wait @ "
                                + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                //条件满足时,完成工作
                System.out.println(Thread.currentThread() + " flag is false,running@ "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));

            }

        }
    }


    static class Notify implements Runnable {

        @Override
        public void run() {
            //加锁,拥有lock 的monitor
            synchronized (lock) {
                // 获取lock锁,然后进行通知(不会释放锁)
                // 直到当前线程释放lock锁之后,WaitThread 才能在wait方法上返回
                System.out.println(Thread.currentThread() + " hold lock ,notify @ "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                lock.notifyAll();
                flag = false;
                SleepUtil.second(5);

            }
            // 再次加锁
            synchronized (lock) {
                System.out.println(Thread.currentThread() + " hold lock again ,sleep @ "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                SleepUtil.second(5);
            }
        }
    }
}


输出:
在这里插入图片描述

等待唤醒机制应用–线程池

奉上并发编程艺术一书中简易web服务器
可前往github 获取
https://github.com/TopForethought/java/tree/master/src/top/forethought/concurrency/threads/httpserver
这里就不再粘贴代码了

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值