【java基础】16.线程

来源:互联网 发布:yosemite mac 编辑:程序博客网 时间:2024/06/06 04:18
1.线程(Thread)概念
线程就是程序中单独顺序的流控制。线程本身不能运行,它只能用于程序中。
多线程则指的是在单个程序中可以同时运行多个不同的线程执行不同的任务.
线程是程序内的顺序控制流,只能使用分配给程序的资源和环境。
线程与进程:
•多个进程的内部数据和状态都是完全独立的,而多线程是共享一块内存空间和一组系统资源,有可能互相影响.
•线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程切换的负担要小。
 
多线程程序比多进程程序需要更少的管理费用。进程是重量级的任务,需要分配给它们独立的地址空间。进程间通信是昂贵和受限的。进程间的转换也是很需要花费的。另一方面,线程是轻量级的选手。它们共享相同的地址空间并且共同分享同一个进程。线程间通信是便宜的,线程间的转换也是低成本的。
 
 
多线程编程的目的,就是"最大限度地利用CPU资源",当某一线程的处理不需要占用CPU而只和I/O等资源打交道时,让需要占用CPU资源的其它线程有机会获得CPU资源。从根本上说,这就是多线程编程的最终目的。
 
Java中如果我们自己没有产生线程,那么系统就会给我们产生一个线程(主线程,main方法就在主线程上运行),我们的程序都是由线程来执行的。
进程:执行中的程序(程序是静态的概念,进程是动态的概念)。
 
多线程:
•一个进程可以包含一个或多个线程
•一个程序实现多个代码同时交替运行就需要产生多个线程
•CPU随机的抽出时间,让我们的程序一会做这件事情,一会做另外一件事情
 
Java内置支持多线程编程(multithreaded programming)。多线程程序包含两条或两条以上并发运行的部分,把程序中每个这样的部分都叫作一个线程(thread)。每个线程都有独立的执行路径,因此多线程是多任务处理的一种特殊形式。
•多任务处理被所有的现代操作系统所支持。然而,多任务处理有两种截然不同的类型:基于进程的和基于线程的。
 
1.基于进程的多任务处理是更熟悉的形式。进程(process)本质上是一个执行的程序。因此基于进程的多任务处理的特点是允许你的计算机同时运行两个或更多的程序。举例来说,基于进程的多任务处理使你在运用文本编辑器的时候可以同时运行Java编译器。在基于进程的多任务处理中,程序是调度程序所分派的最小代码单位。
2.而在基于线程(thread-based)的多任务处理环境中,线程是最小的执行单位。这意味着一个程序可以同时执行两个或者多个任务的功能。例如,一个文本编辑器可以在打印的同时格式化文本。
 
2.线程的实现
在Java中通过run方法为线程指明要完成的任务,有两种技术来为线程提供run方法。
 
将我们希望线程执行的代码放到run方法中,然后通过start方法来启动线程,start方法首先为线程的执行准备好系统资源,然后再去调用run方法。当某个类继承了Thread类之后,该类就叫做一个线程类。
 
一个进程至少要包含一个线程。
对于单核CPU来说,某一时刻只能有一个线程在执行(微观串行),从宏观角度来看,多个线程在同时执行(宏观并行)。
对于双核或双核以上的CPU来说,可以真正做到微观并行。
 
1.继承Thread类并重写run方法。
        • Thread类:是专门用来创建线程和对线程进行操作的类。Thread中定义了    许多方法对线程进行操作。
    •Thread类在缺省情况下run方法什么都不做。可以通过继承Thread类并重写Thread类的run方法实现用户线程。
总体结构如下:
public class MyThread extends Thread {
public void run() {
… …
}
}
MyThread t = new MyThread();
t. start();
2.通过定义实现Runnable接口的类进而实现run方法。
    通过建立一个实现了Runnable接口的类,并以它作为线程的目标对象来创建一个线程。
Runnable接口:定义了一个抽象方法run()。定义如下:
public interface java.lang.Runnable{
public abstract void run();
}
创建的总体框架如下:
•class MyRunner implements Runnable {
public void run() {
}
}
•MyRunner r = new MyRunner();
•Thread t = new Thread( ThreadGroup group, Runnable target, String name);
例如: Thread t = new Thread( r, ―aa‖);
 
总结:
1.两种方法均需执行线程的start方法为线程分配必须的系统资源、调度线程运行并执行线程的run方法。
2.在具体应用中,采用哪种方法来构造线程体要视情况而定。通常,当一个线程已继承了另一个类时,就应该用第二种方法来构造,即实现Runnable接口。
3.线程的消亡不能通过调用一个stop()命令。而是让run()方法自然结束。
停止线程推荐的方式:
public class MyThread implements Runnable
{ private boolean flag=true;
public void run()
{ while (flag)
{…}
}
public void stopRunning()
{ flag=false;}
}
public class ControlThread
{ private Runnable r=new MyThread();
private Thread t=new Thread(r);
public void startThread()
{ t.start(); }
publi void stopThread()
{ r.stopRunning();}
}
 
