1.并发和并行

并行:在同一时刻,有多个指令在多个CPU上同时执行
并发:在同一时刻,有多个指令在单个CPU上交替执行

2.进程和线程

进程:就是操作系统中正在运行的一个应用程序
线程:就是应用程序中做的事情。比如:360软件中的杀毒,扫描木马,清理垃圾

3.Java多线程的实现方案

1.继承Thread类

//继承(extends)Thread类并重写main方法
public class MyThread extends Thread {

    @Override
    public void run() {
        for (int a = 0; a < 100; a++) {
            System.out.println("线程开启了" + a);
        }
    }
}

//在main方法中调用start()方法
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        t1.start();
        t2.start();

2.实现Runnable接口

//定义自定义类并实现(implements)Runable接口
public class MyRunable implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("线程开启了" + i);
        }
    }
}

//创建Thread对象和自己定义的对象 将Runnable传递给Thread对象 并通过调用 start() 方法来启动线程
    public static void main(String[] args) {
        MyRunable myRunable = new MyRunable();
        Thread t1 = new Thread(myRunable);
        Thread t2 = new Thread(myRunable);
        t1.start();
        t2.start();
    }

3.使用 Callable 和 Future

必须要在线程启动后调用get方法 否则会堵塞

//定义一个类实现Callable方法
public class MyCallable implements Callable<String> {
//在MyCallable类中重写call()方法
    @Override
    public String call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println("向女生表白" + i);
        }
        return "答应";
    }
}

    public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建MyCallable对象
        MyCallable myCallabl= new MyCallable();
//创建FutureTask对象 并把MyCallable对象作为参数传递进去
        FutureTask<String> futureTask = new FutureTask<>(myCallabl);
//创建线程对象 并把FutureTask对象作为参数传递进去
        Thread thread = new Thread(futureTask);

        thread.start();
//get()方法可以获取MyCallable方法结束后输出的结果
        System.out.println(futureTask.get());

    }

三种方式的对比

/优点缺点
实现Runable,Callable接口扩展性强,实现该接口的同时还可以继承其他的类编程相对复杂,不能直接使用Thread类中的方法
继承Thread类编程比较简单,可以直接使用Thread类中的方法可扩展性较差,不能再继承其他的类

Thread方法设置/获取名字

获取线程对象的name:

//前情提要:Mythread继承了Thread        
        Mythread mythread = new Mythread();
        Mythread mythread1 = new Mythread();
        String tname = mythread.getName();
        System.out.println(tname);

设置线程对象的name:
方法1:void setName(String name)

mythread.setName("黄乘明爱吃屎");

方法2:通过构造方法设置名字(因为Thread类本来就自带了name成员变量)
//只需要调用父类的构造方法即可

    public Mythread(String name) {
        super(name);
    }

获得线程对象

获得当前正在运行的线程对象

String tname = Thread.currentThread().getName();//static方法

sleep方法:睡多少毫秒

Thread.sleep(1000);//static方法

线程的优先级

设置优先级

public final void setPriority(int newPriority)


获得优先级

public int getPriority()

守护线程/后台线程

守护线程(Daemon Thread)是一种特殊类型的线程,用于执行后台任务或辅助性工作,当所有的用户线程(非守护线程)都结束时,JVM(Java虚拟机)会终止所有的守护线程,且不等待它们完成

Thread thread = new Thread()
thread.setDaemon(true); // 将线程设置为守护线程

同步代码块

public class ticket implements Runnable{
    private int count = 100;
    Object lock = new Object();
    @Override
    public void run() {
        while (true){
            synchronized (lock){
                if (count <= 0){
                    //卖完了
                    break;
                }else {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count--;
                    System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + count + "张");
                }
            }
        }
    }
}

同步方法

同步方法的锁对象是this

    private synchronized boolean synchronizedMethod() {
        if (ticketCount <= 0){
            //卖完了
            return true;
        } else {
            try {
                Thread.sleep(100);
            } catch (Exception e){
                e.printStackTrace();
            }
            ticketCount--;
            System.out.println(Thread.currentThread().getName() + "在卖票" + "还剩下" + ticketCount + "张");
            return true;
        }
    }

lock锁

ReentrantLock锁有更灵活的锁定机制,公平性选项,允许在不同的代码块之间显式地锁定和解锁,支持条件变量这些特性使ReentrantLock更适合处理复杂的同步需求和高并发环境。

package com.itheima.threaddemo10;

import java.util.concurrent.locks.ReentrantLock;

public class Ticket implements Runnable{
    private int ticker = 100;

    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true){
            try {
                lock.lock();
                if (ticker <= 0){
                    break;
                } else {
                    Thread.sleep(100);
                    ticker--;
                    System.out.println(Thread.currentThread().getName() + "正在卖票, " + "还剩" + ticker + "张票");
                }
            } catch (InterruptedException e){
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}

死锁

锁的嵌套是一个常见的死锁原因
所以我们千万不能写锁的嵌套

        Object lockA = new Object();
        Object lockB = new Object();

        new Thread(() -> {
            while (true){
                synchronized (lockA){
                    //线程一
                    synchronized (lockB){
                        System.out.println("小康同学正在走路");
                    }
                }
            }
        }).start();

        new Thread(() -> {
            while (true){
                synchronized (lockB){
                    //线程二
                    synchronized (lockA){
                        System.out.println("小薇同学正在走路");
                    }
                }
            }
        }).start();

生产者和消费者

//套路:

        //1. while(true)死循环
        //2. synchronized 锁,锁对象要唯一
        //3. 判断,共享数据是否结束. 结束
        //4. 判断,共享数据是否结束. 没有结束

package com.itheima.threaddemo12;

public class Desk {
    //定义一个标记flag 如果为true则表示桌上有汉堡包 此时允许吃货执行 反之则允许厨师做汉堡
    public static Boolean flag = false;
    //汉堡包的总数量
    public static int count = 10;
    //锁
    public static final Object lock = new Object();
}
package com.itheima.threaddemo12;

public class Foodie extends Thread{
//        1,判断桌子上是否有汉堡包。
//        2,如果没有就等待。
//        3,如果有就开吃
//        4,吃完之后,桌子上的汉堡包就没有了
//        叫醒等待的生产者继续生产
//        汉堡包的总数量减一


    @Override
    public void run() {
        while (true){
            synchronized (Desk.lock){
                if (Desk.count == 0){
                    System.out.println("再吃就要撑死了!");
                    break;
                }else {
                    if (Desk.flag){//桌子上有汉堡的情况
                        System.out.println("黄乘明在吃汉堡");
                        Desk.flag = false;
                        Desk.count--;
                        Desk.lock.notifyAll();
                    }else {//桌子上没汉堡的情况
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}
package com.itheima.threaddemo12;

public class Cooker extends Thread{
    @Override
    public void run() {
        while (true){
            synchronized (Desk.lock){
                if (Desk.count == 0){
                    System.out.println("黄乘明已经吃饱了");
                    break;
                } else {
                    if (!Desk.flag){
                        System.out.println("厨师正在做汉堡");
                        Desk.flag = true;
                        Desk.lock.notifyAll();
                    }else {
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e){
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}