1.关于多线程
1.1 线程与进程的区别
进程:系统中每一个正在运行的程序都是一个进程,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元
线程:是一组指令的集合,控制着进程的执行,一个进程中至少有一个线程
1.2 为什么用多线程
使用多线程可以将执行时间长的程序中的任务放到后台去处理,在一些需要等待的任务上如文件读写、网络分发数据等,使用多线程就可以更好的利用CPU的资源,从而提高程序运行的效率
2. 线程的状态
线程有5种状态:新建、就绪、运行、阻塞、死亡,如下图所示:
新建状态:当new一个线程时, 例如new Thread(r),线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码
就绪状态:一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由java运行时系统的线程调度程序(thread scheduler)来调度的。
运行状态:当线程竞争到CPU时间之后,执行run()方法,进入运行状态
阻塞状态:线程运行过程中,可能由于各种原因进入阻塞状态:
1)线程通过调用sleep方法进入睡眠状态;
2)线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
3)线程试图得到一个锁,而该锁正被其他线程持有;
4)线程在等待某个触发条件;
所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。
死亡状态:有两种原因会导致线程进入死亡状态:
1)run()方法执行完成正常死亡
2)发生异常终止了run()方法而进入死亡状态
3. 创建线程的方法
3.1 继承Thread类,重写run()方法
class CreateThread extends Thread { publicvoid run() { // run方法中编写 多线程需要执行的代码 } }
3.2 实现Runnable接口,重写run()方法
class CreateRunnable implements Runnable { @Override publicvoid run() { //run方法中编写多线程需要执行的代码 } }
3.3 匿名内部类方式
Thread thread = new Thread(new Runnable() { public void run() { //run方法中编写多线程需要执行的代码 } });
4. 常用的方法
4.1 join()
join():当线程B执行到了线程A的.join()方法时,B线程就会等待,等A线程都执行完毕,B线程才会执行,join可以用来临时加入线程执行。
public class Thread002 { public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.print("这个是t1线程:" + i +" "); } System.out.println(" --------------分割线------------------"); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { try { t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 10; i++) { System.out.print("这个是t2线程:" + i+ " "); } System.out.println(" --------------分割线------------------"); } }); Thread t3 = new Thread(new Runnable() { @Override public void run() { try { t2.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } for (int i = 0; i < 10; i++) { System.out.print("这个是t3线程:" + i +" "); } } }); t1.start(); t2.start(); t3.start(); } }
4.2 sleep()、yield()
sleep():让正在执行的线程休眠,因为使用sleep方法之后,线程是进入阻塞状态的,只有当睡眠的时间结束,才会重新进入到就绪状态,而就绪状态进入到运行状态,是由系统控制的,我们不可能精准的去干涉它,所以如果调用Thread.sleep(1000)使得线程睡眠1秒,可能结果会大于1秒
public class SynTest { public static void main(String[] args) { new Thread(new CountDown(),"倒计时").start(); } } class CountDown implements Runnable{ int time = 10; public void run() { while (true) { if(time>=0){ System.out.println(Thread.currentThread().getName() + ":" + time--); try { Thread.sleep(1000); //睡眠时间为1秒 } catch (InterruptedException e) { e.printStackTrace(); } } } } }
yield():和sleep方法类似,也是Thread类提供的一个静态方法,可以让正在执行的线程暂停,但是不会进入阻塞状态,而是直接进入就绪状态。相当于只是将当前线程暂停一下,然后重新进入就绪的线程池中,让线程调度器重新调度一次。也会出现某个线程调用yield方法后暂停,但之后调度器又将其调度出来重新进入到运行状态
public class SynTest { public static void main(String[] args) { yieldDemo ms = new yieldDemo(); Thread t1 = new Thread(ms,"张三吃完还剩"); Thread t2 = new Thread(ms,"李四吃完还剩"); Thread t3 = new Thread(ms,"王五吃完还剩"); t1.start(); t2.start(); t3.start(); } } class yieldDemo implements Runnable{ int count = 20; public void run() { while (true) { if(count>0){ System.out.println(Thread.currentThread().getName() + count-- + "个瓜"); if(count % 2 == 0){ Thread.yield(); //线程让步 } } } } }