美文网首页
Java多线程入门

Java多线程入门

作者: 王二麻子88 | 来源:发表于2020-11-24 23:55 被阅读0次

Java多线程入门

1. 并发与并行

  • 并发 : 指两个或者多个事件在同一时间段运行
  • 并行 : 指两个或者多个事件在同一时刻发生(同时发生)

并发指的是多个事件交替执行 --> 一个CPU在执行
并行两个或者多个事件同时运行 --> 多个CPU在执行

因此并行的速度比并发快

2. 线程与进程

  • 进程: 是指一个内存中运行的应用程序,每个进程都有一个独立的内存运行空间

  • 线程: 线程是进程中的一个执行单元, 负责运行程序的执行, 一个进程中至少有一个线程, 也可以有多个线程

3. 线程调度

  • 分时调度: 所有线程轮流使用CPU的使用权,平均分配每个线程的占用CPU使用时间

  • 抢占式调度: 优先让优先级高的线程使用CPU, 如果优先级相同, 则同一优先级的线程随机分配

4. 创建线程类

4.1 主线程

程序在main方法中运行的线程称之为主线程 ==> main Thread

4.2 创建线程的方法

  • 将声明为 Thread的子类, 该子类方法重写Thread父类的run方法
// 1.创建子线程 --> childThread
public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("run: ==>"+ i);
        }
    }
}

// 在主线程中调用子线程 ==> mainThread
public class Demo01Thread {
    public static void main(String[] args) {
        // 定义线程只有开启多线程
        MyThread mt = new MyThread();

        mt.start();

        for (int i = 0; i < 20; i++) {
            System.out.println("main: ==> "+ i);
        }  // 此处的应该是随机打印, 于此可以证明线程之间互不影响
        System.out.println(Thread.currentThread().getName()); // 输出 main
    }
}

  • 声明实现Runable接口的类, 然后该类实现run 方法
// 1. Runnable接口的实现类 RunnableImpl
public class Demo01Runnable {
    public static void main(String[] args) {
        // 实例化 Runnable的实现类
        RunnableImpl run = new RunnableImpl();
        // 将实例置入 Thread类的构造函数中
        Thread t = new Thread(run);
        // 调用Thread实例的start方法;
        t.start();
        System.out.println(t.getName());  // Thread-0
    }
}


// 2. 调用 Runnable 接口的实现类 MyRunnable
public class Demo01Runnable {
    public static void main(String[] args) {
        // 实例化 Runnable的实现类
        RunnableImpl run = new RunnableImpl();
        // 将实例置入 Thread类的构造函数中
        Thread t = new Thread(run);
        // 调用Thread实例的start方法;
        t.start();
        System.out.println(t.getName());  // Thread-0
    }
}

总结:

  1. 避免了单继承的局限性
    即一个类只能继承一个类
    实现Runnable接口还可以实现其他的类, 实现其他的接口
  2. 增强了程序的可扩展性(即: 解耦)

4. 匿名内部类创建子线程

匿名内部的作用: 把子类创建父类, 重写父类方法, 创建子类对象的过程合成一步实现

public class Demo01ClassThread {
    public static void main(String[] args) {
        // 父类的线程是 Thread
        // new Thread().start()

        // 使用 Thread创建
        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    System.out.println("ClassThread"+ i);
                    System.out.println(Thread.currentThread().getName());
                }
            }
        }.start();
        
        // 2. Runnable 实现
        Runnable n1 = new Runnable(){
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    System.out.println(Thread.currentThread().getName());
                    System.out.println("Runnable"+ i);
                }
            }
        };
        new Thread(n1).start();
        
        // 上述代码可以简化为
        new Thread(
            new Runnable(){
                @Override
                public void run() {
                    for (int i = 0; i < 20; i++) {
                        System.out.println(Thread.currentThread().getName());
                        System.out.println("Runnable"+ i);
                    }
                }
            }
        ).start();

    }
}

5. 线程安全

多线程访问了共享的数据, 就会产生安全的问题

举个例子

// 1. 在Runnable接口实现类中

public class ThreadSafeRunnable implements Runnable {
    private static int ticket = 100;
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (ticket > 0) {
                System.out.println(Thread.currentThread().getName()+"当前剩余票数: "+ticket);
                ticket--;
            }
        }
    }
}

// 2. 在 Runnable实现类的调用中
public class ThreadSafe {
    public static void main(String[] args) {
        ThreadSafeRunnable n1 = new ThreadSafeRunnable();
        Thread t1 = new Thread(n1);
        Thread t2 = new Thread(n1);
        Thread t3 = new Thread(n1);
        t1.start();
        t2.start();
        t3.start();

        // 此时会出现 相同票数的情况
    }
}


6. 线程安全的解决方案

6.1 同步代码块

