线程:(好东西)
为什么要使用线程:
1、 提高系统的吞吐率或使用效率。(I/0等待)
2、 提供灵敏的用户操作。
举例:
第一部分 原理
定义:
程序内部顺序执行的指令序列。线程本身不拥有资源(没有自己的资源),如内存空间。但是它可以使用进程的资源。
线程与进程的区别:
1、 从资源分配的角度
进程是资源分配的基本单位,而线程本身没有资源。
2、 从程序调度的角度
进程可以包含多个线程,是程序调度的基本单位。
3、 从系统的开销角度
进程的创建过程的系统开销大与线程创建过程的时间开销,因为进程创建需要为他分配资源,而此过程的系统消开销是很大的,而线程共享进程的资源,无需要进行分配。
4、 从并发性的角度
进程与线程在执行过程中都可以并发的进行,但并发的粒度不同。进程的并发表现为应用级的并发,如:如放歌与编写程序。而线程的并发表现在应用内部的功能性并发,如:电影中的图像与声音。
举例说明:
1、 进程与线程
2、 很好的例用线程,必须明确原理
进程的状态:
进程运行的必要条件:
1、 拥有资源
2、 拥有CPU时间
三态:
运行状态:当线程在处理机上运行的状态程为运行状态(拥有资源和CPU)
就绪状态:当线程拥有资源但没有CPU时间时,等CPU时间的状态。
阻塞状态:当线程等待资源的时的状态。
五态:
新建:刚刚创建还没有提交的状态。
终止:结束运行。
第二部分 Java中的线程。
线程的创建:
两种创建方式:
1、 继承Thread类。
声明一个 Thread 类的子类,并覆盖 run() 方法。
class mythread extends Thread {
public void run( ) {/* 覆盖该方法*/ }
}
2、 实现Runnable接口。
声明一个实现 Runnable 接口的类,并实现 run() 方法。
class mythread implements Runnable{
public void run( ) {/* 实现该方法*/ }
}
Ex1:
public class CreateWinthThread extends Thread {
private int countDown = 5;
private static int threadCount = 0;
public SimpleThread() {
super("" + ++threadCount); // Store the thread name
start();
}
public String toString() {
return "#" + getName() + ": " + countDown;
}
public void run() {
while(true) {
System.out.println(this);
if(--countDown == 0) return;
}
}
public static void main(String[] args) {
for(int i = 0; i < 5; i++)
new CreateWinthThread ();
}
}
线程的主要方法:
Run():在此方法中编写线程所要完成的功能。
Start():将线程从新建装态改为就绪状态。
线程就绪以后实际的执行是由JVM和OS负责。
l 执行的时间是无法确定的(多线程中)
l 执行的顺序是无法确定的(多线程中)
l 执行的结果是无法确定的(多线程中)
继承Thread常用的几种构造函数:
l Thread(),用缺省名称创建一个Thread对象
l Thread(String name),用指定的name参数的名称创建一个Thread对象
l Thread(Runnable target) 用实现了Runnable接口的类创建一个Thread对像
l Thread(Runnable target, String name) 用实现了Runnable接口的类创建一个Thread对像,线程为name参数指定的名称。
Ex2:
public class CreateWithRunnable implements Runnable {
private int countDown = 100;
public String toString() {
return "#" + Thread.currentThread().getName() +
": " + countDown;
}
public void run() {
while(true) {
System.out.println(this);
if(--countDown == 0) return;
}
}
public static void main(String[] args) {
for(int i = 1; i <= 5; i++)
new Thread(new CreateWithRunnable(), "" + i).start();
}
}
currentThread():得到当前线程
getName():得到线程的名称
Runnable接口中只有一个Run方法,没有start()无法将线程从新建态转为就绪态,所以无法运行线程。必须把Runnable转为Thread类。才可以运行。
创建反应敏捷的用户界面
public class FriendlyUI {
private static int i = 0;
public static void main(String[] args) {
new StopRun().start();
Compute compute = new Compute();
compute.setI(i);
compute.start();
}
}
class StopRun extends Thread {
private String flag = "";
public void setFlag() {
try {
InputStreamReader reader = new InputStreamReader(System.in);
BufferedReader input = new BufferedReader(reader);
System.out.println("如果要终止程序请输入 exit");
this.flag = input.readLine();
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
while (!flag.equals("exit")) {
this.setFlag();
}
System.exit(0);
}
}
class Compute extends Thread {
private int i = 0;
public void setI(int i) {
this.i = i;
}
public int getI() {
return i;
}
public void run() {
while (true) {
i++;
}
}
}
sleep ():让它停一段以毫秒计的时间
Yielding():把线程的控制权转交其它线程
Join():待线程处理完之后才转交控制权
守护线程(精灵线程、监控线程、服务线程)
所谓"守护线程(daemon thread)"是指,只要程序还在运行,它就应该在后台提供某种公共服务的线程,但是守护线程不属于程序的核心部分。因此,当所有非守护线程都运行结束的时候,程序也结束了。相反,只要还有非守护线程在运行,程序就不能结束。比如,运行main( )的线程就属于非守护线程
Ex3(sleep,yield,join)
public class YieldingThread extends Thread {
private static Test monitor = new Test();
private int countDown = 100;
private static int threadCount = 0;
public YieldingThread() {
super("" + ++threadCount);
start();
}
public String toString() {
return "#" + getName() + ": " + countDown;
}
public void run() {
while(true) {
System.out.println(this);
if(--countDown == 0) return;
//yield();
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
for(int i = 0; i < 5; i++)
try {
new YieldingThread().join();
} catch (InterruptedException e) {
// TODO 自动生成 catch 块
e.printStackTrace();
}
//new YieldingThread();
}
}
友好用户界面的例字
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class FriendlyUI {
private static int i = 0;
public static void main(String[] args) {
new StopRun().start();
Compute compute = new Compute();
compute.setI(i);
compute.start();
}
}
class StopRun extends Thread {
private String flag = "";
public void setFlag() {
try {
InputStreamReader reader = new InputStreamReader(System.in);
BufferedReader input = new BufferedReader(reader);
System.out.println("如果要终止程序请输入 exit");
this.flag = input.readLine();
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
while (!flag.equals("exit")) {
this.setFlag();
}
System.exit(0);
}
}
class Compute extends Thread {
private int i = 0;
public void setI(int i) {
this.i = i;
}
public int getI() {
return i;
}
public void run() {
while (true) {
i++;
}
}
}
线程之间的优先级:
1-10
MAX_PRIORITY=10
通过 setPriority()进行设置
虽然JDK提供了10级优先级,但是却不能很好地映射到很多操作系统上。比方说,Windows 2000平台上有7个等级还没固定下来,因此映射是不确定的(虽然Sun的Solaris有231个等级)。要想保持可移植性,唯一的办法就是,在调整优先级的时候,盯住MIN_PRIORITY, NORM_PRIORITY, 和MIN_PRORITY
线程之间的协作
l 线程互斥使用资源
l 线程同步
线程互斥使用资源:
临界区:一段共公的内存空间或设备,为两个或两个以上的线程所使用。
各线程共同使用临界区的时候可能会产生冲突。
public class CreateMutexThread extends Thread {
static VarClass varClass = new VarClass();
/*
run方法中的内容为线程的中要执行的内容
*/
public void run(){
while(true){
varClass.setI();
int number=0;
number=varClass.getI();
if((number%2)!=0){
System.out.println(getName()+" "+ varClass.getI());
System.exit(0);
}
}
}
public static void main(String[] args) {
for(int i=0;i<10;i++){
new CreateMutexThread().start();
}
}
}
class VarClass{
int i=0;
public void setI(){
i++;
i++;
}
public int getI(){
return i;
}
}
解决办法:
代码的前后设一条加锁和解锁的语句,这样同一时刻只有一个线程能够执行这段代码。
每个对象都有一个锁(也称监控器monitor),它是对象生来就有的东西(因此你不必为此写任何代码)。当你调用synchronized方法时,这个对象就被锁住了。在方法返回并且解锁之前,谁也不能调用同一个对象的其它synchronized方法。就说上面那两个方法,如果你调用了f( ),那么在f( )返回并且解锁之前,你是不能调用同一个对象的g( )的。因此对任何一个特定的对象,所有的synchronized方法都会共享一个锁,而这个锁能防止两个或两个以上线程同时读写一块共用内存
public class CreateMutexThread extends Thread {
static VarClass varClass = new VarClass();
/*
run方法中的内容为线程的中要执行的内容
*/
public void run(){
while(true){
varClass.setI();
int number=0;
number=varClass.getI();
if((number%2)!=0){
System.out.println(getName()+" "+ varClass.getI());
System.exit(0);
}
}
}
public static void main(String[] args) {
for(int i=0;i<10;i++){
new CreateMutexThread().start();
}
}
}
class VarClass{
int i=0;
synchronized public void setI(){
i++;
i++;
}
Synchronized public int getI(){
return i;
}
}
同步:
一个线程的结果是另一个线程运行的条件时用到。
class Order {
private static int i = 0;
private int count = i++;
public Order() {
if(count == 10) {
System.out.println("Out of food, closing");
System.exit(0);
}
}
public String toString() { return "Order " + count; }
}
class WaitPerson extends Thread {
private Restaurant restaurant;
public WaitPerson(Restaurant r) {
restaurant = r;
start();
}
public void run() {
while(true) {
while(restaurant.order == null)
synchronized(this) {
try {
wait();
} catch(InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println(
"Waitperson got " + restaurant.order);
restaurant.order = null;
}
}
}
class Chef extends Thread {
private Restaurant restaurant;
private WaitPerson waitPerson;
public Chef(Restaurant r, WaitPerson w) {
restaurant = r;
waitPerson = w;
start();
}
public void run() {
while(true) {
if(restaurant.order == null) {
restaurant.order = new Order();
System.out.print("Order up! ");
synchronized(waitPerson) {
waitPerson.notify();
}
}
try {
sleep(100);
} catch(InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class Restaurant {
Order order; // Package access
public static void main(String[] args) {
Restaurant restaurant = new Restaurant();
WaitPerson waitPerson = new WaitPerson(restaurant);
Chef chef = new Chef(restaurant, waitPerson);
}
}
同步例2:
public class TestThread {
static ShareDate shareDate=new ShareDate();
public static void main(String[] args) {
SetValue setValue=new SetValue(shareDate);
setValue.start();
new GetValue(shareDate,setValue).start();
}
}
class ShareDate{
private String[] strValue=new String[10];
private int index=0;
synchronized public String getStrValue(){
index--;
return strValue[index+1];
}
synchronized public void setStrValue(String value){
index++;
this.strValue[index-1]=value;
}
synchronized public int getIndex(){
return index;
}
}
class GetValue extends Thread{
private ShareDate shareDate;
private SetValue setValue;
private int index;
public GetValue(ShareDate shareDate,SetValue setValue){
this.shareDate=shareDate;
this.setValue=setValue;
}
public void run(){
while(true){
index=shareDate.getIndex();
System.out.println("GetValue"+index);
if (index==0){
synchronized(setValue){
setValue.notify();
}
try {
sleep(100);
} catch (InterruptedException e) {
// TODO 自动生成 catch 块
e.printStackTrace();
}
yield();
}else if (index>=0 && index<=9){
System.out.println(shareDate.getStrValue());
}
}
}
}
class SetValue extends Thread{
private ShareDate shareDate;
int index;
public SetValue(ShareDate shareDate){
this.shareDate=shareDate;
}
public void run(){
int i=0;
while(true){
this.index=shareDate.getIndex();
System.out.println("SetValue"+index);
if (index>=9){
synchronized (this){
try {
this.wait();
} catch (InterruptedException e) {
// TODO 自动生成 catch 块
e.printStackTrace();
}
}
}else{
shareDate.setStrValue(""+i);
i++;
}
}
}
}
三部分:总结
1、 线程的创建(两种创建方式)
2、 线程的属性与方法
3、 线程的状态
线程的状态
线程的状态可归纳为以下四种:
- New: 线程对象已经创建完毕,但尚未启动(start),因此还不能运行。
- Runnable: 处在这种状态下的线程,只要分时机制分配给它CPU周期,它就能运行。也就是说,具体到某个时点,它可能正在运行,也可能没有运行,但是轮到它运行的时候,谁都不能阻止它;它没有dead,也没有被阻塞。
- Dead: 要想中止线程,正常的做法是退出run( )。在Java 2以前,你也可以调用stop( ),不过现在不建议用这个办法了,因为它很可能会造成程序运行状态的不稳定。此外还有一个destroy( )(不过它还没有实现,或许将来也不会了,也就是说已经被放弃了)。后面我们会讲怎样用其它办法来实现stop( )的功能。
- Blocked: 就线程本身而言,它是可以运行的,但是有什么别的原因在阻止它运行。线程调度机制会直接跳过blocked的线程,根本不给它分配CPU的时间。除非它重新进入runnable状态,否则什么都干不了。
进入阻塞状态
如果线程被阻塞了,那肯定是出了什么问题。问题可能有以下几种:
- 你用sleep(milliseconds)方法叫线程休眠。在此期间,线程是不能运行的。
- 你用wait( )方法把线程挂了起来。除非收到notify( )或notifyAll( )消息,否则线程无法重新进入runnable状态。这部分内容会在后面讲。
- 线程在等I/O结束。
- 线程要调用另一个对象的synchronized方法,但是还没有得到对象的锁。
或许你还在旧代码里看到过suspend( )和resume( ),不过Java 2已经放弃了这两个方法(因为很容易造成死锁),所以这里就不作介绍了。
课后习题:
l 了解守护线程
l 线程共享使用资源
l 线程互斥使用资源
l 线程同步