Java基础--线程(1)

来源:互联网 发布:证大淘宝贷 编辑:程序博客网 时间:2024/05/20 13:05
1.线程的基本概念​​
线程是一个程序内部的顺序控制流​,是一个程序里的不同执行路径。一个时间点上一个cpu只有一个线程在运行
1.定义
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
2.关系​
一个进程是一个静态的概念,一个进程开始执行是指一个main方法开始执行即一个主线程开始执行
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.
相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。
3.区别
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.2) 线程的划分尺度小于进程,使得多线程程序的并发性高。3) 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。4) 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。5) 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
4.优缺点
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。
​多进程:在操作系统中能同时运行多个任务(程序)​
多线程:在同一应用程序中有多个顺序流同时进行​
java的线程是通过java.lang.Thread类来实现的。​
VM(虚拟机)启动是会有一个有主方法(public static void main(){})所定义的线程。同时通过创建Thread的实例来创建新的线程。​
每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,方法run()成为线程体。通过调用Thread类的start()方法来启动一个线程。​
pubilc class{
public static void main(String args[]){
m1();}
pubilc static void m1(){m2();m3();}
public static void m2(){}
public static void m3(){}​}

该main方法中只有一条路径,线程运行是并行的


2.线程的创建和启动​
可以有两种方式创建先的线程​
第一种:
定义线程类实现Runnable接口,Thread myThread = new Thread(target)//target为Runnable接口类型。Runnable中只有一个方法:​
Public void run();用于定义线程的运行体。​
使用Runnable接口可以为多个线程提供共享数据,在实现Runnable接口的类的run方法定义中可以使用Thread的静态方法:public staitc Thread currentThread()获取当前线程的引用。​​
public class TestThread1 {    public static void main(String args[]) {        Runner1 r = new Runner1();        Thread t = new Thread(r);        t.start();        for (int i = 0; i < 100; i++) {            System.out.println("Main Thread:-----" + i);        }    }}class Runner1 implements Runnable {    public void run() {        for (int i = 0; i < 100; i++) {            System.out.println("Runner1:" + i);        }    }}

结果:runner1和Main Thread:-----交替运行,即是并行运行​
如果直接调用r的run方法,不new  thread的话,等同于方法调用,先运行runner1,在运行Main Thread。
​第二种:​
可以定义一个Thread的子类并重写其run方法如:​
class MyThread extends Thread {​
public void run(){……}}​
然后生成该类的对象:​
MyThread myThread = new MyThread(……)​​​
例如:
public class TestThread1 {    public static void main(String args[]) {        Runner1 r = new Runner1();        r.start();        for (int i = 0; i < 100; i++) {            System.out.println("Main Thread:-----" + i);        }    }}class Runner1 extends Thread {    public void run() {        for (int i = 0; i < 100; i++) {            System.out.println("Runner1:" + i);        }    }}

结果:和上述例子相同​
以上两种方法,推荐第一种,因为类继承了Thread的话,就不能在继承其他的类啦​
3.线程状态转换​
创建->start()->就绪状态 <-调度->运行状态->终止​
             阻塞解除​   |                            |   导致阻塞的事件
                              <- 阻塞状态      <-
线程控制基本方法
isAlive()                 判断线程是否还活着,即线程是否还未终止
getPriority()           获得线程的优先级数值
setPriority()           设置线程的优先级数值
Thread.sleep()     将当前线程睡眠指定毫秒数
join()                       调用某线程的该方法,将当前线程与该线程合并,即等待该线程结束,在恢复当前线程的运行
yield()                     让出CPU,当前线程进入就绪队列等待调度
wait()                      当前线程进入对象的wait pool​
wait和sleep的区别:wait时别的线程可以访问锁定对象(调用wait方法时候必须锁定该对象),sleep时别的线程也不可以访问锁定对象
notify/notifyAll唤醒对象的wait pool中的一个/所有等到线程
sleep方法
可以调用Thread的静态方法
public static void sleep(long millis)throws InterruptedException使得当前线程休眠(暂停执行毫秒数),由于是静态方法,sleep可以由类名直接调用:Thread.sleep(……)
public class TestInterrupt {    public static void main(String args[]) {        MyThread thread = new MyThread();        thread.start();        try {            Thread.sleep(5000);        }//主线程睡眠5秒        catch (InterruptedException e) {        }        thread.interrupt();//主线程打断thread    }}class MyThread extends Thread {    boolean flag = true;    public void run() {        while (flag) {            System.out.println("===" + new Date() + "===");            try {                sleep(1000);            } catch (InterruptedException e) {                return;            }        }    }}

结果是:
===Sat Apr 18 11:55:39 GMT+08:00 2015===
===Sat Apr 18 11:55:40 GMT+08:00 2015===
===Sat Apr 18 11:55:41 GMT+08:00 2015===
===Sat Apr 18 11:55:42 GMT+08:00 2015===
===Sat Apr 18 11:55:43 GMT+08:00 2015===​
Interrupt的打断方法,睡眠时打断不是最优的选择(可能有没关闭的文件),不过stop会更见粗暴
join方法​
合并某个线程​
public class TestJoin {    public static void main(String args[]) {        MyThread2 t1 = new MyThread2("t1");        t1.start();        try {            t1.join();        }//将t1合并到main中,等于函数引用,引用t1后在继续往后执行        catch (InterruptedException e) {        }        for (int i = 1; i <= 10; i++) {            System.out.println("i am main thread");        }    }}class MyThread2 extends Thread {    MyThread2(String s) {        super(s);    }    public void run() {        for (int i = 1; i <= 10; i++) {            System.out.println("i am " + getName());            try {                sleep(1000);            } catch (InterruptedException e) {                return;            }        }    }}

结果是:先每隔一秒输出一个i am i,然后输出10个i am main thread,去除join后等同于前例的TestThread1
yield方法
让出CPU,给其他线程执行的机会
public class TestYield {    public static void main(String[] args) {        MyThread3 t1 = new MyThread3("t1");        MyThread3 t2 = new MyThread3("t2");        t1.start();        t2.start();    }}class MyThread3 extends Thread {    MyThread3(String s) {        super(s);    }    public void run() {        for (int i = 1; i <= 100; i++) {            System.out.println(getName() + ": " + i);            if (i == 0) {                yield();            }        }    }}

结果:本程序的目的是让​t1和t2在遇到10的倍数的时候将运行权转交给另一个线程,但是运行的结果并没有这样,其原因是:Thread.yield( )方法:使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。cpu会从众多的可执行态里选择,也就是说,刚刚的那个线程还是有可能会被再次执行到的,并不是说一定会执行其他线程了。
0 0
原创粉丝点击