基本概念
- 进程
- 线程
- 多线程的实现
- 多线程原理
- 继承Thread类来创建线程
- 启动多线程
- 通过Runnable接口来创建线程
- 利用Thread类启动多线程
- 线程状态
- 多线程常用操作方法
- 线程的命名与取得
- 线程休眠
- 线程优先级
- 线程同步与死锁
- 线程同步
- 死锁
- 线程的生命周期
进程
➢进程是程序的一次动态执行过程,它经历了从代码加载、执行到执行完毕的一个完整过程,这个过程也是进程本身从产生、发展到最终消亡的过程。多进程操作系统能同时运行多个进程(程序),由于CPU具备分时机制,所以每个进程都能循环获得自己的CPU时间片。由于CPU执行速度非常快,使得所有程序好象是在“同时”运行一样。
线程
➢多线程是实现并发机制的一种有效手段。进程和线程一样,都是实现并发的一个基本单位。线程是比进程更小的执行单位,线程是在进程的基础之上进行的进一步划分。 所谓多线程是指一个进程在执行过程中可以产生多个线程, 这些线程可以同时存在、同时运行, 一个进程可能包含了多个同时执行的线程。
多线程的实现
➢在Java之中,如果要想实现多线程的程序,那么就必须依靠一个线程的主体类(就好比主类的概念一样,表示的是一个线程的主类) , 但是这个线程的主体类在定义的时候也需要有一些特殊的要求, 此类需要继承Thread类或实现Runnable (Callable) 接口来完成定义。
多线程原理
多线程原理:相当于玩游戏机,只有一个游戏机(cpu),可是有很多人要玩,于是,start 是排队!等 CPU 选中你就是轮到你,你就 run(),当 CPU 的运行的时间片执行完,这个线程就继续排队,等待下一次的run()。
调用 start() 后,线程会被放到等待队列,等待 CPU 调度,并不一定要马上开始执行,只是将这个线程置于可动行状态。然后通过 JVM,线程 Thread 会调用 run() 方法,执行本线程的线程体。先调用 start 后调用 run,这么麻烦,为了不直接调用 run?就是为了实现多线程的优点,没这个 start 不行。
1.start() 方法来启动线程,真正实现了多线程运行。这时无需等待 run 方法体代码执行完毕,可以直接继续执行下面的代码;通过调用 Thread 类的 start() 方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。 然后通过此 Thread 类调用方法 run() 来完成其运行操作的, 这里方法 run() 称为线程体,它包含了要执行的这个线程的内容, run 方法运行结束, 此线程终止。然后 CPU 再调度其它线程。
2.run() 方法当作普通方法的方式调用。程序还是要顺序执行,要等待 run 方法体执行完毕后,才可继续执行下面的代码; 程序中只有主线程——这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的。
记住:多线程就是分时利用 CPU,宏观上让所有线程一起执行 ,也叫并发。
继承Thread类来创建线程
➢Java 的线程是通过 java.lang.Thread 类来实现的。VM 启动时会有一个由主方法所定义的线程。可以通过创建 Thread 的实例来创建新的线程。每个线程都是通过某个特定 Thread 对象所对应的方法 run() 来完成其操作的,方法 run() 称为线程体。通过调用 Thread 类的 start() 方法来启动一个线程。
public class MyThread extends Thread{
private String name;
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {
for(int x = 0; x < 10; x++){
System.out.println(this.name + "-->" + x);
}
}
}
启动多线程
public class Demo {
public static void main(String[] args) {
MyThread a = new MyThread("线程A");
MyThread b = new MyThread("线程B");
MyThread c = new MyThread("线程C");
a.start();
b.start();
c.start();
}
}
结果:线程B-->0
线程B-->1
线程B-->2
线程A-->0
线程C-->0
线程A-->1
线程A-->2
线程C-->1
线程C-->2
通过Runnable接口来创建线程
public class MyThread implements Runnable{
private String name;
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {
for(int x = 0; x < 3; x++){
System.out.println(this.name + "-->" + x);
}
}
}
利用Thread类启动多线程
public class Demo {
public static void main(String[] args) {
MyThread a = new MyThread("线程A");
MyThread b = new MyThread("线程B");
MyThread c = new MyThread("线程C");
new Thread(a).start();
new Thread(b).start();
new Thread(c).start();
}
}
线程状态
线程转换状态
创建状态
在程序中用构造方法创建了一个线程对象后, 新的线程对象便处于新建状态,此时,它已经有了相应的内存空间和其它资源,但还处于不可运行状态。新建一个线程对象可采用Thread类的构造方法来实现, 例如: Thread thread=new Thread();
就绪状态
新建线程对象后,调用该线程的start()方法就可以启动线程。当线程启动时,线程进入就绪状态。此时,线程将进入线程队列排队,等待CPU服务, 这表明它已经备了运行条件。
运行状态
当就绪状态的线程 被调用并获得处理器资源时,线程就进入了运行状态。此时,自动调用该线程对象的run()方法。run()方法定 义了该线程的操作和功能。
堵塞状态
一个正在执行的线程在某些特殊情况下, 如被人为挂起或需要执行耗时的输入输出操作时,将让出CPU并暂时中止自己的执行,进入堵塞状态。在可执行状态下,如果调用sleep()、suspend()、 wait()等方法, 线程都将进入堵塞状态。堵塞时,线程不能进入排队队列,只有当引|起堵塞的原因被消除后,线程才可以转入就绪状态。
终止状态
线程调用stop()方法时或run()方法执行结束后,线程即处于终止状态。处于终止状态的线程不具有继续运行的能力。
多线程常用操作方法
线程的命名与取得
由于多线程的状态不确定,所以线程的名字就成为了唯一的分辨标识。取得当前线程对象的方法为:public static Thread currentThread()
线程名称操作
NO. | 方法 | 类型 | 描述 |
1 | public Thread(Runnable target, String name) | 构造 | 实例化线程对象,接收Runnable 接口子类对象,同时设置线程名称 |
2 | pubilc final void setName(String name) | 普通 | 设置线程名称 |
3 | public final void getName() | 普通 | 取得线程名称 |
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
public class App {
public static void main( String[] args ) {
MyThread mt = new MyThread();
new Thread(mt,"线程A").start();
new Thread(mt,"线程B").start();
}
}
结果:线程A
线程B
线程休眠
线程的休眠指的是让程序的执行速度变慢一些。
方法为:public static native void sleep(long millis) throws InterruptedException;单位是毫秒
class MyThread implements Runnable{
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
public class App {
public static void main( String[] args ) {
MyThread mt = new MyThread();
new Thread(mt,"线程A").start();
}
}
结果:线程A
线程优先级
在java的线程操作中,哪个线程的优先级高,哪个线程就有可能会先被执行。
线程优先级操作
NO. | 方法或常量 | 类型 | 描述 |
1 | public final static int MAX_PRIORITY | 常量 | 最高优先级,值为10 |
2 | public final static int NORM_PRIORITY | 常量 | 中等优先级,值为5 |
3 | public final static int MIN_PRIORITY | 常量 | 最低优先级,值为1 |
4 | public final void setPriority(int newPriority) | 普通 | 设置线程优先级 |
5 | public final int getPriority() | 普通 | 取得线程优先级 |
class MyThread implements Runnable{
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
public class App {
public static void main( String[] args ) {
MyThread mt = new MyThread();
Thread t1 = new Thread(mt,"线程A");
Thread t2 = new Thread(mt,"线程B");
t2.setPriority(Thread.MAX_PRIORITY);
t1.start();
t2.start();
}
线程同步与死锁
➢同步是多线程开发中的一个重要概念, 但是既然有同步,那么就一定会存在有不同步的操作。所以本节将为读者分析线程不同步所带来的影响。
➢多个线程操作同一资源时就有可能出现不同步的问题,例如,现在产生N个线程对象实现卖票操作,同时为了更加明显的观察到不同步所带来的问题,所以本程序将使用线程的休眠操作。
线程同步
同步问题的引出
class MyThread extends Thread{
private int ticket = 5;
private String name;
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {
for(int x = 0; x < 50; x++){
if(ticket > 0){
System.out.println(this.name+ "卖票,ticket = " + ticket--);
}
}
}
}
public class Demo {
public static void main(String[] args) {
MyThread a = new MyThread("线程A");
MyThread b = new MyThread("线程B");
MyThread c = new MyThread("线程C");
a.start();
b.start();
c.start();
}
}
结果:线程A卖票,ticket = 5
线程A卖票,ticket = 4
线程A卖票,ticket = 3
线程A卖票,ticket = 2
线程A卖票,ticket = 1
线程C卖票,ticket = 5
线程C卖票,ticket = 4
线程C卖票,ticket = 3
线程B卖票,ticket = 5
线程B卖票,ticket = 4
线程B卖票,ticket = 3
线程B卖票,ticket = 2
线程B卖票,ticket = 1
线程C卖票,ticket = 2
线程C卖票,ticket = 1
➢如果想解决以上程序的问题,就必须使用同步,所谓的同步就是一个代码块中的多 个操作在同一个时间段内只能有一个线程进行, 其他线程要等待此线程完成之后才可以继续执行
使用同步方法解决问题
class MyThread implements Runnable{
private int ticket = 5;
@Override
public void run() {
for(int x = 0; x < 20; x++){
sale();
}
}
public synchronized void sale(){
if(ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票,ticket = " + ticket--);
}
}
}
public class App {
public static void main( String[] args ) {
MyThread mt = new MyThread();
Thread t1 = new Thread(mt,"线程A");
Thread t2 = new Thread(mt,"线程B");
t1.start();
t2.start();
}
}
结果:线程A卖票,ticket = 5
线程A卖票,ticket = 4
线程A卖票,ticket = 3
线程A卖票,ticket = 2
线程B卖票,ticket = 1
死锁
➢同步就是指一个线程要等待另外一个线程执行完毕才会继续执行的一种操作形式,虽然在一个程序中,使用同步可以保证资源共享操作的正确性,但是过多同步也会产生问题。例如:现在有张三想要李四的画,李四想要张三的书,那么张三对李四说了: “把你的画给我,我就给你书”,李四也对张三说了: “把你的书给我,我就给你画”,此时,张三在等着李四的答复,而李四也在等着张三的答复,那么这样下去最终结果可想而知,张三得不到李四的画, 李四也得不到张三的书,这实际上就是死锁的概念
线程的生命周期
在java中,一个线程对象也有自己的生命周期,如果要控制好线程的生命周期,首先应该认识生命周期。
停止线程运行
class MyThread implements Runnable{
private boolean flag = true;
@Override
public void run() {
while (this.flag){
System.out.println(Thread.currentThread().getName());
}
}
public void stop(){
this.flag = false;
}
}
public class App {
public static void main( String[] args ) {
MyThread mt = new MyThread();
Thread t1 = new Thread(mt,"线程A");
t1.start();
mt.stop();
}
}