文章目录
- 并发
- 一.线程介绍
- 二.线程创建方法
- 1.派生Thread类的子类
- 2.创建Runnable接口的类
- 三.线程的状态
- 1.新线程态
- 2.可运行态
- 3.运行态
- 4.不可运行态
- 5.死亡态
- 四.线程常见方法介绍
- 1.start()和run()
- 2.sleep()
- 3.wait(), notify(), notifyAll()
- 4.yeild()
- 5.join()
并发
并发(concurrenc)机制支持程序员编写可以同时执行多个任务的应用程序。在Java编程语言中,并发编程主要与线程(thread)有关。
一.线程介绍
线程是程序中单个顺序控制流,有时又称为执行上下文(execution context)或者轻量级进程(lightweight process)。线程有起点、终点和顺序但它不能独立运行,而是要在程序中运行。java的重要优点之一就是它可以在一个程序中同时运行多个执行不同任务的线程。
二.线程创建方法
1.派生Thread类的子类
派生Thread类的子类,Thread类在java.lang
包中定义,它实现了Runnable接口;
重写Thread类的子类中的run()方法:
class className extends Thread{
public void run(){
...
}
}
2.创建Runnable接口的类
创建Runnable接口的类,Runnable接口也在java.lang
包中定义;
重写Thread类的子类中的run()方法:
class className implements Runnable{
public void run(){
...
}
}
三.线程的状态
线程跟进程一样,也有自己的生命周期。线程的生命周期分为新线程(New Tread)态,可运行(Runnable)态,运行(Running)态,不可运行(Not Runnable)态以及死亡(Dead)态。
1.新线程态
当用new
运算符创建了一个线程,只要没有调用start()
方法,就只是新线程状态。
例:
Thread MyThread = new Thread();
这时的线程只是一个空的对象,没有分配任何系统资源,可用stop()
方法将新线程态变成死亡态。
2.可运行态
当线程调用start()
方法时启动新线程,此时的线程变成可运行态
例:
MyThread.start();
启动后,该线程会获得系统的资源,并调用线程的run()方法。
3.运行态
CPU 开始调度处于就绪状态的线程,线程获得CPU资源然后执行任务(run()
方法),此时线程才得以真正执行,即进入到运行状态。
注意:可运行态是进入到运行态的唯一入口, 也就是说,线程要想进入运行态执行, 首先必须处于可运行态中;
4.不可运行态
当线程调用了suspend()
方法、sleep()
方法或者是调用了wait()
方法等待一个条件变量或被I/O阻塞,线程就可以从可运行态变成不可运行态。
其中不可运行态可分为以下几种阻塞状态:
- 等待阻塞:运行(Running)的线程执行
wait()
方法,JVM会把该线程放入等待队列(waitting queue)中。 - 同步阻塞:运行(Running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
- 其他阻塞:运行(Running)的线程执行
Thread.sleep()
或t.join()
方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()
状态超时、join()
等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(Runnable)状态。
注意:即使处理器有剩余空间,也不能执行不可运行态的线程。线程如果想要重返可运态,只有消除原来的不可运行的原因,例如被suspend()
挂起的1线程再调用resume()
方法恢复可运行态,sleep()
时间已结束…
5.死亡态
线程可以自然死亡,例如它的run()
方法正常退出运行;也可以被杀死,如调用stop()
方法。
四.线程常见方法介绍
1.start()和run()
- 用
start()
来启动线程,真正实现了多线程,就算出现start()
方法也是继续执行下面的代码而不需等待run()
体代码执行完。 - 通过调用Thread类的
start()
方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()
方法,这里方法run()
称为线程体,它包含了要执行的这个线程的内容,run()
方法运行结束,此线程随即终止。 -
run()
方法只是类的一个普通方法而已,如果直接调用run()
方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run()
方法体执行完毕后才可继续执行下面的代码,没有达到多线程的目的。
例:
public class Test {
public static void main(String args[]) {
Thread t = new Thread() {
public void run() {
fun1();
}
};
t.start();
System.out.println("A");
}
static void fun1() {
System.out.println("B");
}
}
结果:
例子中代码无需等待fun1()方法输出B而是继续执行下面的输出A
2.sleep()
- 线程的
sleep()
方法应该写在线程的run()
方法里,就能让对应的线程睡眠。 - 注意:
sleep()
方法只能让当前线程睡眠。调用某一个线程类的对象t.sleep(),睡眠的不是t,而是当前线程。
例:
public class Test {
public static void main(String[] args) {
Runner1 r = new Runner1();
r.start();
try {
System.out.println("当前运行的线程名称: "+ Runner1.currentThread().getName());
r.sleep(5000); //此处是对象.sleep()
System.out.println("当前运行的线程名称: "+ Runner1.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 3; i++) {
System.out.println("main thread :"+i);
}
}
}
class Runner1 extends Thread{
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("Runner1 : " + i);
}
}
}
结果:
3.wait(), notify(), notifyAll()
-
wait()
的作用是让当前线程进入等待状态,同时,wait()
也会让当前线程释放它所持有的锁。“直到其他线程调用此对象的notify()
方法或notifyAll()
方法”,当前线程被唤醒(进入“可运行态”) -
notify()
和notifyAll()
的作用,则是唤醒当前对象上的等待线程;notify()
是唤醒单个线程,而notifyAll()
是唤醒所有的线程。 -
wait()
让当前线程处于“等待阻塞状态”,“直到其他线程调用此对象的notify()
方法或notifyAll()
方法,或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。
例子:
class myThread implements Runnable{
private boolean flag ;
private Object object ;
myThread(boolean flag, Object o){
this.flag = flag;
this.object = o;
}
private void waitThread(){
synchronized (object) {
System.out.println(Thread.currentThread().getName() + "wait begin...");
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "wait end...");
}
}
private void notifyThread(){
synchronized (object) {
System.out.println(Thread.currentThread().getName() + "notify begin...");
object.notifyAll();
System.out.println(Thread.currentThread().getName() + "notify end...");
}
}
@Override
public void run() {
if(flag){
waitThread();
}else {
notifyThread();
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
Object object = new Object();
myThread mt2 = new myThread(false,object);
Thread thread1 = new Thread(mt2,"线程B ");
for (int i = 0;i<5;i++) {
myThread mt = new myThread(true,object);
Thread thread = new Thread(mt,"线程A "+i);
thread.start();
}
Thread.sleep(1000);
thread1.start();
}
}
结果:
这段代码中,开启了5个线程去执行waitThread()方法,可以发现5个线程都处于等待状态,1秒后开启一个线程去执行notifyThread(),唤醒所以等待的线程.
4.yeild()
-
yield()
让当前处于运行状态的线程退回到可运行状态,让出抢占资源的机会 -
yield()
方法只是提出申请释放CPU资源,至于能否成功释放由JVM决定.由于这个特性,一般编程中用不到此方法,但在很多并发工具包中,yield()方法被使用,如AQS、ConcurrentHashMap、FutureTask等。
5.join()
join()
主要作用是同步,它可以使得线程之间的并行执行变为串行执行。例如在A线程中调用了B线程的join()
方法时,表示只有当B线程执行完毕时,A线程才能继续执行。
例:
class JoinThread implements Runnable
{
public void run()
{
for(int i = 0; i< 10;i++)
{
System.out.println(Thread.currentThread().getName()+"..."+i);
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException
{
JoinThread object = new JoinThread();
Thread t0 = new Thread(object);
Thread t1 = new Thread(object);
t0.start();
t0.join();
t1.start();
for(int j = 0;j < 10;j++)
{
System.out.println(Thread.currentThread().getName()+"..."+j);
}
}
}
例子中程序运行结果是:当t0执行完时,t1和主线程main交替执行