Java多线程机制

来源:互联网 发布:励志情侣走红网络 编辑:程序博客网 时间:2024/06/07 22:18

一、进程与线程的概念
进程是运行中的程序,当一个程序运行时,内部可能包含多个顺序执行流,每个顺序执行流就是一个线程。
进程:一个运行的程序
线程:同一个进程中执行的子程序流
并发性:进程与进程、线程与线程在单处理机操作系统上在宏观上并行,微观上串行
多线程机制使得一个程序可以同时执行多个任务

二、Java中的线程
Java应用程序在发现main方法之后就会启动一个线程,这个线程称为”主线程”(main线程),在main方法的执行中再创建的线程就称为程序中的其他线程。JVM一直要等到Java应用程序中的所有线程都结束之后才结束Java应用程序

线程的五种状态:
1.New 创建状态
当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于创建状态,此时它已经有了相应的内存空间和其他资源
2.Runnable(可执行) 就绪状态
线程创建之后具备了运行条件,但仅仅是占有了内存资源,在JVM管理的线程中还没有这个线程,调用start()方法通知JVM,这样JVM就知道有一个线程等候切换。
3.Running(运行) 执行状态
JVM将CPU使用权切换给线程,线程开始执行线程体,即run()方法中的内容
4.Block(阻塞) 阻塞状态
有4种原因的中断:
①JVM将CPU资源从当前进程切换给其他进程,使得本线程让出CPU的使用权处于中断状态
②线程调用sleep(int millsecond)方法主动放弃所占的CPU资源进入休眠状态
③线程使用wait()方法,使得当前线程进入等待状态
注:sleep()方法等待指定的时间即可重新进入就绪队列,wait()方法必须由其他线程调用notify()方法来通知它,sleep()只释放CPU资源,wait()还释放对象锁
④线程执行某个状态进入阻塞状态,如:控制台输入
5.Dead(死亡) 终止状态
终止状态就是线程释放了实体,即释放分配给线程对象的内存。线程终止的原因有二:
①run()方法执行完毕
②线程被提前强制性的终止,即强制run()方法结束
注:已终止的线程不可以调用start()方法重启

线程状态的转换

线程调度的优先级:
在JVM中的线程调度器负责管理线程,调度器把线程的优先级分为10个级别,为1~10之间的整数(Thread.MIN_PRIORITY = 1,Thread.MAX_PRIORITY = 10,Thead.NORM_PRIORITY = 5),如果没有明确的设置线程优先级别,默认为5.
Java线程调度器的调度方式:使高优先级的线程能始终运行,一旦时间片有空闲,则使具有同等优先级的线程以轮流的方式顺序使用时间片。
注意:有的操作系统只识别三个级别:1、5、10

三、Java线程的创建
1.通过Thread类或其子类创建一个线程对象
用Thread类创建线程的方法有两种:
①继承Thread类并重写其中的run()方法
②创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。