// 1. 在Runnable的实现类中 
public class ThreadSafeRunnable implements Runnable {
    // 为了线程安全, 创建一个锁对象, 保重共享数据同步进行

    // 创建一个锁对象 ==> 也叫同步锁, 对象锁
    final Object obj = new Object();

    private static int ticket = 100;
    @Override
    public void run() {
        while (true) {
            // 同步代码块
            /*
                当多线程代码 执行到同步代码块synchronized时, 会检查有没有锁对象
                发现有, 就会获取锁对象, 进入同步执行中
                
                当下一个线程遇到同步代码块时, 因为锁对象已经被上一个线程拿走, 此时该线程就会进入阻塞状态, 停止运行, 
                并等待锁对象从上一个代码块中释放出来
            */
            synchronized (obj) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName()+"当前剩余票数: "+ticket);
                    ticket--;
                }
            }

        }
    }
}

6.2 使用同步方法

使用 synchronized修饰同步方法


public class SynchronizeMethod implements Runnable {
    private static int ticket = 100;

    // 把访问共享数据的代码抽取出来放到一个方法中, 并加上 synchronized 修饰符
    public synchronized void payTicket() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName()+ "2当前剩余票数: "+ticket);
            ticket--;
        }
    }

    @Override
    public void run() {
        while (true) {
            payTicket();
        }
    }
}


// 2. 
public class Demo01SynchronizeMethod {
    public static void main(String[] args) {
        Runnable n1 = new SynchronizeMethod();

        Thread t1 = new Thread(n1);
        Thread t2 = new Thread(n1);
        Thread t3 = new Thread(n1);


        t1.start();
        t2.start();
        t3.start();
    }

}

6.3 静态方法

即用 synchronized 和 static 修饰的方法

值得注意的是:
因为静态方法只与类相关而不与对象相关, 所以this是指向类而不是实例化对象


public class SynchronizeMethod implements Runnable {
    private static int ticket = 100;

    // 静态方法 ==> synchronized 不能使用 this 对象锁
    public static synchronized void payTicket() {
        synchronized (Runnable.class) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (ticket > 0) {
                System.out.println(Thread.currentThread().getName()+ "2当前剩余票数: "+ticket);
                ticket--;
            }
        }
    }

    @Override
    public void run() {
        while (true) {
            payTicket();
        }
    }
}

6.4 Lock锁

即: 创建一个 ReentrantLock对象 实现了Lock接口ReentrantLock对象(在 Java.utils中实现, 可以调用实例)
在线程安全开始时 调用 lock方法, 结束时调用 unlock方法

// 需要导入java.util包
public class ThreadLock implements Runnable {
    Lock l1 = new ReentrantLock();

    private static int ticket = 100;
    
    private static void payTicket() {
        l1.lock();
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "当前剩余票数: ==> "+ ticket);
            ticket--;
        }
        l1.unlock();
        // 这么写有个好处, 运行到这一步无论过程是否正常处理, 线程锁都能被正常的释放掉, 不会阻塞后续线程的进行
    }
    @Override
    public void run() {
        while (true) {
            payTicket();
        }
    }
}

7. 线程状态

相关文章

  • Java锁机制

    Java多线程编程的入门篇,主要介绍volatile修饰词、Synchronized以及Lock及其子类 多线程编...

  • Java进阶之synchronized关键字详解

    掌握多线程是从Java入门后需要跳过的第一大坎,使用多线程就难以避免要处理数据同步问题,在Java多线程中实现数据...

  • 《Java 多线程精选》思维导图

    本文主要分析了 Java 多线程的精选内容 1.进程和线程 2.Java 多线程编程入门 3.线程组和线程优先级 ...

  • java线程入门基础(二)

    java线程入门基础(二) 一、认识Java里的线程 1.1 Java里的程序天生就是多线程的 一个Java程序从...

  • Java多线程基础

    如你们所知,想在java行业拿到高薪,多线程是个必须熟练的技能,可是入门费劲,故最近搜集关于多线程的资料,做如下总...

  • 带你搞懂Java多线程(五)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三)带你搞懂Java多线程(四) ...

  • 带你搞懂Java多线程(六)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三)带你搞懂Java多线程(四)带...

  • JAVA多线程入门

    继承Thread父类 线程代码执行顺序和调用顺序无关,例如: 上述代码执行理论上“MyThread”和“mainT...

  • Java多线程入门

    对多线程的理解 进程是操作系统进行资源分配的基本单位,而线程是操作系统进行调度的基本单位; 多线程是随着多核CPU...

  • Java入门—多线程

    线程是比进程还要小的运行单位,一个进程包含多个线程。 什么是多线程 线程的创建 两种方式: 创建一个Thread类...

网友评论

      本文标题:Java多线程入门

      本文链接:https://www.haomeiwen.com/subject/xqkgiktx.html