你的位置:首页 > Java教程

[Java教程]Thread线程的方法用法,有代码例子,简单易懂



/**
 *  程序 :
 *      就是指我们写的保存硬盘当中的静态文件,叫程序
 *  进程 :  
 *      就是程序被加载进内存,准备或者正在运行时,就叫进程
 *  线程 :
 *      就是程序中不同的执行路径
 *
 *  线程 :
 *      当我们程序开始执行的时候,就是java中main()方法开始执行,在栈内存中开辟main()栈帧的时候,就是开启了一个线程
 *      一个线程就是一个栈及其里面的链式栈帧,一个栈帧就对应一个方法.
 *  
 */
☆☆☆☆ 重点 ☆☆☆☆ !
    创建一个新的线程有两种方式,
    但是启动一个线程,只有一种方式(唯一的一种);
    //三个要点 :
            线程的定义;
            线程的启动;
            线程开启之后;
       // 线程的定义 :
                程序不同的执行路径,从代码角度来讲,线程就是一个类,从内存角度来讲,线程就是一个栈和链式栈帧
       // 线程必须要先启动 :
           start();//成员方法
       // 一旦开启多线程 :
                这个时候,说代码是从左到右,从上往下执行这句话就不准确了,因为,我们开启了两个线程,在每个线程的内部,仍然是从上到下,从左到右,但是线程之间的代码,没有固定的先后顺序,他们执行的先后顺序,是不确定的,和CPU,操作系统,和我们自己的设定,都相关

Thread :    
    //创建线程
    创建一个新的线程,有两个方式 :
        //第一种 :
                第1步 : 继承java.lang.Thread;这个类
                第2步 : 重写run()方法,为什么要重写?因为run()就相当于新线程的main()方法,就是程序执行的起点和终点;

            //创建了线程,不启动,不能用 : start();
        //开启线程 :
            Thread t = new 子类名();
            t.start();//就开启线程了
Runnable :

        //第二种 :
                第1步 : 实现java.lang.Reunnable接口
                第2步 : 实现接口的run()方法;
        //开启线程 :
            1 Thread t = new Thread(new 子类名());
               t.start();//开启线程
            2 Runnable(或者是子类名) r = new 子类名();//多态
              Thread t = new Thread(r);
              t.start();//开启线程

Thread_start :
        start();//启动线程(唯一的方法) 默认会去调用该线程的run()方法 成员方法
        启动一个线程 : 只能是Thread类中的start()方法;

currentThread :
        
        currentThread();//获取当前线程的内存地址 静态方法,类名调用就行 对象名也行,底层会默认转换为类名
        Thread.currentThread();//获取当前线程的内存地址(在哪个线程写的,Thread就是哪个线程)

setName :
    
        setName();//给线程取名字,不设置默认是Thread-0,依次累加
        Thread t1 = new 子类名();//默认的名字是在new的时候取得 Thread-0
        t1.setName("t1");//现在,线程名字就改为t1了
        t1.start();
        
getName :
        
        getName();//获得当前线程的名字  成员方法
        Thread.currentThread().getName();//因为getName()是成员方法,所以想调用这个方法,要先获得当前线程的内存地址吧,就是Thread.currentThread();

sleep :
    
    sleep : 在那个线程写的,就锁定那个线程,和 用哪个线程的引用调用sleep()没有关系(因为底层也会默认转成类名)
    //让某个线程休眠 可以直接类名调用就行了
    //线程中,加了sleep(),就必须抛异常或者捕捉异常
    sleep();//让当前线程睡眠指定的毫秒数  是静态方法 类名调用就行
    Thread.sleep(1000);//让该线程睡眠 1 秒;写在那个线程,就睡眠那个线程

setPriority :
    
    setPriority();//设置线程的优先级  成员方法
    线程的默认优先级 :
        //子线程默认继承父类的线程优先级
        java中线程的优先级有0-10 11个级别
        win当中是1-7 7个优先级
        MIN_PRIORITY : 1  //最低
        MAX_PRIORITY : 10 //最大
        NORM_PRIORITY : 5 //正常

        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);//(设置当前线程的优先级)成员方法要用对象引用去调用吧.先获取当前线程的内存地址,调用setPriority();去设置优先级 这里设置的为 1 最低的  不设置默认为 5 NORM_PRIORITY
        Thread t = new 子类名();
        t.setPriority(Thread.MAX_PRIORITY);//这和上面那个是一样的,只不过上面那个,写在哪,就设置那个线程的优先级,这个是设置指定的那个线程的优先级