package ex;     import java.lang.Thread; public class Test {   public static void main(String[] args) { Cat cat = new Cat(); Dog doge = new Dog(); Thread dog = new Thread(doge); cat.setName("猫");    //设置线程名,这里没用 dog.setName("狗");    //设置线程名,这里没用 cat.start(); dog.start(); } } class Cat extends Thread{ public void run() { for(int i = 0;i < 10;i++) { System.out.println("喵喵喵"); } } } class Dog implements Runnable{ public void run(){ for(int i = 0;i < 10;i++) { System.out.println("汪汪汪"); } } }//运行结果 /*喵喵喵  *喵喵喵  *喵喵喵  *喵喵喵  *喵喵喵  *喵喵喵  *喵喵喵  *喵喵喵  *汪汪汪  *汪汪汪  *汪汪汪  *汪汪汪  *汪汪汪  *汪汪汪  *汪汪汪  *汪汪汪  *汪汪汪  *汪汪汪  *喵喵喵  *喵喵喵  **/

目标对象与线程的关系:
①目标对象和线程完全解耦
目标对象不含对线程的引用。简而言之,重写了run方法的那个类没有线程对象作为它的成员。比如上面代码中的两个目标对象和两个线程之间就是完全解耦的。
②目标对象组合线程
目标对象中含有对线程的引用。即重写了run方法的类里面组合了线程对象
Eg:

``` package ex;     import java.lang.Thread; public class Test {       public static void main(String[] args) {         Cat cat = new Cat();         cat.meow.start();     } } class Cat extends Thread{     Thread meow;     public Cat() {         meow = new Thread(this); //以当前对象作为线程的目标对象     }     public void run() {         for(int i = 0;i < 3;i++) {             System.out.println("喵喵喵");         }     }    }

Thread类中的常用方法:
①start(),调用之后线程进入就绪队列,排队等CPU
②run(),执行线程中的程序流
③sleep(),高级线程可以通过调用sleep()方法进入休眠状态,把CPU让给后面排队的低级进程
④isAlive(),测试线程是否处于活跃状态。在run()方法执行时,返回的都是true
⑤currentThread(),返回当前正在使用CPU资源的线程
⑥interrupt(),唤醒当前正在休眠的进程,.interrupt()可以”吵醒”正在”sleep()”的自己

线程同步:
多线程并发访问一个对象(临界资源),如果不对线程操作进行同步控制,破坏原子操作,可能会造成”脏数据”。
每个对象都有一个互斥的锁标记和一个锁池。当线程拥有这个对象的锁标记时才能访问这个资源,没有锁标记就进入锁池,保证在同步代码块中只有一个线程,解决了多线程同步控制的问题。
关键字:synchronized
Eg:

package ex;     import java.lang.Thread; public class Test {       public static void main(String[] args) {         Test t = new Test();         BuyTickets buyer1 = t.new BuyTickets();         BuyTickets buyer2 = t.new BuyTickets();         Thread b1 = new Thread(buyer1);         Thread b2 = new Thread(buyer2);         b1.start();         b2.start();     }     int ticketNum = 100;     public class BuyTickets implements Runnable{         public void run() {  //synchronized             buy();         }         public void buy() {             for(int i = 0;i < 20;i++) {                 if(ticketNum>0) {                     System.out.println("购买一张后还有" + --ticketNum + "张");                 }             }         }     } } //运行结果:

运行结果
第一行和第二行,两个进程同时访问了ticketNum(此时为100),读取到了”脏数据”
修改方法:用synchronized设置同步代码块(上锁)

public void run() {             synchronized(BuyTickets.class) {                 buy();             }}

这里贴两篇介绍synchronized用法的博客:
http://www.blogjava.net/konhon/archive/2005/08/17/10296.html
http://blog.csdn.net/luoweifu/article/details/46613015
相关方法:
wait():交出锁和CPU占用
notify():唤醒在此对象监视器上等待的单个线程,若有多个线程,则由系统决定唤醒哪一个
notifyAll():唤醒在此对象监视器上等待的所有线程。
注意:
①这三个方法都是Object类的,而不是Thread类的
②只能对加锁的资源进行wait()和notify()
③判断是否进行等待wait()时,用while代替if来进行判断
线程联合:
一个线程A在占有CPU资源期间,可以让其他线程调用join()与本线程联合。如:B.join()
如果线程A在占有CPU资期间联合了B线程,那么A线程将立即中断执行,一直等到它的联合线程B执行完毕之后,再重新排队等待CPU资源。如果A准备联合的B线程已经结束,那么B.join()没有任何效果。

守护线程:
线程默认是非守护线程,也称为用户(user)线程,一个线程可以调用setDaemon(true)方法将自己设置为守护线程。当程序中的所有用户线程都已结束时,即使用户线程的run方法中还有需要执行的语句,守护线程也会立即结束。我们可以用守护线程做一些不是很严格的工作。
2.Java线程池ThreadPoolExecutor类
内容也不少,贴个大神的链接:
http://www.importnew.com/19011.html

//如有错误,欢迎指正