线程与进程
进程:
1.指在运行中的程序,例如使用微信就启动一个进程,操作系统就会为 该进程分配内存空间;
2.进程是程序的一次执行的过程,或者是正在运行的一个程序;是动态过程,有它自身的产生、存在和消亡的过程。
线程:
1.线程由进程创建的,是进程的一个实体;
2.一个进程可以拥有多个线程;
3.单线程:同一时刻,只允许执行一个线程;
4.多线程:同一时刻,可以执行多个线程。
并发和并行
并发:
同一时刻,多个任务交替执行;单核CPU实现的多任务就是并发
并行:
同一个时刻,多个任务同时执行,多核CPU可以实现并行
线程的使用
继承Thread创建线程,重写run方法
public static void main(String[] args) throws InterruptedException { //创建Dog对象,可以当做线程使用 Dog dog = new Dog(); //启动线程,执行dog的run方法 dog.start(); //当main线程启动一个子线程 Thread -0,主线程不会阻塞,而是继续执行 //主线程和子线程交替执行 for (int i = 0; i < 80; i++) { System.out.println("主线程 i = " + i + Thread.currentThread().getName()); Thread.sleep(1000); } }
//演示通过继承Thread来创建线程 /*说明: * 1.当一个类继承Thread类,该类就可以做为线程使用 * 2.需要重写run方法,在run方法里面写自己需要实现的结果 * 3.Thread类中的run方法,是Thread类实现了Runnable接口的run方法 * */ class Dog extends Thread{ @Override public void run() {//重写run方法,设置自己需要的实现的结果 int temp = 0; while (true) { temp ++; //该线程在1s钟,在控制台输出:汪汪汪... System.out.println("汪汪汪..." + "循环次数:" + temp + Thread.currentThread().getName()); //让线程休息1s try { Thread.sleep(1000);//1000 = 1s } catch (InterruptedException e) { e.printStackTrace(); } //当循环20次后退出循环 if(temp == 100){ break; } } } }
实现Runnable接口,重写run方法
说明:
1.JAVA是单继承的,在某些情况下一个类可能已经继承了某个父类,这是就不能再去继承Thread类方法来创建线程。
2.因此在上面那种情况下,就只能使用提供的另一个方法创建线程,通过实现Runnable接口创建线程。
public static void main(String[] args) { Tiger tiger = new Tiger(); //调用Thread类来调用start方法去实现 Thread thread = new Thread(tiger); thread.start(); }
class Tiger implements Runnable{ int count = 0; @Override public void run() {//普通方法 while (true){ count ++; System.out.println("小老虎..." + count + Thread.currentThread().getName() ); try { Thread.sleep(1000);//延时1s } catch (InterruptedException e) { e.printStackTrace(); } if(count == 20){ break; } } } }
使用jconsole监控进程执行情况
多个子线程案例
public static void main(String[] args) { //在main线程中启动两个子线程 Banana banana = new Banana(); Thread thread = new Thread(banana); thread.start(); Apple apple = new Apple(); Thread thread1 = new Thread(apple); thread1.start(); }
//每隔1s输出,且输出100次 class Banana implements Runnable{ @Override public void run() { int num01 = 0; while (true){ num01 ++; System.out.println("Hello World" + num01 + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (num01 == 10){ break; } } } } // class Apple implements Runnable{ @Override public void run() { int num02 = 0; while (true){ num02++; System.out.println("hi ..." + num02 + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (num02 == 5){ break; } } } }
继承Thread和实现Runnable接口的区别
-
通过继承Thread或者实现Runnable接口来创建线程本质是没有区别的;Thread类的本身就实现了Runnable接口
-
实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制,建议一般情况下使用Runnable接口
练习题:实现多线程售票问题
public static void main(String[] args) { //使用多线程,模拟三个窗口同时售票 /* //第一种方法:使用Thread类 SellTicket01 sellTicket01 = new SellTicket01(); SellTicket01 sellTicket02 = new SellTicket01(); SellTicket01 sellTicket03 = new SellTicket01(); sellTicket01.start(); sellTicket02.start(); sellTicket03.start();*/ //第二种方法:实现Runnable接口 SellTicket02 sellTicket02 = new SellTicket02(); Thread thread01 = new Thread(sellTicket02); Thread thread02 = new Thread(sellTicket02); Thread thread03 = new Thread(sellTicket02); thread01.start(); thread02.start(); thread03.start(); }
//第一种方法:继承Thread class SellTicket01 extends Thread{ //总票数100张 private static int num = 100; @Override public void run() { while (true){ if (num <= 0){ System.out.println("票已售完..."); break; } --num; System.out.println("售票处:" + Thread.currentThread().getName() + "\t" + "售出一张票" + "\t" + "剩余票数=" + num); //延时1s try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } }
//第二种方法:实现Runnable接口 //第一种方法:继承Thread class SellTicket02 implements Runnable{ //总票数100张 private int num = 100; @Override public void run() { while (true){ if (num <= 0){ System.out.println("票已售完..."); break; } --num; System.out.println("售票处:" + Thread.currentThread().getName() + "\t" + "售出一张票" + "\t" + "剩余票数=" + num); //延时1s try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
通知程序退出(程序终止)
1.当线程完成任务后,会自动退出
2.可以通过使用变量来控制run方法退出的方式停止线程,即通知方式
public static void main(String[] args) throws InterruptedException { Peach peach = new Peach(); peach.start(); //先延时10s,将线程停止退出 Thread.sleep(10*1000); //停止线程 peach.setLoop(false); }
class Peach extends Thread{ private int num = 0; private boolean loop = true; //可修改loop public void setLoop(boolean loop) { this.loop = loop; } @Override public void run() { while (loop){ num ++; System.out.println("Peach线程正在执行..." + "\t"+Thread.currentThread().getName() + "\t" +num); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
线程中断
1.setName //设置线程名称,使与参数name相同
2.getName //返回该线程的名称
3.start //使该线程开始执行
4.run //调用线程对象run方法
5.setPriority //更改线程的优先级
6.getPriority //获取线程的优先级
7.sleep //在指定的毫秒数内让当前正在执行的线程休眠(暂时执行)
8.interrupt //中断线程
注意:
1.start底层会创建新的线程,调用run,run就是一个方法的调用,不会启动新的线程
2.线程优先级的范围:MAX_PRIORITY = 10 ;MIN_PRIORITY = 1;NORM_PRIORITY = 5
3.interrupt,中断程序;并未使得程序结束只是暂停;一般用于中断正在休眠的线程
4.sleep;线程的静态方法。使得当前线程休眠
public static void main(String[] args) throws InterruptedException { Lemon lemon = new Lemon(); lemon.setName("线程01");//修改线程名称 lemon.setPriority(Thread.MIN_PRIORITY);//设置优先级:最低 lemon.start(); System.out.println(lemon.getName()); for(int j = 0 ;j < 5;j++){ Thread.sleep(1000); System.out.println("hello..." + j); } lemon.interrupt();//执行到中断方法,就会中断lemon线程的休眠 }
class Lemon extends Thread{ @Override public void run() { while (true) { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "吃柠檬..." + i); } System.out.println(Thread.currentThread().getName() + "吃柠檬..." + "休眠中..."); try { Thread.sleep(50 * 1000); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + "吃柠檬..." + "被interrupt ..."); } } } }
线程插队
1.yield:线程的礼让
让出CPU,让其他线程执行,但礼让的时间不确定,所以也不一定成功
2.join:线程插入
插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务
public static void main(String[] args) throws InterruptedException { Mango mango = new Mango(); mango.start(); for (int i = 0; i < 10; i++) { Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+ "\t" + "吃猕猴桃..." + "\t" + i); if (i == 5){ System.out.println("主线程让子线程先执行完..."); mango.join();//插队处理:让子线程提前执行 //Thread.yield();//让子线程提前执行,但不一定成功 } } }
class Mango extends Thread{ int num = 0; @Override public void run() { while (true) { for (int i = 0; i < 10; i++) { num ++; try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "\t" + "吃芒果..." + "\t" + i); } if (num == 20){ break; } } } }
线程插队练习:
1.主线程每隔1s,输出hi,一共10次 2.当输出到hi 5时,启动一个子线程(要求 实现Runnable),每隔1s输出hello,等该线程输出10次hello后,退出 3.主线程继续输出hi,直到主线程退出.
public static void main(String[] args) throws InterruptedException { Almond almond = new Almond(); Thread thread = new Thread(almond); for (int i = 0; i < 10; i++) { Thread.sleep(1000); System.out.println("线程:" + Thread.currentThread().getName() + "\t" + "hi..."+ "\t" + i); if (i == 5){ System.out.println("允许子线程执行..."); thread.start();//开启子线程 thread.join();//将所有空间给子线程执行 } } }
//定义一个类并实现Runnable接口 class Almond implements Runnable{ int num = 0; @Override public void run() { while (true){ num++; try { Thread.sleep(1000); System.out.println("线程:" + Thread.currentThread().getName()+ "\t" + "hello..." + "\t" + num); } catch (InterruptedException e) { e.printStackTrace(); } if (num == 10){ break; } } } }
守护线程
用户线程和守护线程
1.用户线程:就是工作线程,当线程的任务执行完或通知方式结束
2.守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
3.常见的守护线程:垃圾回收机制
public static void main(String[] args) throws InterruptedException { Grape grape = new Grape(); //1.如果希望当main线程结束后,子线程自动结束 //2.只需要将子线程设为守护线程 grape.setDaemon(true);//将子线程设为守护线程 grape.start(); for (int i = 0; i < 10; i++) {//主线程 System.out.println("线程:" + Thread.currentThread().getName() + "\t" + "吃猕猴桃" + "\t" + i); Thread.sleep(1000); } }
class Grape extends Thread{ int num = 0; @Override public void run() { while (true) { num ++; try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "\t" + "吃葡萄..." + "\t" + num); } } }