//唤醒休眠的两种方法 :
    
    1 Interrup();//异常唤醒,需要捕捉和抛异常
    2 while();//循环

        Interrup :
            Interrup();//这种方式唤醒,一定要try...catch..处理异常或者是throws抛异常  
            //异常类型 : InterruptedException

            Thread t = new 子类名();//这种是继承Thread并覆写run()方法的,这种创建线程方式
            Thread t = new Thread(new 子类名());//这种是实现Runnable接口,并实现run()方法的,这种创建线程方式
            t.start();//启动线程
            t.interrupt();//强行唤醒(异常唤醒)

        while :
           用while循环(或者别的循环,加if),
           需要强行唤醒,就在主线线程设置 引用(对象名).run=false;就行了
           boolean run = true;//成员变量
           int i =0;//成员变量
           public void run(){//线程类的run()方法
               while(run){//循环 这里如果为true 就会一直循环(死循环把)
                   i++;
        MIN_PRIORITY : 1
                   try{
                       Thread.sleep(1000);
                   }catch(Exception e){
                       e.printStackTrace();
                   }
                   System.out.println( Thread.currentThread().getName()+"--->"+i );
               }
           }

join :
    
    join();//也就是线程的合并,把两个线程,合并成一个线程(成了单线程)  成员方法
    等着当前线程执行完毕,然后再在本线程后面继续往下执行,当前行以后,所有的main线程的代码,都必须等着这个线程执行完毕以后再执行,相当于这个线程和main线程合并,变为单线程程序,又是从上往下执行
        Thread t = new 子类名();
        t.start();//启动线程
        t.join();//这行一下的main线程的代码,都必须等待t执行完,才能继续执行

yield :
    
    yield();//静态方法 可以直接类名调用,让当前线程让位一次(只跟同级)
            1 这是一个静态方法,一维这:即使用对象调用他,也会在底层转换为类名调用,停掉的也还是当前所在的线程,也就是说在那个类中调用,就停止那个线程
            2 给同一个优先级的线程让位,不同的优先级,不让位
            3 和sleep()方法相同,那个线程调用就停止那和累的线程,但是没有时间,不固定,只是让出当前这个时间片,下一个时间片让不让,另说

            Thread.yield();//跟同级线程,让位一次 写在哪,哪个就让位一次
        
synchronized: :
    
        synchronized : 并不是往对象里面加锁,锁是每个对象都有的属性,synchronized只是锁上了这把锁,是个持续的动作而已
        synchronized 是一个修饰符,修饰的方法,等于是给方法加了一把钥匙,当一个线程执行这个方法的时候,别的所有线程都不能再去执行这个方法,和用synchronized修饰的方法,一个方法锁住,所有用synchronized修饰的方法都会锁上,但是没有用synchronized修饰的方法,可以访问//目的就是让数据同步
        //线程同步本质 :
       当多个线程(服务,任务),操作同一个数据的时候,保证数据一致,本质就是数据同步,是一种数据安全机制
       这种机制,操作上更多是从数据上来保护的
   //异步编程模型 :
       线程是完全独立的,谁的运行也不会受到别的线程的影响
   //同步编程模型 :
       线程间不是独立的,互相间有影响的,某些线程必须单独完成任务以后,才能 够让别的线程执行(为什么?)
       
       //同步的原因 :
           1 同步是指数据的同步,为了数据安全,必须要等某个线程对数据操作完成以后,在引入别的线程进行操作
               同步机制,某种程度是多线程编程变成了单线程
           2 同步的环境
               1 必须多线程
               2 多个线程有同时操作同一个数据的可能性
               3 主要就是涉及数据更改的操作
       线程同步是数据的概念
       //方法 : 锁定,锁定是一个逻辑上的概念,是为了保证同步的手段
       //方发锁 : 按照封装性,根本不可能再是直接就能对数据进行操作只能通过方法进行操作
       //对象锁 : 堆内存,只要一加锁,谁也进不来
       //类锁 : 更大级别的
       只要加了 synchronized 修饰的成员方法(静态方法),是多个线程不可以用加了synchronized的方法同时访问这个对象(类)
   同一个方法互斥,不同的方法也互斥

    //静态方法声明 :
            又称为类锁;
            public synchronized static void m1(){
             System.out.println( "不管谁被锁,我都会被锁,他解开,我才解开" );   
            }
            public synchronized static void m2(){
                System.out.println( "不管谁被锁,我都会被锁,它解开,我才解开" );
            }
            public static void m3(){
                System.out.println( "别人锁不锁,干我何事" );
            }
        比如我有两个线程,t1和t2把
            t1调用了m1();/*就会锁住*/ t2就不能调用m1()和m2();/*m2()也会被锁住*/只能调用m3();//因为m3()没有用synchronized修饰
    
    //成员方法声明 :
            又称对象锁
            前提是/*对象*/必须是同一个对象
             public synchronized  void m1(){
             System.out.println( "不管谁被锁,我都会被锁,他解开,我才解开" );   
            }
            public synchronized  void m2(){
                System.out.println( "不管谁被锁,我都会被锁,它解开,我才解开" );
            }
            public  void m3(){
                System.out.println( "别人锁不锁,干我何事" );
            }
            比如我有两个线程,t1和t2把//并且t1线程和t2线程都是同一个对象
            t1调用了m1();/*就会锁住*/ t2就不能调用m1()和m2();/*m2()也会被锁住*/只能调用m3();//因为m3()没有用synchronized修饰

