public static void main(String[] args) { ThreadTest(); System.out.println("abc"); } public static void ThreadTest(){ for(int i=0;i<10000;i++){ System.out.println(i); } }
执行main方法可以看到输出结果是按照顺序先执行ThreadTest方法,输出1--9999的数字,之后输出的abc(从上往下输出)
效果如下:
使用线程
创建线程有三种方法
注意:多次启动一个线程使非法的,特别是当线程已经结束执行后,不能再重新启动。
1. 继承Thread类,重写Thread类的run方法
创建步骤:
(1)定义一个类继承Thread
(2)重写run方法
(3)创建子类对象,就是创建线程对象。
(4)调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法。
public static void main(String[] args) { SubThread st = new SubThread();//创建一个继承了Thread类的对象 //st.run();//如果直接调用run,还是单线程程序 st.start();//调用start方法,是多线程程序 for(int i=0;i<50;i++){ System.out.println("main"+i); } } //继承Thread类,重写run方法 public class SubThread extends Thread{ public void run(){ for(int i=0;i<50;i++){ System.out.println("run:"+i); } } }
执行main方法,可以看到并不是如单线程一样从上到下执行的。
start方法的作用:使该线程开始执行,Java虚拟机调用该线程的run方法。
效果:
总结一下:
1. 上面的程序,其实是两个线程,一个是运行main方法的主线程,一个是运行run方法的线程,这两个线程都会被CPU选择(随机选择)。
2. 为什么要继承Thread类呢?因为Thread类是用来描述线程,具备线程该有的功能。那么为什么不直接创建Thread类的对象呢?比如,Thread t = new Thread(); t.start()这种写法。首先要说这么写没有问题,调用start()方法的时候也会去执行run方法,但是这个run方法是Thread类里的run方法,里面没有我们想要运行的代码,我们也不能将我们要运行的代码写到这里。
3. 创建线程的目的是什么?是为了建立程序独立的执行路径,让多部分代码实现同时执行。也就是说线程创建并执行需要给定线程要执行的任务。对于之前主线程,它的任务定义在main函数中。自定义的线程需要执行的任务都定义在run方法中。
4. 线程对象调用run方法和start方法的区别?
(1)调用run方法不开启线程,仅仅是对象调用方法。
(2)调用start方法开启线程,并让jvm调用run方法在开启的线程中执行。
5. 多线程执行得时候,在内存中是如何运行的呢?
多线程执行的时候,在栈内存中,其实每一个执行线程都有一片自己所属的栈内存空间。进行方法的压栈和弹栈。也可以说栈内存都是线程私有的。当执行线程的任务结束了,线程自动在栈内存中释放了。但是当所有的执行线程都结束了,那么进程就结束了。
2. 实现Runnable接口,实现run方法。
实现Runnable接口,避免了继承Thread类的单继承局限性。覆盖Runnable接口中的run方法,将线程任务代码定义到run方法中。
创建Thread类的对象,只有创建Thread类的对象才可以创建线程。线程任务已被封装到Runnable接口的run方法中,而这个run方法所属于Runnable接口的子类对象,所以将这个子类对象作为参数传递给Thread的构造函数,这样,线程对象创建时就可以明确要运行的线程的任务。
实现Runnable的好处。既避免了单继承的局限性,Runnable接口对线程对象和线程任务进行解耦。
public static void ThreadTest(){ SubRunnable sr = new SubRunnable(); Thread t = new Thread(sr); t.start(); for(int i=0;i<10000;i++){ System.out.println("main"+i); } } public class SubRunnable implements Runnable{ public void run(){ for(int i=0;i<10;i++){ System.out.println("run"+i); } } }
3. 实现Callable接口。
具体的使用方法参照下篇文章线程池中的案例。
线程的名字
1. 线程名字的获取(getName和currentThread方法)
(1)主线程名称就是main。因为getName()方法是非静态的,并且静态方法(mian)是不允许调用非静态的方法的,所以main中不能直接写getName方法,需要写currentThread方法
public static void main(String[] args) { System.out.println(Thread.currentThread());//哪个线程运行的这句代码,获取到的就是线程的对象 System.out.println(Thread.currentThread().getName());//哪个线程运行的这句代码,获取到的就是线程的对象 }
效果:
(2)其他线程名字都为Thread-0、Thread-1...等等。方法是直接在run方法中写super.getName()即可,其中super可以省略。也可以写currentThread方法
public class SubThread extends Thread{ public void run(){ System.out.println(super.getName()); System.out.println(getName()); System.out.println(Thread.currentThread()); System.out.println(Thread.currentThread().getName()); } }
效果:
2. 线程名字的设置(setName方法)
(1)最好是在start方法上面先定义,逻辑上比较好理解。
public static void main(String[] args) { SubThread st = new SubThread();//创建一个继承了Thread类的对象 st.setName("Thread-111"); st.start();//调用start方法,是多线程程序 } public class SubThread extends Thread{ public void run(){ System.out.println(super.getName()); System.out.println(getName()); System.out.println(Thread.currentThread()); System.out.println(Thread.currentThread().getName()); } }
(2)子类中设置
public static void main(String[] args) { SubThread st = new SubThread();//创建一个继承了Thread类的对象 st.start();//调用start方法,是多线程程序 } public class SubThread extends Thread{ public SubThread(){ super("222"); } public void run(){ System.out.println(super.getName()); System.out.println(getName()); System.out.println(Thread.currentThread()); System.out.println(Thread.currentThread().getName()); } }
效果:
Thread类的常用方法
Thread类的sleep方法
sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠(暂时停止执行),此操作受到系统计时器和调度程序精度和准确性的影响。该线程不丢失任何监视器的所属权。
下面代码的效果:每隔1s输出一次。
public static void main(String[] args) throws InterruptedException { for(int i=0;i<10;i++){ Thread.sleep(1000);//暂停1s System.out.println(i); } }
如果是写在run方法里,由于不能够抛出异常,所以需要写try catch
public class SubThread extends Thread{ public void run(){ for(int i=0;i<10;i++){ try { Thread.sleep(1000);//暂停1s } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i); } } }
匿名内部类实现线程程序
注意:
1. 匿名内部类的前提,必须有继承或者接口的实现。
2. 写法为new 父类或者接口(){重写抽象方法}
//匿名内部类的前提,必须有继承或者接口的实现 //new 父类或者接口(){ // 重写抽象方法 //} public static void main(String[] args) throws InterruptedException { //写法一:继承方式 xxx extends Thread{public void run(){}} new Thread(){ public void run(){ System.out.println("1"); } }.start(); //写法二:实现接口方式 xxx implement Runnable{public void run(){}} Runnable r = new Runnable() { public void run() { System.out.println("2"); } }; new Thread(r).start(); //写法三: new Thread(new Runnable() { @Override public void run() { System.out.println("3"); } }).start(); }
线程的状态
NEW
尚未启动的线程处于此状态。
RUNNABLE
在Java虚拟机中执行的线程处于此状态。
BLOCKED
被阻塞等待监视器锁定的线程处于此状态。
WAITING
正在等待另一个线程执行特定动作的线程处于此状态。
TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
TERMINATED
已退出的线程处于此状态。
参考:
1. 黑马程序员视频
持续更新!!!
来源:https://www.cnblogs.com/flyinghome/p/12145474.html