线程创建 线程同步 线程状态


第一章 多线程的概念
    1.1线程与进程
    进程:
        是指一个内存中运行的应用程序, 每个进程都有一个独立的内存空间, 一个应用程序可以同时运行多个程序;
        进程是程序的一次执行过程,是系统运行程序的进本单位;
    线程:
        是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程,也可以有多个线程;
    进程与线程的区别:
        进程:有独立的内存空间,进程中数据存放空间(堆内存与栈内存)是独立的,至少有一个线程.
        线程:堆空间是共享的, 栈空间是独立的,线程消耗的资源比进程小得多;
        
第二章 线程的创建--继承方式
    2.1继承Thread类方式
    java使用java.lang.Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例.
    java中通过继承Thread类来创建并启动多线程的步骤如下:
        1. 定义Thread类的子类, 并重写该类的run()方法;  该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体;
        2. 创建Thread子类的实例, 即创建了线程对象
        3. 调用线程对象的start()方法来启动该线程(即调用Thread类的start方法);
    代码实例:


        自定义线程类: 

 public class MyThread extends Thread{
            /*
            重写run方法, 完成该线程程序执行的逻辑
            */
            @Override
            public void run(){
                for(int i = 0; i< 200; i++){
                    System.out.println("自定义线程正在执行"+i);
                }
            }
        }
        测试类:
        public static void main(String[] args){
            //创建自定义线程对象
            MyThread mt = new MyThread();
            //开启新线程
            mt.start();
            //在主方法中执行for循环
            for(int i = 0; i< 200;i++){
                System.out.println("main线程"+i);
            }
        }


        
    2.2 run()方法和start()方法
        run()方法:
            是线程执行的任务方法,每个线程都会调用run()方法执行,我们将线程要执行的任务代码都写在run()方法中就可以被线程调用执行。
        start()方法:
            开启线程,线程调用run()方法;
        注意: 一个线程只能被启动一次!!
        
    2.3 线程名字的设置与获取
        String getName()可以获取到线程的名字。
        setName(String name)设置线程的名字。
        通过Thread类的构造方法Thread(String name)也可以设置线程的名字
        
        获取运行main方法线程的名字:
        static Thread CurrentThread() 获取当前线程对象
        代码实例:

 public class MyThread extends Thread{
            public void run(){
                System.out.println("线程名称:"+getName());
                System.out.println("线程名称:"+Thread.CurrentThread().getName());
                
            }
        }
         测试类:
         public class Demo{
            public static void main(String[] args){
                //创建自定义线程对象
                MyThread mc= new MyThread();
                //设置线程名字
                mc.setName("旺财");
                //开启新线程
                mc.start();
            }
        }


        注意:线程是有默认名字的,如果我们不设置线程的名字,JVM会赋予线程默认名字Thread-0,Thread-1;
        
    为什么要继承Thread类?
        Thread类就是线程类, 具有线程最基本的属性与功能; 比如  设置线程名  开启线程
        继承Thread类  就可以直接使用这些功能;
    那为什么不直接创建Thread类对象 开启线程?
        Thread t = new Thread();
        t.start();
        这样虽然开启了线程  但是运行的是Thread类的main方法, 这个run方法中执行的内容
        并不是我们想要的, 所以我们继承Thread类 重写run方法 这样运行的就是我们自己的run方法;
        
        
