线程

1、概念

进程:计算机在运行的任务。
服务:本质上是一个没有界面的进程。
线程:进程中在执行的小任务。绝大多数软件都是多线程的,例如:QQ、迅雷、JVM。

2、接口及类

1.Runnable interface

重要方法

run();

2.Thread class

Thread是线程的顶级父类,也实现了Runnable interface。

构造方法

Thread();
创建一个Thread对象。
Thread(Runnable target);
创建一个参数为Runnable实现类对象的Thread对象。

重要方法

isAlive();
判断线程是否还存在。
run();
定义线程中执行的内容。
start();
表示开启线程,把当前的线程run();隔离成一个单独的任务。
sleep(long s);
指定睡眠时间,时间单位是毫秒,是个静态方法。在那个方法使用就睡眠那个方法,并且还有异常要抛出或者使用try-catch语句。
toString();
返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
currentThread();
表示获取当前在执行的线程。
getName();
获取当前执行线程的名字。

3.Callable interface

返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做call的方法。

重要方法

call();
计算结果,如果无法计算结果,则抛出一个异常。

3、自定义线程

自定义线程的三种方法:
1.让类继承Thread class,重写run方法,将要执行的任务放到run方法中个,需要调用start方法来开启线程。
2.让类实现Runnable interface,重写run方法,需要将Runnable对象放到一个Thread对象中来启动。
3.让类实现Callable interface,重写call方法。(现阶段要求知道这种实现)

public class CallableDemo {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // 执行器服务             执行器工具类     缓存线程池
        ExecutorService es = Executors.newCachedThreadPool();
        Future<String> f = es.submit(new CDemo());
        System.out.println(f.get());
    }
}
class CDemo implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "hahah~~~";
    }
}

4、同步锁机制

Java 父线程和子线程 线程的父类_多线程


由于多个线程一起执行并且线程之间相互抢占资源导致出现不符合事实的情况称为多线程的并发安全问题。

同步锁机制:需要一个锁对象,使用关键字synchronized()。锁对象需要是被所有线程都认识的、共享资源、类的字节码(类的字节码是加载到方法区,方法区中的信息是被所有线程共享的),this(要求共用一个类对象,启动不同的线程对象)

同步方法:用关键字synchronized修饰方法,将方法上锁。同步方法的锁对象是this。

安全:

不安全:

同步:在同一个时间段内同一段代码只允许一个线程执行。

异步:在同一个时间段内同一段代码允许多个线程一起执行。

同步一定安全,不安全一定异步。(这句话反回去不成立)

死锁情况

死锁:共享资源较多,锁的相互嵌套。
避免死锁:减少共享资源(几乎做不到);减少锁的嵌套。

public class DeadLockDemo {
    static Printer p = new Printer();
    static Scanner s = new Scanner();

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (p) {
                    p.print();
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (s) {
                        s.scan();
                    }
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (s) {
                    s.scan();
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (p) {
                        p.print();
                    }
                }
            }
        }).start();
    }
}
class Printer {
    public void print() {
        System.out.println("打印机在吱呦吱呦的打印~~~");
    }
}
class Scanner {
    public void scan() {
        System.out.println("扫描仪在哼哧哼哧的扫描~~~");
    }
}

5、等待唤醒机制

标记位:如果当前的代码已经不能使用已有变量来控制不同的情况的时候,需要引入第三方的变量来进行控制。第三方变量可以是boolean类型也可以是整数类型。
通过等待唤醒机制来调节线程之间的执行顺序称作线程的相互通信。
等待唤醒机制要配合同步锁来使用,它们使用的对象要是同一个对象。

Object class

方法

notify();
唤醒等待线程。
notifyAll();
唤醒所有线程。
wait();
等待运行。
线程池:本质上是一个存储线程的队列。
线程在等待期间是存储在线程池。

6、总结

sleep和wait方法有什么区别?

sleep方法需要指定睡眠时间,到点自然醒。释放了执行权,不释放锁。被设计在来Thread类上,是一个静态方法。
wait方法可以指定等待时间,如果不指定时间则需要唤醒。释放来执行权,释放来锁。被设计在了Object类上,是一个成员方法。

7、线程的状态

创建、就绪、运行/活跃、阻塞、消亡

Java 父线程和子线程 线程的父类_Java 父线程和子线程_02

8、线程的优先级(Priority)

线程是有优先级划分的。优先级分为来1-10这是个等级,理论上,数字越大优先级越高,那么线程抢占到资源的概率越大。但是实际上,相邻两个优先级的差别不明显。优先级差到5个单位以上才相对明显。
getPriority();
获得当前线程的优先级。
setPriority();
设置当前线程的优先级。

public class PriorityDemo {
    public static void main(String[] args) {
        Thread t1 = new Thread(new PDemo(), "A");
        Thread t2 = new Thread(new PDemo(), "B");
        // System.out.println(t1.getPriority());
        t1.setPriority(1);
        t2.setPriority(10);
        t1.start();
        t2.start();
    }
}
class PDemo implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10;i++){
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}

9、守护线程

守护线程守护被守护的线程。如果被守护的线程结束了,无论守护线程执行完成与否都会随之结束。在Java中,一个线程要么是守护线程,要么被守护线程。如果出现了多个被守护的线程,以最后一个被守护线程的结束作为结束标志。

setDaemon(true);
设置守护线程。

public class DaemonDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Soilder(), "小兵一号");
        Thread t2 = new Thread(new Soilder(), "小兵二号");
        Thread t3 = new Thread(new Soilder(), "小兵三号");
        Thread t4 = new Thread(new Soilder(), "小兵四号");
        t1.setDaemon(true);
        t2.setDaemon(true);
        t3.setDaemon(true);
        t4.setDaemon(true);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        for (int i = 10; i > 0; i--) {
            System.out.println("Boss掉了一滴血,剩余" + i + "滴");
            Thread.sleep(100);
        }
    }
}
class Soilder implements Runnable {
    @Override
    public void run() {
        for (int i = 10; i > 0; i--) {
            System.out.println(Thread.currentThread().getName() + "掉了一滴血,剩余" + i + "滴");
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

10、单例模式(SingleTon)

设计模式:在软件开发过程中常见的方案。常用的有24种。
单例模式:在全局过程中只存在一个唯一的实例。一共有7种形式。

饿汉式:

在创建对象的时候就把对象初始化。

private static TaskManager = new TaskManager();

懒汉式:

在调用getInstance方法的时候才初始化这个对象。
懒汉式相对于饿汉式将初始化的过程推后,节省了对象的初始化时间,但是存在线程安全问题。饿汉式不存在线程安全问题。

public class SingleTonDemo {
}
class TaskManager {
    private static TaskManager tm ;
    private TaskManager() {
    }
    // 懒汉式
    public static TaskManager getInstance(){
        if (tm == null) {
            tm = new TaskManager();
        }
        return tm;
    }
}
/*class TaskManager {
    // 必然要设置为static的---静态才能保证唯一
    // 饿汉式
    private static TaskManager tm = new TaskManager();
    private TaskManager() {
    }
    public static TaskManager getInstance(){
        return tm;
    }
}*/