setDaemon :
    
        setDaemon();//设置守护线程(兜底线程)
        只要主线程执行完毕,不管守护线程执行了多少,执行到哪,都不会在执行,也跟着死去,但是跟主线程晚结束一下,
            //活,他先活,死,他先死(要么怎么守护,守护线程,先活,后死)
        Thread t1 = new 子类名();//创建一个线程把,用的是继承的方式
        t1.setDaemon(true);//里面是布尔型 true/false  把当前线程设置为守护线程
        t1.start();//启动t1线程把
        //如果我守护线程里面有个死循环(就是不会结束的那种),但是,只要主线程执行完毕,守护线程也会跟着结束

Timer :
    
        1 创建一个定时器(Timer)
        2 创建一个执行定时器任务的类,这个类必须继承于 TimerTask 这个类,并且,必须覆写 TimerTask 类中的 run(),方法.(TimerTask)
        3 用 Timer 类里面的 schedule()方法,分配并执行定时器任务(schedule)
    Timer :
        Timer t = new Timer();//创建一个定时器,并且调用Timer类里面的 schedule()方法,分配执行的任务
    
    TimerTask :
        class 类名 extends TimerTask{
            public void run(){
                 System.out.println( new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()) );//获取当前时间,输出
            }
        }
        Date :
            Date d = new Date();//获取当前的时间
            String d1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(d);//以这种形式,输出当前时间d

schedule :

    schedule();//Timer里面的schedule(要执行任务的类对象 , 起始的时间 , 任务执行间隔的毫秒数 );//循环执行方法,默认调用run()方法
        t.schedule(
                new 类名(),/*执行定时器任务的类的对象*/
                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").parse("2015-09-10 14:48:00 000"),/*其实时间(从什么时间,开始执行)*/
                1000*3)/*执行任务的间隔时间*/;


synchronized :
        //死锁
        synchronized : 并不是往对象里面加锁,锁是每个对象都有的属性,synchronized只是锁上了这把锁,是个持续的动作而已
        死锁 : 死锁就是大家都访问不了的状态;
            原理 :
                1 两个线程,两个对象
                2 先后 嵌套 锁定 执行 两个对象(先锁定第一个对象,再嵌套锁定第二个对象)
                3 先后 嵌套 锁定 执行 两个对象(先锁定第二个对象,再嵌套锁定第一个对象)
                4 在第一个线程执行过程中,执行到第二个对象的时候,发现第二个被锁死了,也只能等待
                5 第二个线程执行过程中,执行到第一个对象的时候,发现第一个被锁死了,也只能等待

    public class Thread线程{
        public static void main(String[] args){

        //这里我用Object是因为不用再写实体类了,这个类可以自己写
        Object o1 = new Object();//第一个对象
        Object o2 = new Object();//第二个对象

        Thread t1 = new Thread(new T1(o1,o2));//第一个线程(调用的是有参构造把)
        Thread t2 = new Thread(new T2(o1,o2));//第二个线程
        t1.start();//启动线程
        t2.start();//启动线程

    }
}


