------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、学习目标:
- 理解程序、线程和进程的概念
- 理解多线程的概念
- 掌握线程的各种状态
- 熟练使用Thread类、Runnable接口创建线程 熟练使用线程各种方法
- 掌握线程的调度及线程同步的实现原理
二、程序、线程、进程:
程序:是一段静态的代码,是一段计算机指令。
进程:是程序在自身地址空间中的一次动态执行。进程是资源申请、调度和和独立运行的单位,使用了CPU的资源。
线程:是轻量级的进程,是比进程更小的执行单位,是进程的一个独立的连续控制流。一个进程的执行过程中可以产生多个线程,线程之间可以互相通信,在整个运行过 程中,多有的线程只共享一段内存空间。
程序:是一段静态的代码,是一段计算机指令。
进程:是程序在自身地址空间中的一次动态执行。进程是资源申请、调度和和独立运行的单位,使用了CPU的资源。
线程:是轻量级的进程,是比进程更小的执行单位,是进程的一个独立的连续控制流。一个进程的执行过程中可以产生多个线程,线程之间可以互相通信,在整个运行过 程中,多有的线程只共享一段内存空间。
三、线程的五种状态:
新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread()
就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行 ,并不是说执行了t.start()此线程立即就会执行。
运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU 调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
- 等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
- 同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
- 其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
四、线程的创建:
1.继承Thread类,重写该类的run()方法。
<span style="font-family:SimSun;font-size:14px;"><span style="font-size:14px;">class MyThread extends Thread {
private int i = 0;
@Override
public void run() {
for (i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}</span></span>
<span style="font-family:SimSun;font-size:14px;"><span style="font-size:14px;">public class ThreadTest {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 30) {
Thread myThread1 = new MyThread(); // 创建一个新的线程 myThread1 此线程进入新建状态
Thread myThread2 = new MyThread(); // 创建一个新的线程 myThread2 此线程进入新建状态
myThread1.start(); // 调用start()方法使得线程进入就绪状态
myThread2.start(); // 调用start()方法使得线程进入就绪状态
}
}
}
}</span></span>
如上所示,继承Thread类,通过重写run()方法定义了一个新的线程类MyThread,其中run()方法的方法体代表了线程需要完成的任务,称之为线程执行体。当创建此线程类对象时一个新的线程得以创建,并进入到线程新建状态。通过调用线程对象引用的start()方法,使得该线程进入到就绪状态,此时此线程并不一定会马上得以执行,这取决于CPU调度时机。2.实现Runnable接口,并重写该接口的run()方法,该run()方法同样是线程执行体,创建Runnable实现类的实例,并以此实例作为Thread类的target来创建Thread对象, 该Thread对象才是真正的线程对象。
<span style="font-family:SimSun;font-size:14px;"><span style="font-size:14px;">class MyRunnable implements Runnable {
private int i = 0;
@Override
public void run() {
for (i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}</span></span>
<span style="font-family:SimSun;font-size:14px;"><span style="font-size:14px;">public class ThreadTest {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 30) {
Runnable myRunnable = new MyRunnable(); // 创建一个Runnable实现类的对象
Thread thread1 = new Thread(myRunnable); // 将myRunnable作为Thread target创建新的线程
Thread thread2 = new Thread(myRunnable);
thread1.start(); // 调用start()方法使得线程进入就绪状态
thread2.start();
}
}
}
}</span></span>
设置线程名字:setName(); 获取线程名字:getName();
主线程默认的名字可是:main
返回当前正在执行的线程:currentThread();
挂起线程:sleep();该方法要捕捉异常(InterruptedException) Wait()后面详细讲解;
返回当前正在执行的线程:currentThread();
挂起线程:sleep();该方法要捕捉异常(InterruptedException) Wait()后面详细讲解;
中断线程:interrupt();
终止线程:stop();不建议使用,会引起不安全问题;
终止线程:stop();不建议使用,会引起不安全问题;
下面将上述的方法用到实际的程序中:
<span style="font-family:SimSun;font-size:14px;">public class ThreadMethod extends Thread{ public ThreadMethod(String name){ super(name); }
public void run(){
Thread thread = Thread.currentThread();
System.out.println("修改前线程的名字是:"+thread.getName()); thread.setName("线程B");
System.out.println("修改后的线程名字是:"+thread.getName()); try{
System.out.println(thread.getName()+"睡眠一秒钟"); Thread.sleep(1000);
System.out.println(thread.getName()+"让出锁两秒钟"); wait(1000*2);
}catch(InterruptedException e){
System.out.println(thread.getName()+"被打断了"); } }
public static void main(String[] args){
String name = Thread.currentThread().getName(); System.out.println(name+"开始执行");
</span>
检查线程:
:isALive();
isAlive()方法可以判断一个线程是否处于活动状态,当线程处于新建状态时,使用该方法将会返回FALSE,在线程的run方法结束之前,isAlive方法的返回值都是TRUE,当线程进入死亡状态时,该方法的返回值是FALSE;
线程联合:
join()
如果一个线程需要另一个线程执行完毕,才继续执行,可以使用join方法;
守护线程:
setDaemon();
守护线程也称为用户线程,主要是为其他的线程提供支持。当其他非守护线程执行完毕时,java虚拟机才会停止守护线程的执行,退出程序。
线程的调度和优先级:
设置优先级:setPriority(),参数:Thread.MAX_PRIORITY,Thread.MIN_PRIORITY,Thread.NORM_PRIORITY;
join方法实例
<span style="font-family:SimSun;font-size:14px;">public class JoinMethod extends Thread{ public void runThread(){
String name = Thread.currentThread().getName();
System.out.println(name+"开始执行runThread()方法"); for(int i = 0;i<5;i++){ try{
Thread.sleep(1000);
}catch(InterruptedException e){}
System.out.println(name+"第"+i+"次执行循环体"); } }
public void run(){ runThread(); }
public static void main(String[] args){ JoinMethod jm = new JoinMethod(); jm.setName("联合线程"); jm.start(); try{
jm.join();
}catch(InterruptedException e){} } }</span>
wait方法实例
<span style="font-family:SimSun;font-size:14px;">public class WaitDemo implements Runnable{
private synchronized void readString(String content){ String name = Thread.currentThread().getName(); System.out.println(name+"开始执行"); System.out.println("当前对象"+this); try{
System.out.println(name+"等待1秒"); wait(1000*2); Thread.sleep(1000);
System.out.println(name+"线程休眠了两秒"); }catch(InterruptedException e){}
System.out.println(name+"输出字符串"); for(int i = 0;i<content.length();i++){ System.out.print(content.charAt(i));
} }
public void run(){
String content = "心中的日月"; readString(content); }
public static void main(String[] args){ WaitDemo wd = new WaitDemo(); new Thread(wd,"线程A").start(); new Thread(wd,"线程B").start(); } }</span>
线程同步:当多个线程共同操作同一个对象时,会出现数据的混乱,要加关键字synchronized实现线程的同步。在方法前面加上 synchronized,每次只能使一个线程操作数据。
等待:当一个线程正在使用一个同步方法时,该线程会一直占用改同步方法的锁,知道执行完毕,但是可以通过wait方法使线程自动 释放同步方法的锁,这样会是线程处于就绪状态,不会监视组员使用权。
唤醒:notify()方法,唤醒一个在此对象监视器上等待的线程;notify()唤醒所有在此对象监视器上的等待的线程。
死锁:死锁是由于资源的无序使用带来的,解决死锁问题的方法;线程获取对象锁的顺序一致即可
唤醒:notify()方法,唤醒一个在此对象监视器上等待的线程;notify()唤醒所有在此对象监视器上的等待的线程。
死锁:死锁是由于资源的无序使用带来的,解决死锁问题的方法;线程获取对象锁的顺序一致即可