Java中的线程

盛年不重来,一日难再晨,及时当勉励,岁月不待人。这篇文章主要讲述Java中的线程相关的知识,希望能为你提供帮助。
目录
??线程??
??一个线程的生命周期??
??线程的创建与启动??
??线程的终止??
??线程的同步机制??
??线程的通知机制??
线程一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。
这里定义和线程相关的另一个术语 - 进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。
一个线程的生命周期
线程是一个动态执行的过程,它也有一个从产生到死亡的过程。
下图显示了一个线程完整的生命周期。

  • 新建状态:使用new  关键字和  Thread  类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序  start()  这个线程。
  • 就绪状态:当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
  • 运行状态:如果就绪状态的线程获取 CPU 资源,就可以执行run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
  • 阻塞状态:如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
  • 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
  • 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
  • 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
  • 死亡状态:一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
线程的创建与启动
java 提供了三种创建线程的方法:
  • 通过实现 Runnable 接口;
  • 通过继承 Thread 类本身;
  • 通过 Callable 和 Future 创建线程。
方法一:实现Runnable接口
这是因为 java 不允许多重继承,如果你的类已经继承于别的类,又要作为线程来运行,则可以使用这种方式
public class Hello implementsRunnable
public void run()
//填写要干的事


Hello obj=new Hello();
Thread t=new Thread(obj);
t.start(); //线程的启动

方法二:  继承 Thread 类
public class Helloextends Thread
public voidrun()
//填写要干的事


new Hello().start; //线程的启动

方法三:通过 Callable 和 Future 创建线程
  • 1. 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。
  • 2. 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
  • 3. 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
  • 4. 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。
创建线程的三种方式的对比:
  • 采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。
  • 使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。
线程的终止
当一个线程的start()调用后,线程为Alive状态。当一个线程的主函数run()退出后,线程死亡(Dead); 想让一个线程终止,就是要想办法让它从run()中退出来。当一个线程在sleep的时候,必须使用t.interrupt()来中断目标线程才可以使线程退出。等待线程的退出t.join();
//线程的终止
public class Test1 extends Thread
public boolean quitflag=false;
public void run()

for(int i=0; i< 10; i++)
if(quitflag)
break;

System.out.println("---------");
try
Thread.sleep(1000);
catch (InterruptedException e)
e.printStackTrace();





public class Hello
public static void main(String[] args)
// TODO 自动生成的方法存根
Test1 t1=new Test1();
t1.start();
//接受用户输入 输入后终止线程
InputStreamReader m=new InputStreamReader(System.in);
BufferedReader read=new BufferedReader(m);
try
read.readLine();
read.close();
t1.quitflag=true; //线程终止
t1.interrupt(); //使正在sleep的线程终止
t1.join(); //等待线程退出
catch (Exception e)
// TODO 自动生成的 catch 块
e.printStackTrace();

System.out.println("结束了");


线程的同步机制
当两个或多个线程同时访问一个对象时,可能发生数据不同步的现象。为了实现多线程对同一对象的同步访问,引入互斥锁的概念。
synchronized(lock)//申请锁

//关键代码

  • 若别的线程正持有该锁,则本线程阻塞等待
  • 若此锁空闲,则本线程持有锁,进入大括号执行,完毕后释放锁
线程的通知机制
wait/notify 通知机制 
  1. 调用 wait(),notify(),notifyAll()时需要先对调用对象加锁。
  2. 调用wait()方法后,线程由RUNNING变为WAITING,并将当前线程放置于对象等待队列,释放锁
  3. notify(),notifyAll()方法被调用后,等待线程依然不会从wait()方法返回,而是等调用notify(),notifyAll()的线程释放该锁之后,等待线程才有机会从wait()返回。
  4. notify()方法将等待队列中的一个等待线程从等待队列中移到同步队列中,而 notifyAll()方法则是把所有等待线程从等待队列中移到同步队列中,被移动的线程的状态由WAITING变成BLOCKED
  5. 从wait()方法返回的前提是获得了调用对象的锁。
public class Test1
static boolean flag=true;
static Object lock=new Object();

public static void main(String[] args)
One a=new One();
a.start();
Two b=new Two();
b.start();


static class One extends Thread
public void run()
synchronized(lock)
while(flag)
try
System.out.println("flag is true");
lock.wait(); //线程等待,释放锁
catch (InterruptedException e)
e.printStackTrace();


System.out.println("结束");



static class Two extends Thread
public void run()
synchronized (lock)
System.out.println("flag is false");
flag=false;
lock.notify();
System.out.println("notify之后,不会立即退出!");






【Java中的线程】


    推荐阅读