并行:在同一时刻,有多个指令在多个cpu上同时执行。
并发:在同一时刻,有多个指令在单个cpu上交替执行。
进程:正在运行的程序。
独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位。
动态性:进程的实质是程序的一次执行过程,进程是动态产生,动态消亡的。
并发性:任何进程都可以同其他进程一起并发执行。
线程:是进程中单个顺序的控制流,是一条执行路径。
单线程:一个进程只有一条执行路径,则称为单线程程序。
多线程:一个进程如果有多条执行路径,则称为多线程程序。
(1)介绍thread类
thread extends Object
thread implements Runnable
Thread的构造方法
Tread(Runnable target)
Tread(Runnable target,String name)
(2)具体步骤:创建一个类去继承Thread类,这个子类去重写Thread中的run()方法,然后分配并启动子类的实例。
public class Demo1 extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(i); } } } public class Test01 { public static void main(String[] args) { Demo1 d1 =new Demo1(); Demo1 d2 =new Demo1(); d1.start(); d2.start(); } }
☆为什么要重写run()方法?
因为run()是用来封装被线程执行的代码。
☆run()方法和start()方法的区别?
run():封装线程执行的代码,直接调用,相当于普通方法的调用。
start():启动线程,然后由jvm调用此线程的run()方法。
(1)具体步骤:声明一个实现Runnable接口的类,那个类然后实现run方法,然后可以分配类的实例,在创建Thread对象时作为参数传递,并启动。
(2)Thread的构造方法
Tread(Runnable target)
Tread(Runnable target,String name)
public class Demo2 implements Runnable{ @Override public void run() { for (int i = 0; i < 10; i++) { //不能直接使用getName方法,因为Demo2这个类是实现Runnable这个接口。 //并没有继承Thread这个方法。 //可以通过拿到当前执行的线程。 System.out.println(Thread.currentThread().getName()+":"+i); } } } public class Test2 { public static void main(String[] args) { Demo2 d1 =new Demo2(); Thread t1 = new Thread(d1,"火车"); Thread t2 = new Thread(d1,"汽车"); t1.start(); t2.start(); } }
(1)具体步骤:首先敲写一个实现Callable接口的类。在main方法内new一个这个类的对象,然后把生成的这个实例对象注册到FutureTask类中,然后将FutureTask类的实例注册进入Thread中运行。最后可以采用FutureTask<V>中的get方法获取自定义线程的返回值。
(2)原理:我们可以查看Callable接口的源码,发现只有一个call()方法的定义.FutureTask<V>实现了RunnableFuture<V>的接口,这个RunnableFuture<V>接口也实现了,Runnable这个接口。FutureTask<V>中还有一个需要传入Callable<V>类型参数的构造方法,且会赋值给FutureTask<V>的私有属性。无论我们以怎样的形式去实现多线程,都需要调用Tread类中的Start()方法去向操作系统请求io和cpu等资源,Tread有参构造中需传入一个Runnable类型的接口参数,而FutureTask<V>的实例正可以充当这个参数传入Tread。
ps:get()方法是FutureTask<V>类中的方法,运用可能需要抛异常。
public class Demo3 implements Callable { @Override public Object call() throws Exception { for (int i = 0; i <10 ; i++) { System.out.println("多线程方式3 "+i); } return "结束了"; } } public class Test03 { public static void main(String[] args) throws ExecutionException, InterruptedException { Demo3 d =new Demo3(); FutureTask<String> ft =new FutureTask<>(d); Thread t1 =new Thread(ft); t1.start(); String s = ft.get(); System.out.println(s); } }
Tread类中提供了两个方法用来设置和获取线程名称
void setName(String name) 将此线程的名称更改为等于参数的name
String getName() 返回此线程的名称
Thread currentThread() 返回对当前正在执行的线程对象
ps:如果在Demo4类中输出中调用了Tread类中的getName方法而在Test04中并没有命名,则系统会按照自己的源码给一个名字,例如“Demo-0:0或 Demo-1 :0”等等。
public class Demo4 extends Thread{ public Demo4() { } public Demo4(String name) { super(name); } @Override public void run() { for (int i= 0; i <5; i++) { System.out.println(getName()+":"+i); } } } public class Test04 { public static void main(String[] args) { //命名方法一:调用有参构造 Demo4 d1 =new Demo4("飞机"); Demo4 d2 =new Demo4("高铁"); /*命名方法二:调用无参构造 Demo4 d1 =new Demo4(); Demo4 d2 =new Demo4(); d1.setName("飞机"); d2.setName("高铁"); */ d1.start(); d2.start(); } }
static void sleep(long millis) 使当前正在执行的线程停留(暂停执行)指定的毫秒数。
void join() 等待这个线程死亡
Void setDaemon(boolean on) 将此线程标记为守护线程,当运行的线程都是守护线程时,
Java虚拟机将退出
5.1线程休眠
static void sleep(long millis) 使当前正在执行的线程停留(暂停执行)指定的毫秒数。
public class Demo5 extends Thread{ 运行结果 public Demo5() { 刘备+0 } 孙权+0 public Demo5(String name) { 曹操+0 super(name); 孙权+1 } 刘备+1 @Override 曹操+1 public void run() { 孙权+2 for (int i = 0; i < 5; i++) { 曹操+2 /*Tread表示的是当前运行的线程停留1000毫秒。*/ 刘备+2 try { * Thread.sleep(1000); * } catch (InterruptedException e) { * e.printStackTrace(); } System.out.println(getName()+"+"+i); } } } public class Test5 { public static void main(String[] args) { Demo5 d1 =new Demo5("曹操"); Demo5 d2 =new Demo5("刘备"); Demo5 d3 =new Demo5("孙权"); d1.start(); d2.start(); d3.start(); } }
总结:这三个人在执行的时候也在抢cpu的使用权。
5.2 线程死亡
void join() 只有该线程执行完之后,才会执行其他线程。
public class Demo5 extends Thread{ public Demo5() { } public Demo5(String name) { super(name); } @Override public void run() { for (int i= 0; i <5; i++) { System.out.println(getName()+":"+i); } } } public class Test5 { public static void main(String[] args) throws InterruptedException { Demo5 d1 =new Demo5("皇上"); Demo5 d2 =new Demo5("大阿哥"); Demo5 d3 =new Demo5("二阿哥"); d1.start(); d1.join(); //只有皇上执行完之后,两位阿哥才有机会去执行。 d2.start(); d3.start(); } }
5.3守护线程
Void setDaemon(boolean on) 将此线程标记为守护线程,当运行的线程都是守护线程时,
Java虚拟机将退出(不是立即退出,会挣扎一会)
public class Demo5 extends Thread{ public Demo5() { } public Demo5(String name) { super(name); } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName()+"+"+i); } } } public class Test5 { public static void main(String[] args) { Demo5 d1 =new Demo5("皇上"); Demo5 d2 =new Demo5("大阿哥"); Demo5 d3 =new Demo5("二阿哥"); d3.setDaemon(true); d2.setDaemon(true); d1.start(); d2.start(); d3.start(); } }
6.1线程调度方式:
(1)分时调度模型:所有线程轮流使用cpu的使用权,平均分配每个线程占用cpu的时间片
(2)抢占式调度模型:优先让优先级高的线程使用cpu,如果线程的优先级相同,那么会随机选一个,优先级高的线程获取的cpu时间片相对多一些。
Java使用的是抢占式调度模型。
6.2Tread类中获取和设置优先级的方法
ps:线程的优先级高仅仅表示线程获取的cpu时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到你想要的效果。
final int getPriority() 返回此线程的优先级
final void setPriority(int newPriority) 更改此线程的优先级线程默认优先级是5;线程优先级的范围是:1-10
public class Demo6 implements Callable { @Override public Object call() throws Exception { for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } return "程序完毕"; } } public class Test06 { public static void main(String[] args) throws ExecutionException, InterruptedException { Demo6 d1 =new Demo6(); FutureTask<String> ft =new FutureTask<>(d1); Thread t1 =new Thread(ft); t1.setName("飞机"); t1.setPriority(9); t1.start(); Demo6 d2 =new Demo6(); FutureTask<String> ft2 =new FutureTask<>(d2); Thread t2 =new Thread(ft2); t2.setName("高铁"); t2.setPriority(1); t2.start(); System.out.println(t1.getPriority()); String s = ft2.get(); System.out.println(s); } }
(1)实现Runnable,Callable接口的方式
好处:拓展性强,实现该接口的同时还可以继承其他的类,而且Callable接口的call方法还会有返回值
缺点:代码复杂,不能直接使用Thread类中的方法
(2)继承Thread类
好处:代码简单,可以直接使用Thread类中的方法
缺点:拓展性较差,不能再继承其他的类。