第三章 线程的创建--实现方式
    3.1 线程创建的第二种方式  实现Runnable接口
        1. 定义实现类, 实现 Runnable接口
        2. 重写run方法(线程任务)
        3. 开启线程
            创建实现类对象
            创建Thread对象 传入线程任务
                    public Thread(Runnable r)
            调用Thread的start方法  开启线程
    代码实例:
      

  public class MyRunnable implements Runnable{
            public void run (){
                for(int i = 0; i < 20; i++){
                    System.out.println(Thread.currentThread().getName()+" "+i);
                }
            }
        }
        测试类:
        public class Demo{
            public static void main(String[] args){
                //创建(自定义类)实现类对象, 线程任务对象
                MyRunnable mr = new MyRunnable();
                //创建线程对象(将要执行的线程任务 传递给Thread对象)
                Thread t = new Thread(mr);
                t.start();
                for(int i = 0; i < 20; i++){
                    System.out.println(" main "+i);
                }
            }
        }


    为什么有了继承的方式 还要有实现接口的方式?
        1. 实现Runnable接口的方式 避免了单继承的局限性
                如果一个类已经有了父类  还想开线程 只能通过实现接口的方式
        2. 解耦合 线程任务与线程对象分离
        3. 更符合面向对象 更容易实现 多个线程共享一个任务
        
    3.2 匿名内部类方式创建线程
        使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法
        
        代码演示:

 public class NoNameInnerClassThread {
            public static void main(String[] args) {           
        //        new Runnable(){
        //            public void run(){
        //                for (int i = 0; i < 20; i++) {
        //                    System.out.println("张宇:"+i);
        //                }
        //            }  
        //           }; //---这个整体  相当于new MyRunnable()
                Runnable r = new Runnable(){
                    public void run(){
                        for (int i = 0; i < 20; i++) {
                            System.out.println("张宇:"+i);
                        }
                    }  
                };
                new Thread(r).start();
        
                for (int i = 0; i < 20; i++) {
                    System.out.println("费玉清:"+i);
                }
            }
        }


        
第四章 Thread类API    
    4.1 睡眠sleep方法
        public static void sleep(long time); 让当前线程进入到睡眠状态,到毫秒后自动醒来继续执行
    代码演示:
 

       public class Test{
            public static void main(String[] args){
                for(int i = 1;i<=5;i++){
                    Thread.sleep(1000);
                    System.out.println(i)   
                } 
            }
        }


    4.2 设置线程优先级(了解)
        线程的优先级被划分为10级,值分别为1-10,其中1最低,10最高.线程提高了3个常量来表示最低,最高,以及默认优先级:
            public static final int MIN_PRIORITY   //1 最低优先级
            public static final int NORM_PRIORITY  //5 默认优先级
            public static final int MAX_PRIORITY   //10 最大优先级
            
            public final void setPriority(int newPriority)更改线程的优先级。 
        
    4.3 用户线程与守护线程(了解)    
        用户线程与守护线程
            我们正常写的代码都是用户线程
        守护线程
            用户线程存在 守护线程可以执行 用户线程执行完  守护线程即使没执行完 jvm也直接退出
            GC就是一个守护线程
            
        public final void setDaemon(boolean on) on的值为true将线程设置为守护线程, 注意一定要在开启线程之前设置
        
    代码演示:

public class Test02 {
            public static void main(String[] args)  {
                Thread t1 = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 100; i++) {
                            try {
                                Thread.sleep(200);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
        
                            System.out.println("t1   "+i);
                        }
                    }
                });
                //如果不设置t1为守护线程 由于t1睡眠时间比t2睡眠时间长
                //t1一定是后执行完 ,当t2全部执行结束后,t1再慢慢将剩下的执行完
                //但是如果将t1设置为守护线程 当t2执行完毕后,t1也不再执行了 直接退出jvm
                //t1.setDaemon(true);//设置t1为守护线程
                t1.start();
                Thread t2 = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 100; i++) {
                            try {
                                Thread.sleep(50);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            System.out.println("t2 "+i);
                        }
                    }
                });
                t2.start();
            }
        }


        
    4.4 join方法    
        join方法是让 当前线程等待,调用方法的线程进行插队先执行,执行完毕后,在让当前线程执行.
        对其他线程没有任何影响.注意 此处的当前线程不是调用方法的线程 而是Thread.currentThread().
        
    代码演示:

  public class Test {
            public static void main(String[] args) throws InterruptedException {
                Thread t1 = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 10; i++) {
                            System.out.println("t1   "+i);
                        }
                    }
                });
                Thread t2 = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 10; i++) {
                            System.out.println("t2   "+i);
                        }
                    }
                });
                t1.start();
                //注意 此时当前线程是main线程
                //main线程进入等待,让t1线程执行. 由于此时main等待 所以t2还没有执行
                //t1执行完毕后 main和t2抢夺cpu资源 执行
                // t1.join();
                t2.start();
                //如果t1.join放在这里 那么main进入到等待 t2已经开启了
                //t1 t2互相抢夺资源
                //main一定是在 t1执行完毕后 才会执行
                //那么就会有两种情况  t1执行完毕  t2没执行完 main和 t2进行抢夺
                //t1执行完毕  t2也执行完毕了  main自己执行
                //t1.join();
                for (int i = 0; i < 10; i++) {
                    System.out.println("main "+i);
                }            }
        }


        
    面试题:
    现在有T1,T2,T3 三条线程,同时开启,如何保证T2在T1执行完毕后执行,T3在T2执行完毕后执行?    
    
    代码实例:

  public class Test02 {
            public static void main(String[] args) throws InterruptedException {
                Thread t1 = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 10; i++) {
                            System.out.println("t1   "+i);
                        }
                    }
                });
                Thread t2 = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            //如果t2抢到,等待 让t1先执行
                            t1.join();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        for (int i = 0; i < 10; i++) {
                            System.out.println("t2   "+i);
                        }
                    }
                });
                Thread t3 = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            //如果t3抢到,等待 让t2先执行
                            t2.join();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                        for (int i = 0; i < 10; i++) {
                            System.out.println("t3   "+i);
                        }
                    }
                });
                t1.start();
//                t1.join();
                t2.start();