1) Thread类也实现了Runnable接口,因此实现了Runnable接口中的run方法;
2) 当生成一个线程对象时,如果没有为其设定名字,那么线程对象的名字将使用如下形式:Thread-number,该number将是自动增加的,并被所有的Thread对象所共享(因为它是static的成员变量)。
3) 当使用第一种方式来生成线程对象时,我们需要重写run方法,因为Thread类的run方法此时什么事情也不做。
4) 当使用第二种方式来生成线程对象时,我们需要实现Runnable接口的run方法,然后使用new Thread(new MyThread())(假如MyThread已经实现了Runnable接口)来生成线程对象,这时的线程对象的run方法就会调用MyThread类的run方法,这样我们自己编写的run方法就执行了。
 
建议使用实现runnable接口,其优点是避免了单继承的局限性。
 
3.线程的生命周期
一个线程从创建到消亡的过程。
线程的状态转换图:
线程的生命周期可分为四个状态:
1).创建状态
•当用new操作符创建一个新的线程对象时,该线程处于创建状态。
•处于创建状态的线程只是一个空的线程对象,系统不为它分配资源
2).可运行状态
•执行线程的start()方法将为线程分配必须的系统资源,安排其运行,并调用线程体—run()方法,这样就使得该线程处于可运行( Runnable )状态。
•这一状态并不是运行中状态(Running ),因为线程也许实际上并未真正运行。
3).不可运行状态
当发生下列事件时,处于运行状态的线程会转入到不可运行状态。
•调用了sleep()方法;
•线程调用wait方法等待特定条件的满足
•线程输入/输出阻塞
 
返回可运行状态:
•处于睡眠状态的线程在指定的时间过去后
•如果线程在等待某一条件,另一个对象必须通过notify()或notifyAll()方法通知等待线程条件的改变
•如果线程是因为输入/输出阻塞,等待输入/输出完成
4). 消亡状态
当线程的run方法执行结束后,该线程自然消亡。
 
4.线程的优先级
1). 线程的优先级及其设置
设置优先级是为了在多线程环境中便于系统对线程的调度,优先级高的线程将优先执行。
一个线程的优先级设置遵从以下原则:
–线程创建时,子继承父的优先级
–线程创建后,可通过调用setPriority()方法改变优先级。
–线程的优先级是1-10之间的正整数。
1 - MIN_PRIORITY,
10 – MAX_PRIORITY
5- NORM_PRIORITY
2. 线程的调度策略
线程调度器选择优先级最高的线程运行。但是,如果发生以下情况,就会终止线程的运行。
•线程体中调用了yield()方法,让出了对CPU的占用权
•线程体中调用了sleep()方法, 使线程进入睡眠状态
•线程由于I/O操作而受阻塞
•另一个更高优先级的线程出现。
•在支持时间片的系统中,该线程的时间片用完。
 
Thread的一些常用方法。
•测试 threads:
isAlive()
• Thread priority:
t getPriority()
t setPriority()
•threads 进入非执行状态
Thread. sleep()
Thread. yield()
 
 
5.线程的同步
1).为什么要引入同步机制
在多线程环境中,可能会有两个甚至更多的线程试图同时访问一个有限的资源。必须对这种潜在资源冲突进行预防。
解决方法:在线程使用一个资源时为其加锁即可。访问资源的第一个线程为其加上锁以后,其他线程便不能再使用那个资源,除非被解锁。
2). 怎样实现同步
对于访问某个关键共享资源的所有方法,都必须把它们设为synchronized
如果想保护某些资源不被多个线程同时访问,可以强制通过synchronized方法访问那些资源。 调用synchronized方法时,对象就会被锁定。
说明:
•当synchronized方法执行完或发生异常时,会自动释放锁。
•被synchronized保护的数据应该是私有(private)的。
同步的线程状态图:
具有wait()和notify()的线程状态图:
 
synchronized关键字:当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。
Java中的每个对象都有一个锁(lock)或者叫做监视器(monitor),当访问某个对象的synchronized方法时,表示将该对象上锁,此时其他任何线程都无法再去访问该synchronized方法了,直到之前的那个线程执行方法完毕后(或者是抛出了异常),那么将该对象的锁释放掉,其他线程才有可能再去访问该synchronized方法。
 如果一个对象有多个synchronized方法,某一时刻某个线程已经进入到了某个synchronized方法,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任何synchronized方法的。
如果某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在的对象所对应的Class对象,因为Java中无论一个类有多少个对象,这些对象会对应唯一一个Class对象,因此当线程分别访问同一个类的两个对象的两个static,synchronized方法时,他们的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始执行。
  
synchronized块,写法:
synchronized(object)
{
}
表示线程在执行的时候会对object对象上锁。
 
synchronized方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized方法;synchronized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内、synchronized块之外的代码是可以被多个线程同时访问到的。




  注:此文档为学习圣思园风中叶教程结合自身学习所记录。  
原创粉丝点击