原文链接:https://blog.csdn.net/u011024652/article/details/51583134
一、线程与进程
1、进程是程序(任务)执行过程,持有资源(共享内存,共享文件)和线程,进程是动态性的,如果程序没有执行就不算一个进程。
2、线程是系统中最小的执行单元,同一进程中有多个线程,线程共享进程的资源
Java中创建现成的方式就不再赘述了,有两种:(1)继承Thread类,重写run()方法,(2)实现Runnable接口,重写run()方法。
二、yield()方法
在jdk文档中,yield()方法是这样描述的:暂停当前正在执行的线程对象,并执行其他线程。在多线程的情况下,由CPU决定执行哪一个线程,而yield()方法就是暂停当前的线程,让给其他线程(包括它自己)执行,具体由谁执行由CPU决定。
yield()方法实例
YieldRunnable.java
public class YieldRunnable implements Runnable{
public volatile boolean isRunning = true;
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name + "开始执行!");
while(isRunning) {
for(int i = 1; i < 6; i++) {
System.out.println(name + "执行了[" + i + "]次");
//注意,yield是静态方法
Thread.yield();
}
}
System.out.println(name + "执行结束!");
}
}
执行的main函数
public static void main(String[] args) {
YieldRunnable runnable1 = new YieldRunnable();
YieldRunnable runnable2 = new YieldRunnable();
Thread thread1 = new Thread(runnable1, "线程1");
Thread thread2 = new Thread(runnable2, "线程2");
System.out.println("两个线程准备开始执行");
thread1.start();
thread2.start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
runnable1.isRunning = false;
runnable2.isRunning = false;
}
选取部分执行结果分析:
1、第一种情况:
……
线程1执行了[1]次
线程2执行了[1]次
线程2执行了[2]次
线程2执行了[3]次
线程2执行了[4]次
线程1执行了[2]次
……
从这个执行结果可以看出,线程1在执行了一次之后,让出执行权,CPU将执行权给了线程2,线程2执行了一次之后让出执行权,但是CPU仍将执行权给2,就这样线程2反复执行了几次,CPU一直都是将执行权给线程2,知道线程2执行完第4次之后,CPU才将执行权给线程1。
2、第二种情况:
……
线程1执行了[1]次
线程2执行了[3]次
线程1执行了[2]次
线程2执行了[4]次
线程1执行了[3]次
线程2执行了[5]次
……
从这个执行结果可以看出,线程1执行一次后,让出执行权,CPU将执行权给了线程2,线程2执行一次后,CPU将执行权又给了1,就这样交替执行了几次线程1和线程2。
从上面两种执行结果来看,一个线程执行yield()方法暂停后,CPU决定接下来有哪一个线程执行,可以是其他线程,也可以是原来的那个线程。
三、join()方法
join()方法是指等待调用join()方法的线程执行结束,程序才会继续执行下去,这个方法适用于:一个执行程序必须等待另一个线程的执行结果才能够继续运行的情况。
join()方法实例
定义一个线程:
JoinRunnable.java
public class JoinRunnable implements Runnable {
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name + "开始执行!");
for(int i = 1; i < 6; i++) {
System.out.println(name + "执行了[" + i + "]次");
}
}
}
先来看一下没有使用join()方法的情况:
public static void main(String[] args) {
JoinRunnable runnable1 = new JoinRunnable();
Thread thread1 = new Thread(runnable1, "线程1");
System.out.println("主线程开始执行!");
thread1.start();
System.out.println("主线程执行结束!");
}
执行结果:
主线程开始执行!
主线程执行结束!
线程1开始执行!
线程1执行了[1]次
线程1执行了[2]次
线程1执行了[3]次
线程1执行了[4]次
线程1执行了[5]次
从执行结果可以看出,如果没有使用join方法,那么main方法所在的线程(暂定叫主线程)在启动了JoinRunnable线程(暂定叫子线程)之后,没有等待子线程执行结束,就先执行结束了。
如果使用了join方法:
public static void main(String[] args) {
JoinRunnable runnable1 = new JoinRunnable();
Thread thread1 = new Thread(runnable1, "线程1");
System.out.println("主线程开始执行!");
thread1.start();
try {
thread1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程执行结束!");
}
执行结果:
主线程开始执行!
线程1开始执行!
线程1执行了[1]次
线程1执行了[2]次
线程1执行了[3]次
线程1执行了[4]次
线程1执行了[5]次
主线程执行结束!
从执行结果可以看出,加入join()方法,主线程启动了子线程之后,在等待子线程执行完毕才继续执行下面的操作。
注意:join()方法只有在start()方法之后才可以生效。可以参考下面的代码,将join()方法写在start()方法之前。
public static void main(String[] args) {
JoinRunnable runnable1 = new JoinRunnable();
Thread thread1 = new Thread(runnable1, "线程1");
System.out.println("主线程开始执行!");
try {
thread1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
thread1.start();
System.out.println("主线程执行结束!");
}
执行结果:
主线程开始执行!
主线程执行结束!
线程1开始执行!
线程1执行了[1]次
线程1执行了[2]次
线程1执行了[3]次
线程1执行了[4]次
线程1执行了[5]次
虽然加入了join()方法,但是是在线程启动的start()方法之前调用的,所以join()方法不生效。具体原因可以参考一下源码
public final void join()
throws InterruptedException
{
join(0L);
}
public final synchronized void join(long l)
throws InterruptedException
{
long l1 = System.currentTimeMillis();
long l2 = 0L;
if(l < 0L)
throw new IllegalArgumentException("timeout value is negative");
if(l == 0L)
for(; isAlive(); wait(0L));//isAlive()是判断当前线程的状态,wait(0)是指等待直到线程结束
else
do
{
if(!isAlive())
break;
long l3 = l - l2;
if(l3 <= 0L)
break;
wait(l3);
l2 = System.currentTimeMillis() - l1;
} while(true);
}
从源码可以看出,join()方法调用了join(long l)方法,参数l的值为0,之后在判断中如果l的值为0,则执行一个循环,循环执行的条件为当前线程是否isAlive(),就是指线程是否已经运行,如果线程未启动(即未调用start方法),则循环条件不成立,直接退出,也就是说join()方法不生效。
在看这里的源码的时候发现了一个小问题,对于isAlive()和wait()方法的作用对象是有点让人疑惑的,isAlive()判断当前线程的状态,还好理解,在上面的例子中就是指thread1。但是wait()方法的作用对象到底是谁,产生了疑问,按照jdk的说法等待的应该是当前线程,那就是thread1,但是实际运行中,等待的是主线程,有点让人摸不着头脑。之后在网上找到这篇文章,里面说当我们在synchronized块 中调用锁对象的wait()方法,那么调用线程就会阻塞在wait()语句那里,直到其他线程调用线程的notify()方法。这里的调用线程也就是指主线程,因为wai()方法是在Thread类中的synchronized方法join(long l)中调用的,所以主线程阻塞,直到线程执行完毕调用了notify()方法才继续执行,这个说法先记录一下,以备以后考证。
最后一点小插曲:
在编码的时候尝试过在start()方法和join()方法之间加入一些内容,也就是说调用start()方法启动线程之后不立即调用join()方法,想看一下是否出现:先启动子线程,执行相关操作,然后再执行主线程中的内容,再等待子线程执行结束的情况。示例代码:
public static void main(String[] args) {
JoinRunnable runnable1 = new JoinRunnable();
Thread thread1 = new Thread(runnable1, "线程1");
System.out.println("主线程开始执行!");
thread1.start();
//启动子线程之后主线程休眠1秒
<span style="white-space:pre"> </span>try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
try {
thread1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程执行结束!");
}
执行结果:
主线程开始执行!
线程1开始执行!
线程1执行了[1]次
线程1执行了[2]次
线程1执行了[3]次
线程1执行了[4]次
线程1执行了[5]次
主线程执行结束!
实际执行的时候主线程休眠1秒是在子线程执行结束之后才执行的,由此可以推断,只要在start()方法之后调用了join()方法,不管这两个方法之间加入多少内容,主线程都会优先等待子线程执行结束才会继续往下执行。
---------------------
作者:蛋蛋要学编程
来源:CSDN
原文:https://blog.csdn.net/u011024652/article/details/51583134
版权声明:本文为博主原创文章,转载请附上博文链接!