黑马程序员----多线程

来源:互联网 发布:脂肪坏处 知乎 编辑:程序博客网 时间:2024/06/04 01:29

——- android培训、java培训、期待与您交流! ———-


一、多线程

进程:正在进行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫做控制单元。
线程:就是进程中的一个独立的控制单元,线程在控制着进程的执行。

一个进程中至少有一个线程。
jvm启动的时候会有一个java.exe。该进程中至少有一个线程负责java程序的执行,而且这个线程运行的代码存在于主函数中,该线程被称为主线程。

虚拟机启动的时候就是多线程的,因为有负责垃圾回收机制的线程。(可以说是单线程的,知道即可。)
如果是单线程执行程序,当内存被垃圾占满时,程序就会停止去清理垃圾。

多线程:一个进程中有多个线程。Java虚拟机允许应用程序并发地运行多个执行线程。

如何在程序中自定义一个控制单元(线程)?通过对API的查找,Java已经提供了对线程这类事物的描述,就是Thread类。
创建新执行线程方法:将类声明为Thread类的子类。该子类应重写Thread类的run方法。
        步骤:
            1.继承Thread类;
            2.重写Thread类的run方法;(目的是将自定义的代码存储在run方法中,让线程运行。)
            3.创建并启动线程:创建该类的对象,调用start方法;
    
run方法:如果该线程是使用独立的Runnable运行对象,则调用该Runnable对象的run方法,否则,该方法不执行任何操作并返回。
start方法:使该线程开始执行,虚拟机开始调用该线程的run方法;该方法有两个作用:启动线程;调用run方法。

发现运行结果每一次都不一样,因为多个线程都获取cpu的执行权,cpu执行到谁,谁就运行。
明确一点,在某一时刻,只能有一个程序在运行。(多核除外。)cpu在做着快速的切换,以达到看上去是同时运行的效果。

我们可以形象的把多线程的运行认为是在互相抢夺cpu的资源(执行权)。这就是多线程的一个特性:随机性。
谁抢到谁执行,至于执行多长时间是cpu说了算。

为什么要重写run方法?
Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法。
也就是说,Thread类中的run方法是用于存储线程要运行的代码。
开启线程是为了运行指定的代码,父类提供了空间,如果有代码,怎样做才能被运行到?沿袭父类功能,复写父类内容。
start方法编译的时候走的是父类的方法,但是运行的时候是调用子类的run方法。

线程的运行状态:
1.被创建:创建对象;
2.运行:start();
3.冻结:(放弃执行资格)
    睡眠:sleep(time);时间到自己就回到运行状态了;
    等待:wait();回来临时状态的方式:notify();//唤醒之后不一定会立刻执行,在特殊状态等待获取cup的执行权。
4.消亡:
    方式一:stop();
    方式二:run方法结束;
5.临时状态或者阻塞状态:(特殊状态);
    具备运行资格但是没有执行权;
    
获取线程的对象以及名称:
线程都有自己的名称:Thread-序号;
获取线程的名称:getName();
自定义线程名称:
    1.用setName方法进行设置
    2.通过构造函数中的super(name);去找父类的方法即可;
获取当前线程对象:
    static Thread currentThread()//该方法是静态的,返回值类型是Thread,即返回的是Thread对象;