//                t2.join();
                t3.start();
            }
        }

    4.5 线程停止(了解)
        public final void stop()  直接中断线程 此方法已过时 不安全
        public boolean isInterrupted() 获取线程是否中断的状态 如果中断返回true 没中断返回false
        public void interrupt()  中断线程 此时调用interrupted方法 会返回true  

 代码演示:

 public class Test02 {
            public static void main(String[] args)  {
                Thread t1 = new Thread(new Runnable() {
                    @Override
                    public void run() {

                        for (int i = 0; i <1000000 ; i++) {
                            System.out.println(i);
                            //是否有人要中断线程 如果有返回true 如果没有返回false
                            //让线程中断更为平滑 可以使用代码来控制中断
                            boolean b = Thread.currentThread().isInterrupted();
                            if(b){
                                break;
                            }
                        }
                    }
                });

                t1.start();

                try {
                    Thread.sleep(2000);

//                    t1.stop(); //方法已经过时 不安全
                    t1.interrupt();


                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

第五章 线程安全
    5.1 线程安全问题
    
        线程安全问题引发
            线程安全问题引发:线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,
            一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全

    5.2 线程同步
        当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题。
        要解决上述多线程并发访问一个资源的安全性问题:也就是解决重复票与不存在票问题,Java中提供了同步机制(**synchronized**)来解决。
        三种方法为:
            1.同步代码块
            2.同步方法
            3.锁机制
            
    5.2.1同步代码块
        同步代码块:线程操作的共享数据进行同步。synchronized关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。
        格式:
            synchronized(同步锁){
                需要同步操作的代码
            }
        同步锁:
        同步锁又称为对象监视器。同步锁只是一个概念,可以想象为在对象上标记了一个锁.
        1. 锁对象 可以是任意类型。
        2. 多个线程对象  要使用同一把锁。
        注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着(BLOCKED)。

        代码演示:  

 public class Ticket implements Runnable{
            private int ticket = 100;
            private Object lock = new Object();
            /*
            * 执行卖票操作
            */
            @Override
            public void run() {
                //每个窗口卖票的操作 
                //窗口 永远开启 
                while(true){
                    synchronized (lock) {
                        if(ticket>0){//有票 可以卖
                            //出票操作
                            //使用sleep模拟一下出票时间 
                            try {
                                Thread.sleep(50);
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                            //获取当前线程对象的名字 
                            String name = Thread.currentThread().getName();
                            System.out.println(name+"正在卖:"+ticket--);
                        }
                    }
                }
            }
        }

    5.2.2 同步方法
        同步方法:当一个方法中的所有代码,全部是线程操作的共享数据的时候,可以将整个方法进行同步。使用synchronized修饰的方法,就叫做同步方法,
        保证A线程执行该方法的时候,其他线程只能在方法外等着
    格式:
        public synchronized void method(){
            可能会产生线程安全问题的代码
        }
    使用同步方法代码如下:
      

 public class Ticket implements Runnable{
            private int ticket = 100;
            /*
            * 执行卖票操作
            */
            @Override
            public void run() {
                //每个窗口卖票的操作 
                //窗口 永远开启 
                while(true){
                    sellTicket();
                }
            }
    
            /*
            * 锁对象 是 谁调用这个方法 就是谁 
            *   隐含 锁对象 就是  this
            *    
            */
            public synchronized void sellTicket(){
                if(ticket>0){//有票 可以卖    
                    //出票操作
                    //使用sleep模拟一下出票时间 
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //获取当前线程对象的名字 
                    String name = Thread.currentThread().getName();
                    System.out.println(name+"正在卖:"+ticket--);
                }
            }
        }

    5.2.3 lock锁
        java.util.concurrent.locks.Lock机制提供了比*synchronized*代码块和*synchronized*方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。

        Lock锁也称同步锁,加锁与释放锁方法化了,如下:
            public void lock() `:加锁。
            public void unlock()`:释放锁

        代码演示:
      

 public class Ticket implements Runnable{
            private int ticket = 100;
    
            Lock lock = new ReentrantLock();
            /*
            * 执行卖票操作
            */
            @Override
            public void run() {
                //每个窗口卖票的操作 
                //窗口 永远开启 
                while(true){
                    lock.lock();
                    if(ticket>0){//有票 可以卖
                        //出票操作 
                        //使用sleep模拟一下出票时间 
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        //获取当前线程对象的名字 
                        String name = Thread.currentThread().getName();
                        System.out.println(name+"正在卖:"+ticket--);
                    }
                    lock.unlock();
                }
            }
        }


第六章 线程状态
    6.1 线程状态概述
        当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。
        在API中java.lang.Thread.State这个枚举中给出了六种线程状态:
        
        | 线程状态                | 导致状态发生条件                                             |
        | ----------------------- | ------------------------------------------------------------ |
        | NEW(新建)               | 线程刚被创建,但是并未启动。还没调用start方法。              |
        | Runnable(可运行)        | 线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。 |
        | Blocked(锁阻塞)         | 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。 |
        | Waiting(无限等待)       | 一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。 |
        | Timed Waiting(计时等待) | 同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、Object.wait。 |
        | Teminated(被终止)       | 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。 |

    6.2 等待与唤醒
        Object类的方法
        public void wait() : 让当前线程进入到等待状态 此方法必须锁对象调用.
        
        代码演示:
      

 public class Demo1_wait {
            public static void main(String[] args) throws InterruptedException {
            // 步骤1 : 子线程开启,进入无限等待状态, 没有被唤醒,无法继续运行.
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("begin wait ....");
                        synchronized ("") {
                            "".wait();
                        }
                        System.out.println("over");
                    } catch (Exception e) {
                    }
                }
            }).start();
        }


        
        public void notify() : 唤醒当前锁对象上等待状态的线程  此方法必须锁对象调用.
        代码演示:

 public class Demo2_notify {
            public static void main(String[] args) throws InterruptedException {
                // 步骤1 : 子线程开启,进入无限等待状态, 没有被唤醒,无法继续运行.
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            System.out.println("begin wait ....");
                            synchronized ("") {
                                "".wait();
                            }
                            System.out.println("over");
                        } catch (Exception e) {
                        }
                    }
                }).start();
                //步骤2:  加入如下代码后, 3秒后,会执行notify方法, 唤醒wait中线程.
                Thread.sleep(3000);
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            synchronized ("") {
                                System.out.println("唤醒");
                                "".notify();
                            }
                        } catch (Exception e) {
                        }
                    }
                }).start();
            }
        }


       
  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值