所谓的线程就是程序的执行线路。平时我们所写的程序几乎都是单线程的,即程序的执行路径只有一个。但是,在许多应用中单线程是不足以满足我们的要求的。比如:mp3中的功能,如果用单线程去实现,只能播完歌曲之后再显示歌词或者其他,显然不行,我们要求的是边播放歌曲边滚动歌词。这就要用到了我们的多线程,以达到多个功能并发执行。
在我们平时的程序运行时,主要自启动了两个线程:
◆main主线程,调用main方法,执行main方法
◆GC线程:垃圾回收机制,主要用于main线程运行时运行,回收程序之中产生的垃圾
一:线程的构造
1:继承自Thread的线程类 线程名称 = new 继承自Thread的线程类();-->创建了一个继承自Thread的线程类
2:Thread 线程名称 = new Thread (实现了Runnable接口的线程类);-->创建一个已经实现了Runnable接口的线程类
二:如何写多线程?
1:继承自Thread类,重写run方法,然后调用Thread的start方法启动这个新线程
public class TestThread { public static void main(String[] args) { MyThread m = new MyThread();//创建自己定义的线程对象 m.start();//启动这个线程对象用start //m.run();相当于方法的调用,并没有开启新线程 for(int i =0;i<10;i++){ System.out.println("main-->"+(i+1)); } } } class MyThread extends Thread{//单独定义一个类,继承自Thread类,重写run方法,实现多线程 @Override public void run() { for(int i =0;i<10;i++){ System.out.println("run-->"+(i+1)); } } } |
结果:
main-->1 run-->1 main-->2 run-->2 main-->3 run-->3 main-->4 run-->4 main-->5 run-->5 main-->6 run-->6 main-->7 run-->7 main-->8 run-->8 main-->9 run-->9 main-->10 run-->10 |
结果分析:从结果中可以发现,每次运行这个程序,结果可能不唯一,这是因为程序的执行需要CPU参与, 谁得到了执行权,谁的线程可以运行。main线程和自己创建的线程之间交替进行,也可以说是一种乱序执行,这就是我们所说的多线程之间的乱序执行,那单线程怎么执行的呢?
单线程也就是我们平时所编写的普通程序,如下:
public class TestThread { public static void main(String[] args) { MyThread m = new MyThread();//创建自己定义的线程对象 //m.start();//启动这个线程对象用start m.run();//相当于方法的调用,并没有开启新线程,仍是单线程 for(int i =0;i<10;i++){ System.out.println("main-->"+(i+1)); } } } class MyThread extends Thread{//单独定义一个类,继承自Thread类,重写run方法,实现多线程 @Override public void run() { for(int i =0;i<10;i++){ System.out.println("run-->"+(i+1)); } } } |
结果:
run-->1 run-->2 run-->3 run-->4 run-->5 run-->6 run-->7 run-->8 run-->9 run-->10 main-->1 main-->2 main-->3 main-->4 main-->5 main-->6 main-->7 main-->8 main-->9 main-->10 |
结果分析:细心的会发现,这个程序只和上边的那个程序有一句之差,而仅仅是这一句之差,让他变成了单线程,这个程序无论执行多少次,结果只有这一个,这就说明:单线程内部顺序执行。
2:实现Runnable接口,实现接口中的run方法,创建对象,作为Thread构造的参数,调用thread的start方法启动一个新线程。
public class MyRunnable { public static void main(String[] args) { MyRun m = new MyRun();//创建实现Runnable接口线程的对象 Thread th = new Thread(m);//作为参数传递给线程 th.start();//线程的启动 for(int i = 0;i<100;i++){ System.out.println("main-->"+(i+1)); } } } class MyRun implements Runnable{//线程的第二种创建方法,实现Runnable接口 @Override public void run() { for(int i = 0;i<100;i++){ System.out.println("run-->"+(i+1)); } } }
结果:结果仍然同第一种创建线程的乱序结果(因为是乱序,结果不唯一,可自行试试)
三:线程的常用方法
1:start():启动一个新线程,如“线程.start()”这样使用
2:run():创建线程时,要重写的方法,也是线程启动后运行的方法
3:Thread.currentThread():获得当前线程
4:setName(String name):设置线程名称,也可以在创建实现Runnable接口的类对象的参数中传递。
5:getName():获得线程名称,主线程的名字默认为main,其余线程的默认名字形式为:Thread-0,Thread-1,Thread-2...
public class MyRunnable { public static void main(String[] args) { MyRun m = new MyRun();//创建实现Runnable接口线程的对象 Thread th = new Thread(m);//作为参数传递给线程 th.start();//线程的启动 th.setName("天津");//给线程命名 Thread.currentThread().setName("哈尔滨");//给当前线程main命名 for(int i = 0;i<100;i++){ System.out.println(Thread.currentThread().getName()+"-->"+(i+1)); } } } class MyRun implements Runnable{//线程的第二种创建方法,实现Runnable接口 @Override public void run() { for(int i = 0;i<100;i++){ System.out.println(Thread.currentThread().getName()+"-->"+(i+1)); } } } |
结果:图中的结果只是截取的,这里要说明的是以上5个方法的使用。任然是乱序执行。
天津-->77 哈尔滨-->90 天津-->78 哈尔滨-->91 天津-->79 哈尔滨-->92 天津-->80 哈尔滨-->93 |
6:getPriority()、setPriority():得到和设置线程的优先级。优先级范围:1-10。默认为5。优先级也就是谁的优先级高谁就有更大的可能性运行,所以低优先级的线程并不是不执行,只是没有那么大的机会被执行。在资源不足的情况下有所体现。
public class TestPerority { public static void main(String[] args) { Run m = new Run(); Thread th1 = new Thread(m); th1.setPriority(10);//设置线程1 的优先级为10 th1.start(); Thread th2 = new Thread(m); th2.setPriority(1);//设置线程2 的优先级为1 th2.start(); } } class Run implements Runnable{ @Override public void run() { for(int i = 0;i<1000;i++){//设置的大一点,看出的效果会大一点 System.out.println(Thread.currentThread().getName()+"-->"+(i+1)); } } } |
结果:可以从结果中看出线程0的优先级高,当他执行到83时,线程1才执行到73。结果仍为乱序。
Thread-0-->83 Thread-1-->73 Thread-0-->84 Thread-1-->74 Thread-0-->85 Thread-1-->75 |
7:sleep():让线程休眠(停止运行)一段时间
public class TestSleep { public static void main(String[] args) { MySleep m = new MySleep(); Thread th = new Thread(m); th.start(); } } class MySleep implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { try { Thread.sleep(1000);//让线程停止1秒输出一个,停止1秒输出一个 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "-->" + (i + 1)); } } } |
执行效果:线程每隔1秒输出一个数,直到输出到100为止。
8:join():当线程A运行的时候,若线程Bjoin() 进来的话,则暂停A的执行,先执行B,直到B执行结束之后,才继续开始A的执行。
public class Testjoin { public static void main(String[] args) { MyJoin m = new MyJoin(); Thread th = new Thread(m); th.start(); try { //在main中把th添加进来,只有th执行完毕之后才开始执行main th.join(); } catch (InterruptedException e) { e.printStackTrace(); } for(int i = 0;i<100;i++){ System.out.println(Thread.currentThread().getName()+"-->"+(i+1)); } } } class MyJoin implements Runnable{ @Override public void run() { for(int i = 0;i<100;i++){ System.out.println(Thread.currentThread().getName()+"-->"+(i+1)); } } } |
结果:
Thread-0-->1 。。。。。。 Thread-0-->97 Thread-0-->98 Thread-0-->99 Thread-0-->100 main-->1 main-->2 main-->3 main-->4 main-->5 。。。。。。 main-->100 |
结果分析:这个程序如果去掉try-catch块中的内容,执行效果和第二个大问题中的第一点一样,应为乱序执行,而加入了join方法之后导致,自己创建的线程先执行,执行结束后才执行main线程。
9:yield():让位,我把执行权先让出来,大家抢,谁抢到了谁执行,抢不过我,就我执行。
public class TestYeild { public static void main(String[] args) { MyYeild m = new MyYeild(); Thread th = new Thread(m); th.start(); for(int i = 0;i<1000;i++){ System.out.println(Thread.currentThread().getName()+"-->"+(i+1)); } } } class MyYeild implements Runnable{ @Override public void run() { for(int i = 0;i<1000;i++){ System.out.println("让你一下"); //让你一下,你抢得过我你就执行,否则还是我执行 Thread.yield(); System.out.println(Thread.currentThread().getName()+"-->"+(i+1)); } } } |
结果:图中结果为截取的。
main-->958 Thread-0-->633 让你一下 main-->959 main-->960 main-->961 main-->962 main-->963 main-->964 main-->965 main-->966 main-->967 main-->968 main-->969 main-->970 main-->971 main-->972 main-->973 Thread-0-->634 让你一下 Thread-0-->635 |
结果分析:线程让位了一下,导致程序结果中的一段如图所示,说明在某一时刻,main线程抢过了自己创建线程的执行权。
10:setDaemon(boolean bool):将线程设置为守护线程。守护线程一般用于为其他线程提供服务。如GC是一个典型的守护线程。守护线程的特点是:他会随着其他非守护线程结束而结束,且只能在调用start方法之前使用。isDaemon()方法用于判断一个线程是不是守护线程。
public class TestDemo { public static void main(String[] args) { Demo demo = new Demo(); Thread th = new Thread(demo); th.setDaemon(true);//在调用start之前设置为守护线程 th.start(); for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "-->" + (i + 1)); } } } class Demo implements Runnable { @Override public void run() { while (true) { System.out.println("我是守护线程"); } } } |
结果:
我是守护线程 我是守护线程 main-->1 main-->2 main-->3 main-->4 main-->5 main-->6 main-->7 main-->8 main-->9 main-->10 我是守护线程 我是守护线程 |
结果分析:图中的结果是因为守护线程随着主线程的运行而运行,而主线程运行结束之后,通知虚拟机,我结束了,而在通知的这一段时间之内,守护线程又执行了一会,说以就没有马上结束。
线程还有许多应用,我们明天继续哈...