当前位置 : 主页 > 编程语言 > java >

新手了解java 多线程基础知识

来源:互联网 收集:自由互联 发布时间:2021-08-21
目录 一、线程的生命周期 JDK中用Thread.State类定义了线程的几种状态: 二、线程同步 1、为什么要有线程同步 2、synchronized 2.1同步代码块 2.2同步方法 3、Lock锁 四.基本概念 五.多线程的创
目录
  • 一、线程的生命周期
    • JDK中用Thread.State类定义了线程的几种状态:
  • 二、线程同步
    • 1、为什么要有线程同步
    • 2、synchronized
      • 2.1同步代码块
      • 2.2同步方法
    • 3、Lock锁
    • 四.基本概念
      • 五.多线程的创建
        • 六.Thread类方法介绍
          • 总结

            一、线程的生命周期

            JDK中用Thread.State类定义了线程的几种状态:

            要想实现多线程,必须在主线程中创建新的线程对象。Java语言使用 Thread类及其子类的对象来表示线程,在它的一个完整的生命周期中通常 要经历如下的五种状态:

            • 新建:当一个Thread类或其子类的对象被声明并创建时,新生的线程 对象处于新建状态;
            • 就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间 片,此时它已具备了运行的条件,只是没分配到CPU资源;
            • 运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态, run()方法定义了线程的操作和功能;
            • 阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时中止自己的执行,进入阻塞状态;
            • 死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常 导致结束。 在上面五个阶段中,只有新建和死亡是不可重复,其它几个状态都可能改变。

            在这里插入图片描述

            注意:

            1.新建和死亡状态只能有一次;

            2.运行状态只能是从就绪状态变过来的;

            3.阻塞状态不能直接变为运行状态,需要通过就绪状态;

            4.当一个运行状态的线程调用yield()方法后,就会变为就绪状态;

            5.当一个运行状态的线程调用sleep()、等待同步锁方法、wait()方法或 join()方法时,就会处理阻塞状态;

            6.当一个阻塞状态的线程调用notify()/notifyAll()方法,或者sleep()方法 结束,再或者获得同步锁,或者join()线程执行完毕就可以变为就绪状 态的线程

            7.当一个线程执行过多后就处理死亡状态,即线程生命周期结束。

            二、线程同步

            1、为什么要有线程同步

            为了解决线程安全问题,在多线程下,多个线程对一个数据进行修改时,可能会产生数据出错的问题,所以我们就需要通过线程同步的方法来解决问题。

            Java中的线程同步实现方式:

            2、synchronized

            2.1同步代码块

            示例:

            public class MyRunnableTest {
                public static void main(String[] args) {
                    MyRunnable myRunnable = new MyRunnable();
            
                    Thread thread = new Thread(myRunnable,"A");
                    Thread thread1 = new Thread(myRunnable,"B");
                    Thread thread2 = new Thread(myRunnable,"C");
                    Thread thread3 = new Thread(myRunnable,"D");
                    Thread thread4 = new Thread(myRunnable,"E");
                    thread.start();
                    thread1.start();
                    thread2.start();
                    thread3.start();
                    thread4.start();
            
                }
                public class MyRunnable implements Runnable{
                    @Override
                    public void run() {
                        synchronized (Thread.class){
                            System.out.println(Thread.currentThread().getName()+"在过山洞");
                            try {
                                Thread.sleep(300);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
            

            注意: 同步锁可以是任意对象,但该对象必须唯一; 同步代码块所在位置也很重要,同步代码块需要把引起数据问题的所有代码都包裹起,不能多裹,也不能少裹。 我们通常都是使用字节码文件来作为同步锁。

            2.2同步方法

            示例:

            public class MyRunnableTest {
                public static void main(String[] args) {
                    MyRunnable myRunnable = new MyRunnable();
            
                    Thread thread = new Thread(myRunnable,"A");
                    Thread thread1 = new Thread(myRunnable,"B");
                    Thread thread2 = new Thread(myRunnable,"C");
                    Thread thread3 = new Thread(myRunnable,"D");
                    Thread thread4 = new Thread(myRunnable,"E");
                    thread.start();
                    thread1.start();
                    thread2.start();
                    thread3.start();
                    thread4.start();
            
                }
                public class MyRunnable implements Runnable{
                    @Override
                    public void run() {
                        test()
                    }
             public synchronized void test(){
                    System.out.println(Thread.currentThread().getName()+"在过山洞");
                    try {
                         Thread.sleep(300);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            

            **注意:**当使用同步方法来处理多线程的共享数据问题,如果是静态方法,使用的是类名.class方式,如果是非静态方法,使用的是this。

            3、Lock锁

            使用Lock.lock()进行加锁操作,然后使用Lock.unlock()方法来进行 解锁。

            Lock是一个接口,不能直接实例化,需要使用子类来实例化,通常使用的 子类是ReentrantLock。

            public class MyRunnableTest {
                public static void main(String[] args) {
                    MyRunnable myRunnable = new MyRunnable();
                    Thread thread = new Thread(myRunnable,"A");
                    Thread thread1 = new Thread(myRunnable,"B");
                    Thread thread2 = new Thread(myRunnable,"C");
                    Thread thread3 = new Thread(myRunnable,"D");
                    Thread thread4 = new Thread(myRunnable,"E");
                    thread.start();
                    thread1.start();
                    thread2.start();
                    thread3.start();
                    thread4.start();
            
                }
            }
            public class MyRunnable implements Runnable{
                Lock lock = new ReentrantLock();
                    lock.lock();
                    System.out.println(Thread.currentThread().getName()+"在过山洞");
                    try {
                            Thread.sleep(3000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }finally {
                        lock.unlock();
                    }
                    }
                }
            

            说明:

            在需要作同步操作的代码块之前需要使用Lock.lock()方法来作加锁处 理;在同步操作的代码块之后,增加finally语句块来释放锁,释放锁的方法为Lock.unlock()方法;一定要确保锁能释放,否则就是死锁;

            Lock比synchronized更轻量级,功能更强大,如果可以尽量使用Lock。

            四.基本概念

            程序、进程、线程

            • 程序(program)是为完成特定任务、用某种语言编写的一组指令的集 合。即指一段静态的代码,静态对象。
            • 进程(process)是程序的一次执行过程,或是正在运行的一个程序。是 一个动态的过程:有它自身的产生、存在和消亡的过程——具有生命 周期。可以理解为一个正在运行的软件。
            • 线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行 路径。可以理解为一个软件的功能。

            多线程程序的优点:

            • 提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
            • 提高计算机系统CPU的利用率。
            • 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理 解和修改。

            五.多线程的创建

            ​ 在Java中我们可以使用java.lang.Thread类来实现 ,要想我们的类具有多线程的功能,需要让我们的类去继承Thread类,然后重写run()方法,并调用 start()方法来启动多线程。

            示例 1:

            public class MyThread extends Thread{
                public void run(){
                    for (int i = 0; i < 50; i++) {
                        System.out.println("MyThread:"+i);
                    }
                }
            }
            public class MyThreadTest{
                public static void main(String[] args){
                    Thread t1 = new MyThread();
                    t1.start();
                    for (int i = 0; i < 20; i++) {
                        System.out.println("world=====" + i);
                    }
                }
            }
            

            说明:创建一个Java类继承Thread类,并重写父类Thread中的run方法,在run方法中写具体的多线程业务。创建线程类的对象,并调用start方法启动多线程。

            **注意:**多线程的启动调用的是start方法,在jvm底层中start方法内部会调用run方法。

            一个对象只需要调用一次start()方法,如果多次调用则会抛出异常“IllegalThreadStateException”。

            示例 2:

            public class MyRunnable implements Runnable {
                public void run() {
                    for (int i = 0; i < 50 ; i++) {
                        System.out.println( "MyRunnable:"+i);
                    }
                }
            }
            public class ThreadDemo {
                public static void main(String[] args) {
                    Thread thread = new Thread( new MyRunnable());
                    thread.start();// 只有调用Thread类中的start()方法才可以实现多线程
                        for (int i = 0; i < 50; i++) {
                            System.out.println("main:" + i);
                        }
                }
            }
            

            说明:

            • 编写一个类,实现Runnable接口;
            • 重写run()方法;
            • 根据Runnable子类对象来创建Thread对象;
            • 通过Thread对象调用start()方法来启动多线程;

            总结:

            • 继承Thread:重写run()方法,业务代码在run()中。
            • 实现Runnable:线程代码存在接口的子类的run方法。
            • 通过Callable和线程池的方式创建线程。

            **提示:**在应用中我们如果可以使用Runable接口那么就尽量使用,这样可以避免Java单继承的局限。

            六.Thread类方法介绍

            1)currentThread():返回当前正在执行的线程对象的引用。

            2)getName():返回当前线程的名称

            3)isAlive():判断当前线程是否存活

            4)isDeaomon():判断线程是否为守护线程

            5)join():在当前线程中引入另一个线程,而当前线程会被阻塞

            6)sleep():让当前线程进入睡眠状态

            7)yield():让当前线程放弃CPU的执行权,重新进入排队,与其他线程平等争夺CPU执行。

            8)interrupt() 中断线程 9)interrupted() 如果当前线程已经中断,则返回 true;否则返回 false。

            示例:

            public class ThreadTest {
                public static void main(String[] args) {
                    //创建线程并开启线程1
                    Thread thread = new MyThread();
                    thread.start();
                    //创建线程并开启线程2
                    Thread thread1 = new Thread(new MyRunnable());
                    thread1.start();
                    //创建线程并开启线程3
                    MyCallable myCallable = new MyCallable();
                    FutureTask futureTask = new FutureTask(myCallable);
                    new Thread(futureTask).start();
            
                    for (int i = 0; i < 100; i++) {
                        if (i== 50){
                            //在main线程中当i=50加如thread线程,会在thread执行完后才会继续执行main线程剩下的
                            try {
                                thread.join();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("main:"+i);
                    }
                }
            }
            
            public class MyThread extends Thread{
                public void run(){
                    for (int i = 0; i < 100; i++) {
                        // void interrupt() 中断线程 可以理解为线程中断状态有 true | false,每一次调用就是修改状态为true
                        //static boolean interrupted() 如果当前线程已经中断,则返回 true;否则返回 false。
                        Thread.currentThread().interrupt();
                        if (Thread.interrupted()){
                            System.out.println("Thread interrupted");
                        }
                        // static currentThread()  返回对当前正在执行的线程对象的引用
                        //String getName()   返回该线程的名称。
                        //Thread.currentThread().getName()  获取当前线程对象的名称
                        System.out.println(Thread.currentThread().getName()+":"+i);
                    }
                }
            }
            
            public class MyRunnable implements Runnable{
                @Override
                public void run() {
                    for (int i = 0; i < 100; i++) {
                        System.out.println("MyRunnable"+i);
                        //每次执行完打印让线程休眠1秒
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            public class MyCallable implements Callable {
                @Override
                public Object call() throws Exception {
                    for (int i = 0; i < 100; i++) {
                        System.out.println("MyCallable:"+i);
                    }
                    return null;
                }
            }
            

            总结

            本篇文章就到这里了,希望对你有所帮助,也希望你能够多多关注自由互联的更多内容!

            网友评论