赞
踩
使用单核CPU的时候,同一时刻只能有一条指令执行,但多个指令被快速的轮换执行,使得在宏观上具有多个指令同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干端,使多个指令快速交替的执行。
如上图所示,假设只有一个CPU资源,线程之间要竞争得到执行机会。图中的第一个阶段,在A执行的过程中,B、C不会执行,因为这段时间内这个CPU资源被A竞争到了,同理,第二阶段只有B在执行,第三阶段只有C在执行。其实,并发过程中,A、B、C并不是同时进行的(微观角度),但又是同时进行的(宏观角度)。
在同一个时间点上,一个CPU只能支持一个线程在执行。因为CPU运行的速度很快,CPU使用抢占式调度模式在多个线程间进行着高速的切换,因此我们看起来的感觉就像是多线程一样,也就是看上去就是在同一时刻运行。
并行(parallellism)
使用多核CPU的时候,同一时刻,有多条指令在多个CPU上同时执行。
如图所示,在同一时刻,ABC都是同时执行(微观、宏观)。
并发编程与并行编程
① 在CPU比较繁忙(假设为单核CPU),如果开启了很多个线程,则只能为一个线程分配仅有的CPU资源,这些线程就会为自己尽量多抢时间片,这就是通过多线程实现并发,线程之间会竞争CPU资源争取执行机会。
② 在CPU资源比较充足的时候,一个进程内的多个线程,可以被分配到不同的CPU资源,这就是通过多线程实现并行。
③ 至于多线程实现的是并发还是并行?上面所说,所写多线程可能被分配到一个CPU内核中执行,也可能被分配到不同CPU执行,分配过程是操作系统所为,不可人为控制。所以,如果有人问所写的多线程程序是并发还是并行的?答案其实是都有可能。
总结: 单核CPU上的多线程,只是由操作系统来完成多任务间对CPU的运行切换,并非真正意义上的并发。随着多核CPU的出现,也就意味着不同的线程能被不同的CPU核得到真正意义的并行执行,故而多线程技术得到广泛应用。
不管并发还是并行,都提高了程序对CPU资源的利用率,最大限度地利用CPU资源,而我们使用多线程的目的就是为了提高CPU资源的利用率。
如果多个线程被分配到一个CPU内核中执行,则同一时刻只能允许有一个线程能获得CPU的执行权,那么进程中的多个线程就会抢夺CPU的执行权,这就是涉及到线程调度策略。
两种调度策略:
①编写一个类继承Thread,重写run方法。
②创建线程对象:Thread t = new MyThread();
③启动线程:t.start();
示例代码:
package threadtest; /** * 在java语言中,实现线程有两种方式,第一种方式: * 第一步:编写一个类继承java.lang.Thread * 第二步:重写run()方法 * 第三步:new线程对象 * 第四步:调用线程对象的start()方法来启动线程 */ public class ThreadTest01 { public static void main(String[] args) { //创建线程对象 Thread t = new MyThread(); //直接调用run()方法,不会启动新的线程 //java中有一个语法规则:对于方法体重的代码,必须遵循自上而下的顺序逐次执行 //run()方法不结束,main方法是无法继续执行的 //t.run(); //调用start()方法,启动线程 //java中有一个语法规则:对于方法体中的代码,必须遵循自上而下的顺序逐次执行 //start()方法不结束,main方法是无法继续执行的 //start()方法瞬间就会结束,因为这个方法的作用是启动一个新的线程,只要新线程启动成功了,start()就结束了 t.start(); //下面的代码在main方法中,因此其属于在主线程中 for (int i = 0; i < 100; i++) { System.out.println("main-->" + i); } } } /** * 自定义一个线程类 * java.lang.Thread本身就是一个线程。 * MyThread继承Thread,因此MyThread本身也是一个线程。 */ class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("Thread-->" + i); } } }
运行结果:
Thread-->0 Thread-->1 Thread-->2 Thread-->3 Thread-->4 Thread-->5 Thread-->6 Thread-->7 Thread-->8 Thread-->9 Thread-->10 Thread-->11 Thread-->12 main-->0 main-->1 main-->2 main-->3 main-->4 main-->5 main-->6 main-->7 main-->8 main-->9 main-->10 main-->11 Thread-->13 main-->12 main-->13 main-->14 main-->15 main-->16 main-->17 main-->18 main-->19 main-->20 main-->21 main-->22 main-->23 main-->24 main-->25 Thread-->14 Thread-->15 Thread-->16 Thread-->17 Thread-->18 Thread-->19 Thread-->20 main-->26 Thread-->21 Thread-->22 Thread-->23 Thread-->24 Thread-->25 Thread-->26 Thread-->27 Thread-->28 Thread-->29 Thread-->30 Thread-->31 Thread-->32 Thread-->33 Thread-->34 Thread-->35 Thread-->36 Thread-->37 Thread-->38 Thread-->39 Thread-->40 Thread-->41 Thread-->42 Thread-->43 Thread-->44 Thread-->45 Thread-->46 Thread-->47 Thread-->48 Thread-->49 Thread-->50 Thread-->51 Thread-->52 Thread-->53 Thread-->54 Thread-->55 Thread-->56 Thread-->57 Thread-->58 Thread-->59 main-->27 Thread-->60 Thread-->61 Thread-->62 Thread-->63 Thread-->64 Thread-->65 Thread-->66 Thread-->67 main-->28 main-->29 main-->30 main-->31 main-->32 main-->33 main-->34 main-->35 main-->36 main-->37 main-->38 main-->39 main-->40 main-->41 main-->42 main-->43 Thread-->68 Thread-->69 Thread-->70 Thread-->71 Thread-->72 Thread-->73 Thread-->74 main-->44 Thread-->75 Thread-->76 Thread-->77 Thread-->78 Thread-->79 Thread-->80 main-->45 main-->46 Thread-->81 Thread-->82 Thread-->83 Thread-->84 Thread-->85 Thread-->86 Thread-->87 Thread-->88 Thread-->89 Thread-->90 main-->47 main-->48 Thread-->91 Thread-->92 Thread-->93 Thread-->94 main-->49 Thread-->95 Thread-->96 Thread-->97 Thread-->98 Thread-->99 main-->50 main-->51 main-->52 main-->53 main-->54 main-->55 main-->56 main-->57 main-->58 main-->59 main-->60 main-->61 main-->62 main-->63 main-->64 main-->65 main-->66 main-->67 main-->68 main-->69 main-->70 main-->71 main-->72 main-->73 main-->74 main-->75 main-->76 main-->77 main-->78 main-->79 main-->80 main-->81 main-->82 main-->83 main-->84 main-->85 main-->86 main-->87 main-->88 main-->89 main-->90 main-->91 main-->92 main-->93 main-->94 main-->95 main-->96 main-->97 main-->98 main-->99 Process finished with exit code 0
直接调用run()方法的内存结构图:
调用start()方法的内存结构图:
①编写一个类实现Runnable接口,实现run方法。
②创建线程对象:Thread t = new Thread(new MyRunnable());
③启动线程:t.start();
示例代码:
package threadtest; /** * 在java语言中,实现线程有两种方式,第二种方式: * 第一步:编写一个类实现java.lang.Runnable接口(可运行接口) * 第二步:实现接口中的run方法 * 第三步:new线程对象 * 第四步:调用线程的start方法启动线程 */ public class ThreadTest02 { public static void main(String[] args) { //创建Runnable对象 Runnable r = new MyRunnable(); //创建线程对象 Thread t = new Thread(r); //启动线程 t.start(); for (int i = 0; i < 100; i++) { System.out.println("main-->" + i); } } } /** * 创建一个类实现Runnable接口 */ class MyRunnable implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("Thread-->" + i); } } }
运行结果:
main-->0 Thread-->0 Thread-->1 Thread-->2 Thread-->3 Thread-->4 Thread-->5 Thread-->6 Thread-->7 Thread-->8 Thread-->9 Thread-->10 Thread-->11 Thread-->12 Thread-->13 main-->1 main-->2 Thread-->14 Thread-->15 Thread-->16 Thread-->17 Thread-->18 Thread-->19 Thread-->20 Thread-->21 Thread-->22 Thread-->23 main-->3 main-->4 main-->5 main-->6 main-->7 main-->8 main-->9 main-->10 main-->11 main-->12 main-->13 main-->14 main-->15 main-->16 main-->17 main-->18 Thread-->24 Thread-->25 Thread-->26 Thread-->27 Thread-->28 Thread-->29 Thread-->30 Thread-->31 Thread-->32 Thread-->33 Thread-->34 Thread-->35 Thread-->36 Thread-->37 Thread-->38 Thread-->39 Thread-->40 Thread-->41 Thread-->42 Thread-->43 Thread-->44 Thread-->45 Thread-->46 Thread-->47 Thread-->48 Thread-->49 Thread-->50 Thread-->51 Thread-->52 Thread-->53 Thread-->54 Thread-->55 Thread-->56 Thread-->57 Thread-->58 Thread-->59 Thread-->60 Thread-->61 Thread-->62 Thread-->63 Thread-->64 Thread-->65 main-->19 main-->20 Thread-->66 Thread-->67 Thread-->68 Thread-->69 Thread-->70 Thread-->71 Thread-->72 Thread-->73 Thread-->74 Thread-->75 Thread-->76 Thread-->77 Thread-->78 Thread-->79 Thread-->80 Thread-->81 Thread-->82 Thread-->83 Thread-->84 Thread-->85 Thread-->86 Thread-->87 Thread-->88 Thread-->89 Thread-->90 Thread-->91 Thread-->92 Thread-->93 Thread-->94 Thread-->95 Thread-->96 Thread-->97 Thread-->98 Thread-->99 main-->21 main-->22 main-->23 main-->24 main-->25 main-->26 main-->27 main-->28 main-->29 main-->30 main-->31 main-->32 main-->33 main-->34 main-->35 main-->36 main-->37 main-->38 main-->39 main-->40 main-->41 main-->42 main-->43 main-->44 main-->45 main-->46 main-->47 main-->48 main-->49 main-->50 main-->51 main-->52 main-->53 main-->54 main-->55 main-->56 main-->57 main-->58 main-->59 main-->60 main-->61 main-->62 main-->63 main-->64 main-->65 main-->66 main-->67 main-->68 main-->69 main-->70 main-->71 main-->72 main-->73 main-->74 main-->75 main-->76 main-->77 main-->78 main-->79 main-->80 main-->81 main-->82 main-->83 main-->84 main-->85 main-->86 main-->87 main-->88 main-->89 main-->90 main-->91 main-->92 main-->93 main-->94 main-->95 main-->96 main-->97 main-->98 main-->99 Process finished with exit code 0
总结:
优先选择第二种方式:因为实现接口的同时,保留了类的继承。
第二种方式也可以使用匿名内部类如下:
Thread t = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("main-->" + i);
}
}
});
t.start();
本质上没有区别,都是普通方法调用。只不过两个方法完成的任务不同。
实例方法:
①String getName(); 获取线程对象的名字;
②void setName(String name);修改线程对象的名字。
静态方法:
①static Thread currentThread();获取当前线程对象的引用。
package threadtest; public class ThreadTest03 { public static void main(String[] args) { Thread t1 = new MyNewThread(); t1.start(); Thread t2 = new MyNewThread(); t2.start(); Thread thread = Thread.currentThread(); System.out.println("当前运行线程:" + thread.getName()); } } class MyNewThread extends Thread{ @Override public void run() { //获取当前运行线程的引用 Thread t = Thread.currentThread(); //获取当前运行线程的名字 System.out.println("当前运行线程:" + t.getName()); } }
运行结果:
创建线程时,若没有给线程指定名字,则会给对应线程赋上默认的名字:Thread-xx
。
可以修改线程名字:
package threadtest; public class ThreadTest03 { public static void main(String[] args) { Thread t1 = new MyNewThread(); t1.setName("t1"); t1.start(); Thread t2 = new MyNewThread(); t2.setName("t2"); t2.start(); Thread thread = Thread.currentThread(); System.out.println("当前运行线程:" + thread.getName()); } } class MyNewThread extends Thread{ @Override public void run() { //获取当前运行线程的引用 Thread t = Thread.currentThread(); //获取当前运行线程的名字 System.out.println("当前运行线程:" + t.getName()); } }
运行结果:
也可以在创建线程时,通过构造方法为线程指定名字:
package threadtest; public class ThreadTest03 { public static void main(String[] args) { Thread t1 = new MyNewThread("tt1"); // t1.setName("t1"); t1.start(); Thread t2 = new MyNewThread("tt2"); // t2.setName("t2"); t2.start(); Thread thread = Thread.currentThread(); System.out.println("当前运行线程:" + thread.getName()); } } class MyNewThread extends Thread{ public MyNewThread() { } //有参构造方法 public MyNewThread(String name) { super(name); } @Override public void run() { //获取当前运行线程的引用 Thread t = Thread.currentThread(); //获取当前运行线程的名字 System.out.println("当前运行线程:" + t.getName()); } }
运行结果:
线程的生命周期有7个状态:
static void sleep(long millis)
:静态方法,没有返回值,参数是为毫秒单位的时间,1秒=1000毫秒。
作用:让当前线程进入休眠,即让当前线程放弃占有的CPU时间片,使其进入超时等待状态。等待时间以设定的毫秒数为准,在该指定的时间范围内,当前线程没有权利抢夺CPU时间片。
如何理解“当前线程”:Thread.sleep(1000)
,这个代码出现在哪个线程中,这个线程就是“当前线程”。
示例代码:
package threadtest; public class ThreadTest04 { public static void main(String[] args) { //让主线程睡眠5秒 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "====>" + i); } Runnable r = new MyNewRunnable(); Thread t = new Thread(r); t.start(); } } class MyNewRunnable implements Runnable{ //run方法在重写时不能在方法声明位置使用 throws 抛出异常,子类不能比父类抛出更多异常,所以这里只能try...catch @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "====>" + i); //让当前线程睡眠1秒再进入下一次循环 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
package threadtest; public class ThreadTest05 { public static void main(String[] args) { MyLatestThread t = new MyLatestThread(); t.setName("t"); t.start(); try { t.sleep(3000); //当前代码是让t线程睡眠3秒吗? } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "====>" + i); } } } class MyLatestThread extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "====>" + i); } } }
以上t.sleep(3000);
并不是让t线程睡眠,而是让当前线程,即主线程main线程睡眠。
语法:线程对象.interrupt(true);
示例代码:
package threadtest.thread06; public class ThreadTest06 { public static void main(String[] args) { //创建线程 Thread t = new MyThread(); t.setName("t"); //启动线程 t.start(); //要求5秒之后睡眠的t线程起来干活 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } //终止t线程的睡眠 //其底层原理是利用了异常处理机制:当调用这个方法时,如果t线程正在睡眠,必然会抛出:InterruptedException,然后捕捉异常,终止睡眠 t.interrupt(); } } class MyThread extends Thread{ @Override public void run() { System.out.println(Thread.currentThread().getName() + "--> begin"); try { //睡眠一年 Thread.sleep(1000*60*60*24*365); } catch (InterruptedException e) { System.out.println("睡眠中断了!"); e.printStackTrace(); } //睡眠一年之后,起来干活了 System.out.println(Thread.currentThread().getName() + " --> work!"); } }
运行结果:
语法:线程对象.stop();
如下代码:
package threadtest.thread07; public class ThreadTest07 { public static void main(String[] args) { Thread t = new Thread(new MyRunnable()); t.setName("t"); t.start(); //当前线程睡眠5秒 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } //终止t线程的执行,从java2开始就不建议使用了,因为这种方式是强行终止线程,容易导致数据丢失。 t.stop(); } } class MyRunnable implements Runnable{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "-->" + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
运行结果:
设置标记。
如下代码:
package threadtest.thread08; public class ThreadTest08 { public static void main(String[] args) { MyRunnable r = new MyRunnable(); Thread t = new Thread(r); t.setName("t"); t.start(); //当前线程睡眠5秒 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } //修改标记,终止线程t的执行 r.ifRun = false; } } class MyRunnable implements Runnable{ //设置标记 boolean ifRun = true; @Override public void run() { for (int i = 0; i < 10; i++) { if(ifRun){ System.out.println(Thread.currentThread().getName() + "-->" + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }else{ return; } } } }
运行结果:
在java语言中,线程被分为两大类:
在JVM中有一个隐藏的守护线程:GC线程。
守护线程的特点:所有用户线程结束之后,守护线程自动退出/结束。
设置一个线程为守护线程(需要在线程启动前设置):线程对象.setDaemon(true);
示例代码:
package threadtest.thread09; public class ThreadTest09 { public static void main(String[] args) { Thread t = new MyThread(); t.setName("t"); //需要再t线程启动前,设置t线程为守护线程,所有用户线程结束守护线程自动结束 t.setDaemon(true); //启动t线程 t.start(); //设置main用户线程10秒后结束 for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "-->" + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } class MyThread extends Thread{ @Override public void run() { int i = 0; //死循环 while (true){ System.out.println(Thread.currentThread().getName() + " --> " + (++i)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
运行结果:
JDK中提供的定时任务:
Timer的构造方法:
Timer的一个方法:
package threadtest.thread10; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimerTask; /** * 定时任务类TimerTask是一个抽象类,其实现了Runnable接口,所以继承该类需要覆写run()方法 */ public class LogTimerTask extends TimerTask { int i = 1; SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS"); @Override public void run() { Date now = new Date(); String strTime = dateFormat.format(now); System.out.println(strTime + " " + (i++)); } }
package threadtest.thread10; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; public class ThreadTest10 { public static void main(String[] args) throws Exception { //创建定时器对象(其本质上就是一个线程) //如果这个定时器执行的任务是一个后台任务,建议将其定义为守护线程 Timer timer = new Timer(true); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date firstTime = dateFormat.parse("2024-03-26 17:45:00"); //指定定时任务为LogTimerTask,第一次执行时间为firstTime,每间隔1秒执行一次。 timer.schedule(new LogTimerTask(), firstTime,1000); //main线程5秒后结束,则上述守护线程也随之结束 for (int i = 0; i < 5; i++) { Thread.sleep(1000); } } }
运行结果:
join()方法是一个实例方法,线程对象调用join()方法完成线程合并。
Thread t = new Thread();
t.setName("t");
t.join();
t.join方法其实是让当前线程进入阻塞状态,直到t线程结束,当前线程阻塞解除。
假设在main方法(main线程)中调用了t.join()方法,则为将t线程合并到main线程中,main线程进入阻塞状态,直到t线程执行结束,main线程解除阻塞。
join()方法和sleep()方法的联系和区别:
示例代码:
package threadtest.thread11; public class ThreadTest11 { public static void main(String[] args) throws InterruptedException { Thread t = new MyThread(); t.setName("t"); t.start(); //合并线程,将t线程合并到main线程中,main线程收到阻塞,t线程继续执行,直到t线程结束,main线程阻塞解除 t.join(); System.out.println("main begin"); //主线程 for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName() + "-->" + i); } System.out.println("main over"); } } class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName() + "-->" + i); } } }
运行结果:
t-->0 t-->1 t-->2 t-->3 t-->4 t-->5 t-->6 t-->7 t-->8 t-->9 t-->10 t-->11 t-->12 t-->13 t-->14 t-->15 t-->16 t-->17 t-->18 t-->19 main begin main-->0 main-->1 main-->2 main-->3 main-->4 main-->5 main-->6 main-->7 main-->8 main-->9 main-->10 main-->11 main-->12 main-->13 main-->14 main-->15 main-->16 main-->17 main-->18 main-->19 main over Process finished with exit code 0
join()方法也可以有参数,参数是毫秒,表示线程合并时长。
package threadtest.thread11; public class ThreadTest11 { public static void main(String[] args) throws InterruptedException { Thread t = new MyThread(); t.setName("t"); t.start(); //合并线程,将t线程合并到main线程中,main线程受到阻塞,t线程继续执行,直到t线程结束,main线程阻塞解除 //合并时长为10毫秒,即只阻塞当前线程10毫秒,但并不一定能达到设置的阻塞时长,如果在指定的时间内,t线程早就结束了,则阻塞就立即解除了。 t.join(10); System.out.println("main begin"); //主线程 for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName() + "-->" + i); } System.out.println("main over"); } } class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName() + "-->" + i); } } }
线程是可以设置优先级的,JVM采用的是抢占式调度模型,优先级高的线程获取CPU时间片的总体概率会高一些。
默认情况下一个线程的优先级是5,最低为1,最高为10。
package threadtest.thread12; public class ThreadTest12 { public static void main(String[] args) { System.out.println("最低优先级" + Thread.MIN_PRIORITY); System.out.println("最高优先级" + Thread.MAX_PRIORITY); System.out.println("默认优先级" + Thread.NORM_PRIORITY); //获取线程的优先级 System.out.println("当前线程优先级:" + Thread.currentThread().getPriority()); //设置优先级 Thread.currentThread().setPriority(10); System.out.println("当前线程优先级:" + Thread.currentThread().getPriority()); } }
运行结果:
给多个线程设置不同优先级:
package threadtest.thread12; public class ThreadTest12 { public static void main(String[] args) { Thread t1 = new MyThread(); t1.setName("t1"); Thread t2 = new MyThread(); t2.setName("t2"); //设置t1为最高优先级,t2为最低优先级,相对来说t1指定的概率高一些 t1.setPriority(Thread.MAX_PRIORITY); t2.setPriority(Thread.MIN_PRIORITY); t1.start(); t2.start(); } } class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName() + "-->" + i); } } }
静态方法:Thread.yield(),当前线程让位。
注意:让位不会使当前线程进入阻塞状态,只是放弃目前占有的CPU时间片,进入就绪状态,继续抢夺CPU时间片。
示例代码:
package threadtest.thread13; /** * 线程让位 */ public class ThreadTest13 { public static void main(String[] args) { Thread t1 = new MyThread(); t1.setName("t1"); Thread t2 = new MyThread(); t2.setName("t2"); t1.start(); t2.start(); } } class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { if(Thread.currentThread().getName().equals("t1") && i%10 == 0){ //当前线程为t1线程,且i为10的倍数时,进行线程让位 System.out.println(Thread.currentThread().getName() + "让位了,此时i=" + i); //注意:t1线程让位只是放弃当前占有的时间片,至于后续时间片被哪个线程抢占并不一定,也有可能t1会继续抢到 Thread.yield(); } System.out.println(Thread.currentThread().getName() + "==>" + i); } } }
运行结果就不贴了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。