黑马程序员——java基础——多线程

时间:2022-10-08 00:43:33

-----------android培训java培训、java学习型技术博客、期待与您交流!------------

 

1、对线程的理解

进程:每个独立执行的程序成为进程。

线程:线程就是进程内部的一条执行路径。

多线程:在一个进程中同时有多条执行路径。

线程和进程的区别如下:

(1) 每个进程都有独立的代码和数据空间(进程上下文),进程间的切换开销大。

(2) 同一进程内的多个线程共享相同的代码和数据空间,每个线程有独立运行栈   和线序计数器,线程间的切换开销小。

2、线程的创建和启动

实现线程的创建有两种方法:

(1) 实现Runnable接口

(2) 继承Thread

启动方法:调用Thread实例的方法start()

示例:FirstThreadTest.java

 

/** 创建和启动多个线程 */

public class FirstThreadTest {

 

public static void main(String[] args) {

System.out.println("主线程开始执行");

 

Thread thread1 = new Thread(new MyRunner());

//启动第一个线程

thread1.start();

System.out.println("启动一个新线程(thread1)...");

 

Thread thread2 = new MyTread();

//启动第二个线程

thread2.start();

System.out.println("启动一个新线程(thread2)...");

 

System.out.println("主线程执行完毕");

}

}

  

 

 

/** 实现自java.lang.Runnable来创建一个打算用线程来执行的类 */

public class MyRunner implements Runnable {

 

public void run() {

// 要在线程中执行的代码

for (int i = 0; i < 100; i++) {

System.out.println("MyRunner:" + i);

}

}

}

  

执行结果:

主线程开始执行

启动一个新线程(thread1)...

MyRunner:0

MyRunner:1

MyRunner:2

MyRunner:3

启动一个新线程(thread2)...

主线程执行完毕

MyRunner:4

MyRunner:5

MyRunner:6

MyRunner:7

MyThread:0

MyRunner:8

......

MyThread:94

MyThread:95

MyThread:96

MyThread:97

MyThread:98

MyThread:99

  

主线程中创建了两个子线程,这两个子线程会交替的执行,这就是多线程的程序。

3、线程的生命周期:

 

 

当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。
例如:Thread  t1=new Thread();

就绪(runnable
线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源。例如:t1.start();

运行(running
线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。

死亡(dead
当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。

自然终止:正常运行run()方法后终止

异常终止:调用stop()方法让一个线程终止运行

堵塞(blocked
由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。

正在睡眠:用sleep(long t) 方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。

正在等待:调用wait()方法。(调用motify()方法回到就绪状态)

被另一个线程所阻塞:调用suspend()方法。(调用resume()方法恢复)

线程睡眠示例:ThreadSleepTest

 

/** 线程睡眠示例 */

public class ThreadSleepTest {

 

public static void main(String[] args) {

System.out.println("主线程开始执行");

 

Thread thread1 = new Thread(new SleepRunner());

thread1.start();

System.out.println("启动一个新线程(thread1)...");

 

Thread thread2 = new Thread(new NormalRunner());

thread2.start();

System.out.println("启动一个新线程(thread2)...");

 

System.out.println("主线程执行完毕");

}

}

 

class SleepRunner implements Runnable{

public void run() {

try {

Thread.sleep(100);  //线程睡眠100毫秒

} catch (InterruptedException e) {

e.printStackTrace();

}

 

// 要在线程中执行的代码

for (int i = 0; i < 100; i++) {

System.out.println("SleepRunner:" + i);

}

}

}

 

class NormalRunner implements Runnable{

public void run() {

// 要在线程中执行的代码

for (int i = 0; i < 100; i++) {

System.out.println("NormalRunner:" + i);

}

}

}

  

执行结果:

.........

NormalRunner:96

NormalRunner:97

NormalRunner:98

NormalRunner:99

SleepRunner:0

SleepRunner:1

SleepRunner:2

SleepRunner:3

SleepRunner:4

SleepRunner:5

.......

  

轮到SleepRunner时,使它休眠,先执行NormalRunner,等SleepRunner休眠结束时,NormaRunner已经执行完毕。

 

 

线程让步示例:ThreadYieldTest

 

/** 线程让步示例 */

public class ThreadYieldTest {

 

public static void main(String[] args) {

//获取当前线程的名称

        System.out.println(Thread.currentThread().getName());

        Thread thread1 = new Thread(new YieldThread());

        thread1.start();

        Thread thread2 = new Thread(new YieldThread());

        thread2.start();

    }

}

 

class YieldThread implements Runnable{

    public void run() {

        for(int i = 0; i < 100; i++){

            System.out.println(Thread.currentThread().getName()+ ":" + i);

            if(i % 10 == 0){    //当i可以被10整除时,当前线程让步给其它线程

            	Thread.yield(); //线程让步的方法

            }

        }

    }

}

  

执行结果:

Thread-1:96

Thread-1:97

Thread-1:98

Thread-1:99

Thread-0:76

Thread-0:77

Thread-0:78

Thread-0:79

  

每到条件达成时都会做出线程让步的动作

 

 

线程的加入示例:ThreadJoinTest

 

/** 线程合并操作 */

public class ThreadJoinTest {

 

public static void main(String[] args) {

        Thread thread1 = new Thread(new MyThread3());

        thread1.start();

        //主线程中执行for循环

        for (int i = 1; i <= 50; i++) {

            System.out.println(Thread.currentThread().getName() + ":" + i);

            if (i == 30) {

                try {

                	thread1.join();    //把子线程加入到主线程中执行

                } catch (InterruptedException e) {    e.printStackTrace();    }

            }

        }

    }

}

class MyThread3 implements Runnable{

    public void run() {

        for (int i = 1; i <= 20; i++) {

            System.out.println(Thread.currentThread().getName() + ":" + i);

            try {

            	Thread.sleep(10);  

            } catch (InterruptedException e) {     e.printStackTrace();    }

        }

    }

}

  

执行结果

main:28

main:29

main:30

Thread-0:2

Thread-0:3

Thread-0:4

Thread-0:5

Thread-0:6

  

当主线程到达30时执行让步操作,子线程执行完成之后继续执行主线程

 

5、多线程中常用到的方法

  1.sleep()方法 
  在指定时间内让当前正在执行的线程暂停执行,但不会释放"锁标志"。 
  sleep()使当前线程进入阻塞状态,在指定时间内不会执行。 
  2.wait()方法 
  在其他线程调用对象的notifynotifyAll方法前,导致当前线程等待。线程会释放掉它所占有的"锁标志",从而使别的线程有机会抢占该锁。 
  当前线程必须拥有当前对象锁。
  waite()notify()必须在synchronized函数或synchronized block中进行调用。 
  3.yield方法 
  暂停当前正在执行的线程对象。 
  yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。 
  yield()只能使同优先级或更高优先级的线程有执行的机会。 
  4.join方法 
  等待该线程终止。 
  等待调用join方法的线程结束,再继续执行。如:t.join();//主要用于等待t线程运行结束,若无此句,main则会执行完毕,导致结果不可预测。

 

  6、总结:

    多线程并不是同时执行多条路径,而是系统分配执行时间,在同一时间还是只能进行一个执行路径,只是因为CPU的速度非常快,所以感觉是同时执行,就类似于十字路口的红绿灯,把同一个方向行驶的车辆分为直行走、左转、右转,红绿灯就是我们的系统,车辆就是就是我们执行的代码。