多线程:
进程:进程指正在运行的程序;确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能(进入内存运行的程序成为进程)!
线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程!一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序(线程是执行单元,一个进程可以包括多个线程,一个程序可以有多个进程)!
单线程程序:若有多个任务只能依次执行(这个任务执行完毕,下一个任务开始执行)!如:去网吧上网,网吧只能让一个人上网,当这个人下机后,下一个人才能上网(这个任务进站执行完毕,弹栈之后,下一个任务才可以执行)!
多线程程序:若有多个任务可以同时执行!如:去网吧上网,网吧能够让多个人同时上网(一个线程建一个栈,多个栈内的任务同时执行,Main方法也是一个栈)!
//八核十六线程的CPU:一个核心有16个线程!
程序运行原理:
分时调度:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间(比如360安全卫士有3个功能:杀毒,电脑清理,系统修复,这三个功能会被JVM分配1微秒的时间片,轮流执行1微秒,看起来像是同时执行,但实际并不是)!
抢占式调度:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(称为线程的随机性),Java使用的为抢占式调度(Java采用抢占式调度)!
//可以通过Windwos系统的任务管理器(Ctrl键+Shift键+Esc键调出)设置进程的优先级:
实际上,CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换(分配时间片)!对于CPU的一个核而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行,其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高(单核单线程只能这个任务执行完毕再执行下一个任务,现在多核多线程可以多个任务同时执行,节省时间)!
主线程:Main方法就是主线程!
JVM启动后必然有一个执行路径(线程)从main方法开始的,一直执行到main方法结束,这个线程在Java中称之为主线程!当程序的主线程执行时,如果遇到了循环而导致程序在指定位置停留时间过长,则无法马上执行下面的程序,需要等待循环结束后能够执行!
Thread类:
//第2个构造方法的String name是用来指定线程的名称!
常用方法:
//start()方法会调用继承自Thread类的子类重写之后的run()方法(如果直接使用Thread子类的对象调用重写后的run()方法,不会开启线程,就是普通的对象调用方法,所以想要开启线程用start()方法)!
//sleep()方法:让该线程休眠,即结束进程!
强调:
Thread t1 = new Thread();
t1.start();
//这样做没有错,但是该start()调用的是Thread类中的run()方法,而这个run()方法没有做什么事情,更重要的是这个run方法中并没有定义我们需要让线程执行的代码!
每一个线程都有一个独立的内存中的栈空间,所以可以实现并发执行(又要牵扯到管理高并发的问题):
当执行线程的任务结束了,线程自动在栈内存中释放了,但是当所有的执行线程都结束了,那么进程就结束了!
//获取当前执行的线程的对象或名称:
//由于这个方法是静态的,所以可以类名.方法调用!
课堂示例代码:
package com.oracle.duoxiancheng; public class Demo01 { public static void main(String[] args) { //thread新建的线程,和Main方法都是线程,Main方法是主线程,在内存中享有独立的栈空间: //新建第一个线程: MyThread mt1=new MyThread(); //开启该线程(调用run()方法): mt1.start(); //新建第二一个线程: MyThread mt2=new MyThread(); //开启该线程(调用run()方法): mt2.start(); for(int i=0;i<1000;++i){ System.out.println("main……"+i); } System.out.println("程序结束了!"); //查看main方法线程名字: System.out.println(Thread.currentThread().getName()); } } package com.oracle.duoxiancheng; public class MyThread extends Thread { public void run() { System.out.println(super.getName()); //重写run方法,把并发执行的代码放在里面,run方法不需要程序员调用,线程对象的.start()方法: for(int j=0;j<1000;++j){ System.out.println("run……"+j); } } }
创建线程方式—实现Runnable接口:
创建线程的另一种方法是声明实现 Runnable 接口的类!该类然后实现 run 方法,然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程!
//好处:把任务体(Runnable中的run()方法)跟创建线程和开启线程(Thread和start()方法)分离开了!
Thread类的构造方法:
//可以传入一个Runnable对象,以及一个线程名字!
创建线程的步骤:
1;定义类实现Runnable接口!
2;覆盖接口中的run方法!
3;创建Thread类的对象!
4;将Runnable接口的子类对象作为参数传递给Thread类的构造函数!
5;调用Thread类的start()方法开启线程!
通过Runnable和Thread方法 来实现创建开启线程的好处:
① 实现Runnable接口,避免了继承Thread类的单继承局限性!覆盖Runnable接口中的run方法,将线程任务代码定义到run方法中!
② 更加符合面向对象思想,实现了线程任务和线程对象的解耦!
//下方TIMED_WAITING应该是WAITING,其中Wait()和Notify()是属于Object类!
示例代码:
package com.oracle.duoxiancheng; public class Demo01 { public static void main(String[] args) { //thread新建的线程,和Main方法都是线程,Main方法是主线程,在内存中享有独立的栈空间: //新建第一个线程,设置线程名称方法②,MyThread重写了Thread的构造方法: MyThread mt1=new MyThread("诸葛鬼才"); //开启该线程(调用run()方法): mt1.start(); //新建第二一个线程: MyThread mt2=new MyThread(); //设置线程名称方法①: mt2.setName("蔡文姬"); //开启该线程(调用run()方法): mt2.start(); /*for(int i=0;i<1000;++i){ System.out.println("main……"+i); }*/ System.out.println("程序结束了!"); //查看main方法线程名字: System.out.println(Thread.currentThread().getName()); } } package com.oracle.duoxiancheng; public class MyThread extends Thread { public MyThread(String getName){ super(getName); } public MyThread(){ } public void run() { System.out.println(super.getName()); //重写run方法,把并发执行的代码放在里面,run方法不需要程序员调用,线程对象的.start()方法: /*for(int j=0;j<1000;++j){ System.out.println("run……"+j); }*/ } } 通过Runnable接口和Thread对象开启线程: package com.oracle.duoxiancheng; public class Demo02 { public static void main(String[] args) throws InterruptedException { //runnable实现类任务的编写: MyRunnable mr=new MyRunnable(); //Thread负责创建线程并开启线程 Thread th1=new Thread(mr); th1.start(); for(int i=0;i<50;++i){ //休眠可以自己醒: Thread.sleep(5000); System.out.println("main……"+i); } } } package com.oracle.duoxiancheng; public class MyRunnable implements Runnable { public void run(){ for(int i=0;i<50;++i){ try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("这个是run()方法内的……"+i); } } } 匿名对象创建并开启线程: package com.oracle.duoxiancheng; public class Demo03 { public static void main(String[] args) { //匿名内部类,下面的new Thread是Thread的子类,只不过没有名字: new Thread(){ public void run(){ for(int i=0;i<50;++i){ System.out.println("Run……"+i); } } }.start(); //匿名内部类,匿名new Thread对象里面放一个匿名内部类(Runnable的子类): new Thread(new Runnable(){ public void run(){ for(int i=0;i<50;++i){ System.out.println("Run……"+i); } } }).start(); //匿名内部类,new Runnable(){}部分是Runable的子类,前面的Runnable ra=是多态,传入Thread子类对象的构造方法: Runnable ra=new Runnable(){ public void run(){ for(int i=0;i<50;++i){ System.out.println("Run……"+i); } } }; Thread ta=new Thread(ra); } }