class Demo extends Thread{//private String name;Demo(String name){//this.name=name;super(name);//找父类即可;}public void run(){//覆盖run方法;for(int x=0;x<60;x++){//获取线程的名称;System.out.println(Thread.currentThread().getName()+"--demo run----"+x);System.out.println((Thread.currentThread()==this)+"..."+this.getName()+"--demo run----"+x);//验证获取的是不是当前的name;//两种方法获取的是一样的;}}}public class Thread_0 {public static void main(String[] args){/*for(int x=0;x<4000;x++){System.out.println("Hello World");}*///创建一个对象,就创建好一个线程;Demo d1=new Demo("Number_1");//名称怎样打印出来?Demo d2=new Demo("Number_2");d1.start();d2.start();//d.run();//线程创建了但没有开启,按代码顺序执行,不会出现交叉执行的情况。for(int x=0;x<60;x++){System.out.println("Hello World----"+x);}/* Thread t=new Thread(); t.start();//start方法调用run方法,但是Thread类的run方法里面没有可以运行的代码;  */}}

二、线程同步

如何找到问题(哪些代码要同步,哪些代码不需要同步,一定要搞清楚):
        1.明确哪些代码是多线程运行代码;
        2.明确共享数据;
        3.明确多线程运行代码中,哪些语句是操作共享数据的。

同步代码块和函数在封装代码上有什么区别?
        同步代码块在封装代码的时候带有同步的特性。

同步函数:
如何让函数具备同步性?
把synchronized当做关键字放函数上即可;

/*需求:银行有个小金库,有两个储户分别存300元,每次存100元,存3次。*/class Back{private int sum;//Object obj=new Object();public synchronized void add(int n) //throws InterruptedException{//synchronized(obj)//加锁;//{sum=sum+n;try {Thread.sleep(10);}catch (InterruptedException e) {e.printStackTrace();}System.out.println("sum="+sum);//}}}class Cus implements Runnable{private Back b=new Back();public void run(){for(int x=0;x<3;x++)//x每个线程里面都有,不需要同步;{b.add(100);}}}public class SynchronizedFunction {public static void main(String[] args){Cus c=new Cus();Thread t1=new Thread(c);Thread t2=new Thread(c);t1.start();t2.start();}}

三、线程死锁

造成线程死锁的原因:同步中嵌套同步,锁却不一样。

class DeadLock implements Runnable{private static int ticket=100;Object obj=new Object();boolean flag=true;public void run(){if(flag){while(true){//同步代码块:synchronized(obj){show();}}}else{while(true)this.show();}}//同步函数:public synchronized void show(){//同步中有同步代码块;synchronized(obj){if(ticket>0){try{Thread.sleep(10);}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"---sellcode:"+ticket--);}}}}//---------------------------------------------------------------class TestDeadLock implements Runnable{private boolean flag;TestDeadLock(boolean flag){this.flag=flag;} public void run() { if(flag) {  while(true) { synchronized(MyLock.lock_1) { System.out.println("if lock_1"); synchronized(MyLock.lock_2) { System.out.println("if lock_2"); } } } } else { while(true) { synchronized(MyLock.lock_2) { System.out.println("else lock_2"); synchronized(MyLock.lock_1) { System.out.println("else lock_1"); } } } } } }class MyLock{static Object lock_1=new Object(); static Object lock_2=new Object();}public class DeadLockDemo {public static void main(String[] args){/*DeadLock t=new DeadLock();Thread t1=new Thread(t);Thread t2=new Thread(t);t1.start();try{Thread.sleep(10);}catch(Exception e){}//让主线程先停一会;t.flag=false;//切换;t2.start();*///----------------------------------------------------------Thread t1=new Thread(new TestDeadLock(true));Thread t2=new Thread(new TestDeadLock(true));t1.start();try{Thread.sleep(10);}catch(Exception e){}//让主线程先停一会;t2.start();}}

四、线程间的通信

其实就是多个线程在操作同一个资源,但是操作的动作不同

等待唤醒机制:
        wait();必须标出线程所在的锁;
        notify();
        notifyAll();
全部定义在同步中,因为要对持有监视器(锁)的线程进行操作,只有同步才有锁的概念。

为什么上面的专门操作线程的方法被定义在Object类中?
锁是任意对象,任意对象都能调用的方法调用在Object中。这些方法在操作同步中线程时,都必须要标识它们所操作的线程所持有的锁。
只有同一个锁上的被等待线程可以被同一个锁上的notify唤醒,不可以对不同锁中的线程进行唤醒。
等待和唤醒必须是同一个锁,而锁可以是任意对象,可以被任意对象调用的方法定义在Object中。

class Res1{private String name;private String sex;private boolean flag=false;public synchronized void set(String name,String sex){if(flag)try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}this.name=name;this.sex=sex;flag=true;this.notify();}public synchronized void out(){if(!flag)try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name+"------"+sex);flag=false;this.notify();}}class Input1 implements  Runnable{private Res1 r;Object obj=new Object();Input1(Res1 r){this.r=r;}public void run(){int x=0;//synchronized(r)//{while(true){//if(r.flag)//try {r.wait();} catch (InterruptedException e) {e.printStackTrace();}if(x==0){//r.name="Mike";//r.sex="male";r.set("Mike", "male");}else{//r.name="凯瑟琳";//r.sex="女";r.set("凯瑟琳", "女");}x=(x+1)%2;//r.flag=true;//r.notify();}//}}}class Output1 implements  Runnable{private Res1 r;Object obj=new Object();Output1(Res1 r){this.r=r;}public void run(){while(true){//synchronized(r)//?????????????????????????????//{//if(!r.flag)//try {r.wait();} catch (InterruptedException e) {e.printStackTrace();}//System.out.println(r.name+"------"+r.sex);//r.flag=false;//r.notify();//notifyAll();唤醒线程池中的所有线程。//}r.out();}}}public class ThreadCommunication_1{public static void main(String[] args){Res1 r=new Res1();new Thread(new Input1(r)).start();new Thread(new Output1(r)).start();/*Input1 i=new Input1(r);Output1 o=new Output1(r);Thread t1=new Thread(i);Thread t2=new Thread(o);t1.start();t2.start();*/}}
thread类中的join方法:等待该线程终止。
当进行多线程运算的时候,一个线程在运算过程中,可以临时加入一个满足条件的线程,让这个线程运算完,然后另外一个线程再继续运行。

Thread类中的toString方法:覆盖了Object中的toString方法。
返回该线程的字符串表现形式,包括线程名称、优先级和线程组。

优先级代表着抢资源的频率。
优先级一共就10级(1-10),默认是5.

Thread类中的yield方法:暂停当前正在执行的线程对象,并执行其他线程。减缓线程执行的频率,临时释放执行权。

class Join implements Runnable{public void run(){for(int x=0;x<70;x++){//System.out.println(Thread.currentThread().getName()+"......"+x);System.out.println(Thread.currentThread().toString()+"......"+x);Thread.yield();//减缓线程执行的频率,临时释放执行权;}}}public class ThreadJoin{public static void main(String[] args){Join j=new Join();Thread t1=new Thread(j);Thread t2=new Thread(j);t1.start();/*try {t1.join();//抢夺cup执行权;此时主线程处于冻结状态。} catch (InterruptedException e1) {e1.printStackTrace();}*///t1.setPriority(Thread.MAX_PRIORITY);//静态常量;t2.start();for(int x=0;x<80;x++){//System.out.println(Thread.currentThread().getName()+"......"+x);}System.out.println("over");//主函数结束;}}

五、停止线程

1.定义循环结束标记
        因为线程运行代码一般都是循环,只要控制了循环即可;
2.使用interrupt(中断)方法
        该方法是结束线程的冻结状态,使线程回到运行状态中来。

stop方法已过时,不再使用。如何停止线程呢?
只有一种方法:run方法结束。开启多线程运行,运行代码通常都是循环结构,只要控制住循环,就可以让run方法结束,也就是让线程结束。

特殊情况:通过控制循环也无法将线程停下,同步synchronized
线程处于冻结状态,就不会读取标记,线程就不会结束。

这时候怎么办?
Thread类中提供了一个interrupt方法,强制中断线程,该方法是结束线程的冻结状态,是线程回到运行状态中来。
interrupt方法不是停止线程,stop方法才是停止线程的。
当没有指定的方式让冻结的线程恢复到运行状态时,需要对冻结进行清除,强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。
 
thread类中的setDaemon方法:将该线程标记为守护线程或者用户线程。当正在运行的线程都是守护线程时,Java虚拟机退出。
        该方法必须在启动线程前调用。
        该方法首先调用该线程的checkAccess方法,且不带任何参数,这可能抛出SecurityException(在当前线程中)。

当把某些线程标记为后台线程后,它就具备了某些特殊的含义:
        后台线程的特点是,开启后和前台线程共同争夺cpu的执行权。
        当所有的前台线程都结束后,后台线程会自动结束。
class StopThread implements Runnable{private boolean flag=true;public synchronized void run(){while(flag){try {wait();} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName()+":Exception.");flag=false;//程序可以结束了;}System.out.println(Thread.currentThread().getName()+"......run");}}public void changeflag(){flag=false;}}//主函数public class ThreadStop{public static void main(String[] args){StopThread st=new StopThread();Thread t1=new Thread(st);Thread t2=new Thread(st);//守护线程:t1.setDaemon(true);t2.setDaemon(true);t1.start();t2.start();int num=0;while(true){if(num++==60){//st.changeflag();//t1.interrupt();//t2.interrupt();break;}System.out.println(Thread.currentThread().getName()+"------main");}}}

0 0
原创粉丝点击