线程Thread中的方法详解(二)
1、start()
- start()方法的作用讲得直白点就是通知"线程规划器",此线程可以运行了,正在等待CPU调用线程对象得run()方法,产生一个异步执行的效果。通过start()方法产生得到结论,先看下代码:
/** * @author zs * @date 2019/9/20 10:13 */ public class MyTicketThread extends Thread { private int ticket=5;//仅剩五张票 public MyTicketThread(String threadName) { super(threadName);//调用父类的带参构造方法 } @Override public void run() { // TODO Auto-generated method stub for(int i=0;i<100;i++){ synchronized (this) { if(ticket>0){ try { Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "卖第" + (ticket--) + "张票"); } } } } }
/**
* @author zs
* @date 2019/9/20 10:14
*/
public class Test {
public static void main(String[] args) {
//4个窗口
MyTicketThread t1=new MyTicketThread("A窗口");
MyTicketThread t2=new MyTicketThread("B窗口");
MyTicketThread t3=new MyTicketThread("C窗口");
MyTicketThread t4=new MyTicketThread("D窗口");
//启动线程
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行结果:
B窗口卖第5张票
C窗口卖第5张票
D窗口卖第5张票
A窗口卖第5张票
D窗口卖第4张票
A窗口卖第4张票
B窗口卖第4张票
C窗口卖第4张票
D窗口卖第3张票
A窗口卖第3张票
...
- 解释:可以看出,线程执行的先后顺序,是不确定的。
2、currentThread()
currentThread()方法返回的是对当前正在执行线程对象的引用。
在线程类中,有两种方式,一定要理解这两种方式的区别:
(1)this.XXX()
这种调用方式表示的线程是线程实例本身
(2)Thread.currentThread.XXX()或Thread.XXX()
上面两种写法是一样的意思。这种调用方式表示的线程是正在执行Thread.currentThread.XXX()所在代码块的线程
下面通过看一个重要的例子,然后得出结论:
public class MyThread04 extends Thread { static { System.out.println("静态块的打印:" + Thread.currentThread().getName()); } public MyThread04() { System.out.println("构造方法的打印:" + Thread.currentThread().getName()); } public void run() { System.out.println("run()方法的打印:" + Thread.currentThread().getName()); } }
public static void main(String[] args) { MyThread04 mt = new MyThread04(); mt.start(); }
看一下运行结果:
静态块的打印:main 构造方法的打印:main run()方法的打印:Thread-0
这个例子说明了,线程类的构造方法、静态块是被main线程调用的,而线程类的run()方法才是应用线程自己调用的。在这个例子的基础上,再深入:
public class MyThread05 extends Thread { public MyThread05() { System.out.println("MyThread5----->Begin"); System.out.println("Thread.currentThread().getName()----->" + Thread.currentThread().getName()); System.out.println("this.getName()----->" + this.getName()); System.out.println("MyThread5----->end"); } public void run() { System.out.println("run----->Begin"); System.out.println("Thread.currentThread().getName()----->" + Thread.currentThread().getName()); System.out.println("this.getName()----->" + this.getName()); System.out.println("run----->end"); } }
public static void main(String[] args) { MyThread05 mt5 = new MyThread05(); mt5.start(); }
看一下运行结果:
MyThread5----->Begin Thread.currentThread().getName()----->main this.getName()----->Thread-0 MyThread5----->end run----->Begin Thread.currentThread().getName()----->Thread-0 this.getName()----->Thread-0 run----->end
可以看出:当前执行的Thread未必就是Thread本身。从这个例子就能看出来:
(1)执行MyThread05构造方法是main,当前线程却是Thread-0
(2)执行run()方法的Thread-0,当前线程也是Thread-0,说明run()方法就是被线程实例去执行的
所以,再强调一下,未必在MyThread05里调用Thread.currentThread()返回回来的线程对象的引用就是MyThread05。
3、sleep(long millis)
sleep(long millis)方法的作用是在指定的毫秒内让当前"正在执行的线程"休眠(暂停执行)。这个"正在执行的线程"是关键,指的是Thread.currentThread()返回的线程。根据JDK API的说法,"该线程不丢失任何监视器的所属权",简单说就是sleep代码上下文如果被加锁了,锁依然在,但是CPU资源会让出给其他线程。看一下例子:
public class MyThread07 extends Thread { public void run() { try { System.out.println("run threadName = " + this.getName() + " begin"); Thread.sleep(2000); System.out.println("run threadName = " + this.getName() + " end"); } catch (InterruptedException e) { e.printStackTrace(); } } }
public static void main(String[] args) { MyThread07 mt = new MyThread07(); System.out.println("begin = " + System.currentTimeMillis()); mt.start(); System.out.println("end = " + System.currentTimeMillis()); }
看一下运行结果:
begin = 1443694780609 end = 1443694780609 run threadName = Thread-0 begin run threadName = Thread-0 end
当然,因为打印结果是静态的,所以只能看出异步执行的效果,看不出sleep(long millis)方法执行的效果。实际上第3句打出2秒后打出第4句,这和run()方法里面的sleep(2000)是对应的
4、yield()
暂停当前执行的线程对象,并执行其他线程。这个暂停是会放弃CPU资源的,并且放弃CPU的时间不确定,有可能刚放弃,就获得CPU资源了,也有可能放弃好一会儿,才会被CPU执行。看一下例子:
public class MyThread08 extends Thread { public void run() { long beginTime = System.currentTimeMillis(); int count = 0; for (int i = 0; i < 50000000; i++) { Thread.yield(); count = count + i + 1; } long endTime = System.currentTimeMillis(); System.out.println("用时:" + (endTime - beginTime) + "毫秒!"); } }
public static void main(String[] args) { MyThread08 mt = new MyThread08(); mt.start(); }
看一下运行结果:
用时:3264毫秒! 用时:3299毫秒! 用时:3232毫秒! 用时:3256毫秒! 用时:3283毫秒! 用时:3504毫秒! 用时:3378毫秒!
看到,每次执行的用时都不一样,证明了yield()方法放弃CPU的时间并不确定。
5、注意
1、sleep()和wait()方法的区别,三点:
- sleep()是Thread中的方法,线程休眠;wait()是Object中的方法,线程等待;
- 都导致线程的阻塞;
- sleep()对对象所持有的锁不释放,wait()释放所持有的锁;
2、Java中Wait、Sleep和Yield方法的区别
- wait()是Object的方法,用于线程间的通信,线程等待,进入阻塞状态,会释放锁;
- sleep是Thread的静态方法,调用该方法,令当前线程休眠,进入阻塞状态,不会释放锁;
- yield和sleep的主要是,yield方法会临时暂停当前正在执行的线程,来让有同样优先级的正在等待的线程有机会执行。如果没有正在等待的线程,或者所有正在等待的线程的优先级都比较低,那么该线程会继续运行。执行了yield方法的线程什么时候会继续运行由线程调度器来决定,不同的厂商可能有不同的行为。yield方法不保证当前的线程会暂停或者停止,但是可以保证当前线程在调用yield方法时会放弃CPU。
在所有的矛盾中,要优先解决主要矛盾,其他矛盾也就迎刃而解。
不要做个笨蛋,为失去的郁郁寡欢,聪明的人,已经找到了解决问题的办法,或正在寻找。