关于线程 thread (2)线程的使用简介

线程的创建代码相关

一般有两种

  1. 直接写一个继承自Thread的子类并重写里面的run方法。
  2. 写一个Runnable, 用的时候作为Thread的初始化参数。 然后创建的时候直接new就行了。
/**
	 * 线程的创建示例
	 */
	public void createThread() {
		//用继承Thread类的那个类
		Thread2 thread1 = new Thread2();
		Thread2 thread2 = new Thread2();
		//用Thread 添加Runnable实现类的方式
		Runnable1 runnable1 = new Runnable1();
		Thread thread3 = new Thread(runnable1);
	
		thread1.start();
		thread2.start();
		thread3.start();
	}
class Thread2 extends Thread{
	public void run() {
		for (int i = 0; i < 30; i++) {
			System.out.println(Thread.currentThread() + "" + i);
		}
	
	}

}

class Runnable1 implements Runnable{
	public void run() {
		for (int i = 0; i < 30; i++) {
			System.out.println(Thread.currentThread() + "" + i);
		}
	}
}

在这里插入图片描述
一些常见的问题

  1. 线程的名字,一个运行中的线程是有名字的,名字有两个来源,一个是虚拟机给自己的名字,另外一个是编码人员自己定义的名字。在没有指定名字的情况下,虚拟机总会给线程指名字,并且主线程的名字总是main。
  2. 线程都可以自己设置名字,也可以获取线程的名字。设置名字这方面都可以设置,包括主线程。
  3. 获取当前线程对象的方法是 Thread.currentThread();这段逻辑是在哪个线程里执行的,他就给哪个线程。
  4. 在上面的那堆代码中,只能保证每个线程都将启动,每个线程都运行直至完成。一系列线程以某种顺序启动,并不意味这将按照该顺序走他们各自的逻辑。对于任何一组启动的线程来说,调度程序不能保证他们的执行顺序,持续时间也无法保证。所以上面的log图会出现那个样子,一会儿0,一会儿1一会儿2.这个不是我们可以控制的。
  5. 当目标线程的run()方法执行完后,该线程也就算完成了,进入死亡状态。
  6. 一旦线程启动,就永远不可能再重启同一个线程。只有一个新鲜的线程才具有被启动的条件,并且只有一次。
  7. 线程的调度是JVM的一部分,在一个cpu的机器上,实际上同时只能运行一个线程,一次只有一个线程栈执行。JVM线程调度程序决定实际运行哪个处于可运行状态的线程。众多可运行线程中的某一个会被选中作为当前线程,可运行线程被选择运行的顺序是没有保障的,怎么个轮法儿要看具体的调度机制。
  8. 尽管通常采用队列形式,但这是没有保障的。队列形式是指当一个线程完成一轮时,它移动到可运行队列的尾部等待,知道它最终排队到该队列的前端位置,他才被再次选中。事实上,我们把它称为可运行池而不是一个可运行队列,目的是帮助认识线程并不是以某种有保障的队列顺序来获得运行权这个事实。

让线程离开运行状态的代码相关

对于人为的阻止线程,在不考虑IO阻塞的情况下,大致有下面三个方面:

  • 睡眠
  • 等待
  • 因为需要一个对象的锁定而被阻塞。个人感觉大多是人为的操作,因为达到某种目的故意致使其阻塞,或者是操作不当没有考虑到,不小心致使发生了阻塞。

所以想让它阻塞,从上述3个方面入手

睡眠 sleep()

静态方法 Thread.sleep(), 它有两个重载的方法。作用是强制使当前正在执行的线程休眠(暂停执行),以“减慢线程”。当线程睡眠时,在苏醒之前他都不会转向就绪状态,而到睡眠时间到期的时候,才会转换为就绪状态。注意这是就绪状态(可运行状态),不是运行状态哈,它要不要执行还得看调度器进一步的分配。所以,睡眠的时间之后执行某些逻辑,这里的时间值在事实上其实是有可能小于真实执行的时间间隔的。

睡眠的实现方法:

try{
Thread.sleep(123);
}catch(InterrruptedException e) {
e.printStackTrace();
}

为了让其他的线程有机会执行,可以将Thread.sleep()的调用放到run()方法里,这样才能保证该线程执行过程中会睡眠。
注意:

  1. 线程睡眠是帮助所有线程获得运行机会的最好方法。
  2. 线程睡眠到自然苏醒,并返回到可运行状态,不是运行状态。sleep()中的时间指的是线程不会运行的最短时间。因此,sleep方法不能保证该线程睡眠到期后就一厘二不差的执行。
  3. sleep是一个静态方法,只能控制当前正在运行的线程。所以把sleep写到run方法里面也是情有可原的。执行到run方法里面的逻辑就说明当前运行的线程一定是run方法所在的子线程里。
  4. 通过yield()让线程直接从运行状态回到可运行状态(就绪状态)
    Thread.yield()是负责线程让步的。在这里我们先熟悉一下什么是线程让步!让步,很显然就是先让一下的意思。它主要是使分配器重新分配使用权,但是再次分配的使用权也有可能降到自己的头上。