class T1 implements Runnable{//线程类吧
    Object o1;//成员变量 这里用于接收实参传进来的Object的对象引用(实体类用的那个类,这里就要用那个类去接吧)
    Object o2;
    T1(Object o1,Object o2){//有参构造
        this.o1 = o1;//赋值
        this.o2 = o2;
    }
    public void run(){//覆写的run()方法
        synchronized(o1){//锁定第一个对象 (o1)
            try{
                Thread.sleep(1000);//不睡眠一秒,有时候不死锁,睡眠一秒,百分百死锁(因为如果是我t1线程执行完了,t2线程在开始执行的呢?那就不是死锁了吧,这样,t1到这停一秒,一秒足够t2也走的o2的那个对象那里,所以就死锁了,都在等待)
            }catch(Exception e){
                e.printStackTrace();
            }
            synchronized(o2){//在锁定第一个对象(o1)中 嵌套锁定第二个对象(o2)
                System.out.println( "t1" );//如果,t1执行完成了,就会输出 但是如果死锁了,t1就进不来吧 就不会输出
            }
        }
    }
}
class T2 implements Runnable{//创建线程类
    Object o1;//成员变量 这里用于接收实参传进来的Object的对象引用(实体类用的那个类,这里就要用那个类去接吧)
    Object o2;
    T2(Object o1, Object o2){//和上面一样,不懂就看上面的注释
        this.o1 = o1;
        this.o2 = o2;
}
        public void run(){
            synchronized(o2){//t1线程先锁定的o1,我这里如果先锁定o2,没有关系吧,t1能进o1,但是我也能进o2 ,因为是不同的对象,但是我如果再想进o1,是不是就进不去了(因为t1进去就锁了)
            try{
                Thread.sleep(1000);//这个也停一秒,就是当t2进来o2这个对象,并锁定了,t1也肯定就能进去o1这个对象,并也锁定了,现在就开始进入等待了吧,就是死锁
            }catch(Exception e){
                e.printStackTrace();
            }
            synchronized(o1){//嵌套锁定o1
                System.out.println( "t2" );
            }
        }
   }
}

wait :

    wait是 Object 类的方法 ,不是Thread中的方法 Thread中wait也是继承于 Object

    注意 : 如果该对象没有被锁定,则调用wait方法,就会报错,即只有在同步方法或者同步代码块中(也就是某线程访问,用 synchronized 修饰的方法之后)才可以调用wait方法,notify,notifyAll同理

    this.wait();不是让当前对象wait,而是让当前锁定this对象的线程wait,同时 释放对this的锁定

    //wait()和sleep()的区别 :

            wait : wait()会把锁打开,让别的线程进来,并且自身进入睡眠,只能等待唤醒,如果不唤醒,会一直休眠(并且,当前线程wait()下面的代码不会执行,只能等待唤醒,才能执行)

            sleep : sleep()就算设置了休眠,但是锁也不会打开,别的线程也进不来,并且可以唤醒,但是如果不唤醒,到了指定的睡眠时间,自身也会唤醒(并且,当前线程sleep()下面的代码不会执行,只能等待唤醒,才能执行)

        this.wait();//如果这个休眠,会打开锁,让别的线程执行
        System.out.println( "我在睡眠" );//不会执行,如果没有线程唤醒,永远不会执行,会永远睡眠

        Thread.sleep(5000);//睡眠5秒,并且不会打开锁,别的线程也得等着,一样进不来
        System.out.println( "我快唤醒了" );//如果没有线程唤醒当前睡眠的线程,那么,5秒后,自动醒,一样会输出


notify :

        notify是Object 类的方法 ,不是Thread中的方法 Thread中notify也是继承自Object

        注意 : 如果该对象没有被锁定,则调用wait方法,就会报错,即只有在同步方法或者同步代码块中(也就是某线程访问,用 synchronized 修饰的方法之后)才可以调用wait方法,notify,notifyAll同理

        notify() : 随机唤醒一个在该对象上睡眠的某一个线程(一般都是谁先睡眠,谁先醒,具体不知道,是系统随机唤醒的),并且,没有让自身进入睡眠状态
            this.notify();//随机唤醒一个在该对象上睡眠的线程
            System.out.println( "我能输出" );//我能输出,因为上面只是唤醒了一个线程,并没有让自身进入睡眠状态

notifyAll :

        notifyAll是Object 类的方法 ,不是Thread中的方法 Thread中notifyAll也是继承自Object

        注意 : 如果该对象没有被锁定,则调用wait方法,就会报错,即只有在同步方法或者同步代码块中(也就是某线程访问,用 synchronized 修饰的方法之后)才可以调用wait方法,notify,notifyAll同理

        notifyAll() : 唤醒在该对象上,所有等待的线程,并且,没有让自身进入睡眠状态
            this.notifyAll();//唤醒所有在该对象上睡眠的线程
            System.out.println( "我能输出" );//我能输出,因为上面只是唤醒了所有线程,并没有让自身进入睡眠状态