1.什么是进程
-
进程就是一个在内存中独立运行的程序,有自己的地址空间。比如正在运行的QQ、微信
-
多任务:指操作系统能同时运行多个进程(程序),比如Windows系统能同时运行浏览器】写字板、画画、QQ等
2.什么是线程
-
线程是进程内部单一的一个顺序控制流
-
一个进程可以拥有多个线程,一个进程至少要有一个线程
3.创建线程
-
虚拟的CPU:有java.lang.Thread类封装和虚拟
-
CPU执行代码:传递给Thread对象
-
CPU处理的数据:传递给Thread对象
-
java的线程是通过java.lang.Thread类来实现的
-
每个线程都是通过某个特定的Thread对象所对应的run()方法来完成其操作的,方法run()称为线程体
-
创建线程的两种方式
-
继承Thread类
package com.jxd.edu; /** * @author Ming WanBao * 1.定义类继承Thread类 * 2.重写父类的run()方法完成具体的操作 * 3.创建子类对象,就是创建线程对象 * 4.调用线程对象的start()方法来启动线程(告诉jvm调用run()方法) */ public class ThreadDemo extends Thread { private String name; public ThreadDemo(String name) { this.name = name; } public ThreadDemo() { } /** * 重写run(),在方法体中添加你想要在该线程中执行的代码 */ @Override public void run() { for (int i = 0; i < 500; i++) { System.out.println(name + "运行:" + i); } } } package com.jxd.edu; /** * @author Ming WanBao */ public class TestThreadDemo { public static void main(String[] args) { //创建线程对象 ThreadDemo td1 = new ThreadDemo("A"); ThreadDemo td2 = new ThreadDemo("B"); //启动线程 td1.start(); td2.start(); /* 结果分析: 》在任何java程序启动时,一个线程立刻运行,即main方法对应的线程, 该线程通常称为程序的主线程,随着start()方法的调用,另外两个线程也启动, 这样,整个应用就在多线程下运行了 》从程序的运行结果可以发现,多线程程序是乱序执行,谁先抢占到CPU的资源谁先执行 》java.lang.Thread类包含了创建和运行线程所需的一切东西 》主线程的特点: 》它是产生其他子线程的线程 》它不一定是最后执行完成的线程,子线程可能在它结束之后还在运行 */ } }
-
实现Runnable接口
package com.jxd.edu; /** * @author Ming WanBao * 1.创建类实现RunnableDemo接口 * 2.重写run() * 3.创建Thread类的对象,将Runnable接口的实现类对象作为参数传递给Thread类的构造函数 * 4.调用Thread类的start()方法 */ public class RunnableDemo implements Runnable{ private String name; public RunnableDemo() { } public RunnableDemo(String name) { this.name = name; } @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println(name + "运行:" + i); } } } package com.jxd.edu; /** * @author Ming WanBao * 实现了Runnable接口的类并不具备任何天生的线程处理能力, * 这与那些继承了Thread类的子类是不同的 * 为了从一个Runnable对象产生线程,必须再单独创建一个线程对象,把Runnable对象作为参数传递给它 */ public class TestRunnableDemo { public static void main(String[] args) { RunnableDemo r1 = new RunnableDemo("A"); RunnableDemo r2 = new RunnableDemo("B"); Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); t2.start(); /* 两种创建线程方式的比较 》继承Thread类 》优点:Thread类的子类对象就是线程对象,具有Thread类的方法,且具有线程体 》缺点:不适应多继承,因为java是单继承的语言,继承了Thread类就无法继承其他累了 》实现Runnable接口 》如果一个类已经继承了其他类还必须要以线程方式运行,就需要实现Runnable接口, 实现Runnable接口的类可以避免java中单继承的限制 */ } }
-
4.join()方法和线程优先级
-
在线程中插入另一个线程,该线程被阻塞,直到插入的线程执行完毕以后,该线程才继续执行下去
package com.jxd.edu; /** * @author Ming WanBao */ public class TestJoin { public static void main(String[] args) { RunnableDemo rd = new RunnableDemo("A"); Thread thread = new Thread(rd); //设置子线程的优先级为10 //thread.setPriority(Thread.MAX_PRIORITY); thread.start(); try { //join的作用是让父线程等待子线程运行结束之后再运行 thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 1000; i++) { System.out.println("主线程:" + i); } /*try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }*/ /*System.out.println("子线程的优先级为" + thread.getPriority()); System.out.println("主线程的优先级为" + Thread.currentThread().getPriority());*/ } }
-
为什么要用join
-
在很多情况下,主线程产生并启动了子线程,如果子线程中要进行大量的耗时的计算,主线程往往优先于子线程结束,但是如果主线程中需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再执行,这个时候就需要用到join
-
-
线程优先级:
-
在java中,每个线程都有一个优先级,默认情况下,一个线程将继承其父线程的优先级。
-
线程优先级用数字表示,范围从1-10,一个线程的缺省优先级是5
-
Thread.MIN_PRIORITY ---------1
-
Thread.NORM_PRIORITY ---------5
-
Thread.MAX_PRIORITY ---------10
-
-
不是高优先级的线程一定比低优先级的线程先执行完,只是高优先级线程先执行的概率比低优先级线程高
-
设置优先级:void setPriority(int newPriority);
-
获取优先级:int getPriority();
-
5.sleep()
-
sleep()是指在指定的毫秒数内让当前正在执行的线程休眠(暂停运行)
package com.jxd.edu; /** * @author Ming WanBao */ public class TestSleep { public static void main(String[] args) { ThreadDemo td1 = new ThreadDemo("A"); td1.start(); try { //让主线程休眠一段时间后重新获取CPU的使用权 Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 1000; i++) { System.out.println("主线程:" + i); } } }
6.yield
-
主动释放当前线程的执行权
package com.jxd.edu; /** * @author Ming WanBao */ public class YieldDemo extends Thread { private String name; public YieldDemo(String name) { this.name = name; } @Override public void run() { for (int i = 0; i < 50; i++) { System.out.println(name + "执行:" + i); if (i == 30) { //当i为30时,该线程会把CPU让出来,让其他线程或自己的线程执行 // (也就是谁抢占到CPU谁执行) Thread.yield(); } } } } package com.jxd.edu; /** * @author Ming WanBao */ public class TestYieldDemo { public static void main(String[] args) { Thread t1 = new YieldDemo("A"); Thread t2 = new YieldDemo("B"); t1.start(); t2.start(); } }
7.对象的监视锁
-
在java中,每个对象都包含了一把锁(也叫监视器),他自动成为对象的一部分,在给定时刻,只有一个线程可以拥有一个对象的锁
8.同步
-
我们知道,在同一时间只能有一个线程在CPU上运行,线程之间的调度是通过分时和抢占来完成的
-
为了确保在任何时刻一个共享对象只被一个线程使用,必须使用同步
-
synchronized关键字声明的方法就是同步方法,声明的代码块就是同步块
-
同步方法
-
synchronized public void setName(){}
-
-
同步块
-
synchronized(obj) {}
-
obj是被锁定的对象
-
-
作用:
-
任何时刻,都最多只能有一个线程调用此方法,若有多个线程请求调用此方法,只能排队
-
9.wait()和notify()
-
wait():当前线程被中断,并进入到一个对象的等待列表中,直到另外的线程调用同一个对象上的notify()或notifyAll()方法
-
notify():用于将对象等待列表中任意一个线程唤醒,使它再次成为可运行的线程
-
notifyAll():用于将对象等待列表中的所有线程唤醒,使它们再次成为可运行的线程
10.线程的生命周期
-
新建状态(new):当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
-
就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法,该线程位于可运行线程池中,变得可运行,等待获取CPU的使用权
-
运行状态(Running):就绪状态的线程获得了CPU使用权,执行程序代码
-
阻塞状态(Blocked):线程因为某种原因放弃了CPU的使用权,暂时停止运行,直到线程进入就绪状态才有机会转到运行状态。sleep() join() yield() wait()
-
死亡状态(dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期