要理解yield(),必须要了解线程优先级的概念。前面已经有了一些介绍。 Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程。

yield() 应该做的是让当前线程从运行状态变为就绪状态,以允许具有相同优先级的其他线程获得机会,因此使用yield()的目的就是让具有相同优先级的线程之间能够适当的轮转执行。但是实际中是无法保证yield()达到目的的,因为让步的线程还有可能会被调度机制再次选中。

结论:yield()从没有导致线程转到等待/睡眠/阻塞状况,在大多数情况下,yield()将导致线程从运行状态变为可运行状态,但是有可能没有效果。

join() 方法

Thread的静态方法Join()让一个线程B加入到线程A,,或者也可以说A中强行加入B, 等到B执行完之后(注意,即使B里面就算是sleep了,那也得到B执行完!),A就继续走。

thread t = new MyiTread();
t.start();
t.join();

过程示例:

public void  testJoin() {
		Thread2 thread2 = new Thread2();
		thread2.start();
	}
class Thread2 extends Thread{
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread() + "" + i);
		}
		Thread3 thread3 = new Thread3();
		thread3.start();
		try {
			thread3.join();
		} catch (Exception e) {
			// TODO: handle exception
		}
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread() + " go on.." + i);
		}
	}

}
class Thread3 extends Thread{
	public void run() {
		for (int i = 0; i < 100; i ++) {
			System.out.println(Thread.currentThread() + " 张三 " + i);
		}
	}
}

运行结果:

另外 jion()还有一个带超时限制的重载。jion(int t)。什么意思呢? 就是线程B jion进来了,就立马执行线程B的逻辑,那么线程A的逻辑也就此打断,等到t毫秒以后,反正时间到了,管他B执行没执行完呢,继续从A上次暂停的逻辑处走。

/**
	 * 不好意思,我先占你个300毫秒的时间,做一些事情哈。你要搞什么我不管,但你从现在开始务必给我停上300毫秒!时间一到咱们公平竞争!
	 * @throws Exception
	 */
	public void testJionTime() throws Exception{
		System.out.println("第一行代码时间 == " + System.currentTimeMillis());
		Thread3 thread3 = new Thread3();
		thread3.start();
		//线程里面一执行就让他睡了299毫秒
		//这样就可以看出来,过了这个300毫秒之后,,两个线程的执行,是不是就是按着正常的调度走了。
		thread3.join(300);
		//没猜错的话,这一行估计就是等了300毫秒之后,开始走的
		System.out.println("jion的下一行代码时间 == " +System.currentTimeMillis());
		
		for (int i = 0; i < 30; i++) {
			System.out.println(Thread.currentThread() + " time == " +  System.currentTimeMillis() + " 主线程循环 " + i);
		}
	}



class Thread3 extends Thread{
	public void run() {
		try{
			Thread.sleep(299);	//此处睡了299毫秒,然后主线程设置睡300毫秒。  那么底下的怎么着也得执行个1毫秒吧。
			for (int i = 0; i < 200; i ++) {
				System.out.println(Thread.currentThread() + " time == " + System.currentTimeMillis() + " 张三 " + i);
			}
		}catch(Exception e) {
			
		}
		
	}
}


我非常好奇什么情况下会用join(),,哎呀反正到时候就会用啦,

小结

到目前为止,介绍了线程离开运行状态的3种方法:

  1. 调用Thread.sleep() 使当前线程睡眠至少指定的毫秒数,尽管他可能在指定的时间之前被中断
  2. 调用Thread,yield() 不能保障太多事情,尽管通常他会让当前线程回到可运行状态,使得有相同优先级的线程有机会执行。
  3. 调用 join(): 保证当前线程停止执行,直到该线程所加入的线程回到就绪状态,或者线程执行完成为止。然而如果它加入的线程没有存活,则当前线程不需要停止。

除了以上3种方式,还有下面几种特殊状况可能使线程离开状态:
4. 线程的run方法完成
5. 在对象上调用wait()方法,(不是在线程上调用)这个稍后有。
6. 线程不能在对象上获得锁定,它正视图运行该对象的方法代码。
7. 线程调度程序可以决定将当前运行状态移动到可运行状态,以便于让另外一个线程获得运行的机会,而不需要任何理由。

join()方法源码

/**
     * Waits at most {@code millis} milliseconds for this thread to
     * die. A timeout of {@code 0} means to wait forever.
     *
     * <p> This implementation uses a loop of {@code this.wait} calls
     * conditioned on {@code this.isAlive}. As a thread terminates the
     * {@code this.notifyAll} method is invoked. It is recommended that
     * applications not use {@code wait}, {@code notify}, or
     * {@code notifyAll} on {@code Thread} instances.
     *
     * @param  millis
     *         the time to wait in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public final void join(long millis) throws InterruptedException {
        synchronized(lock) {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                lock.wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                lock.wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

娅娅梨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值