JAVA多线程

线程简介

程序

程序是指令和数据的有序集合,其本身没有运行的含义。它是一个静态的概念。

进程(Process)

是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,他是一个动态的概念。

一个进程中可以包含若干个线程,一个进程中至少有一个线程。

线程(Thread)

是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。

线程可以利用进程所拥有的资源。

多线程

在这里插入图片描述

线程

三种创建方式

  • 继承Thread类
  • 实现Runnable接口
  • 实现Callable接口

继承Thread

  1. 继承Thread 类
  2. 重写run方法
  3. 调用 start方法开启线程
public class TestThread1 extends Thread{
    @Override
    public void run() {
        //run方法
        for (int i = 0; i < 20; i++) {
            System.out.println("aaa"+i);
        }
    }

    public static void main(String[] args) {
        TestThread1 testThread1 = new TestThread1();
        testThread1.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("xin"+i);
        }
    }
}

线程开启不一定立即执行,由cpu进行调度。

实现Runnable

  1. 定义类实现Runnable接口
  2. 实现Run()方法,编写线程执行体
  3. 创建Thread线程对象,调用start()方法启动线程
public class TestThread3 implements Runnable{

    @Override
    public void run() {
        //run方法
        for (int i = 0; i < 20000; i++) {
            System.out.println("aaa"+i);
        }
    }

    public static void main(String[] args) {
        TestThread3 testThread3 = new TestThread3();
        //创建线程对象,通过线程对象开启线程
        //new Thread(testThread3).start();效果一样
        Thread thread = new Thread(testThread3);
        thread.start();
        for (int i = 0; i < 20000; i++) {
            System.out.println("xin"+i);
        }
    }
}

实现Callable接口

  1. 定义类实现Callable接口
  2. 实现call()方法,编写线程执行体
  3. 创建目标对象,
  4. 创建执行服务
  5. 提交执行
  6. 获取结果
  7. 关闭服务
public class TestCallable12 implements Callable<Boolean> {


    @Override
    public Boolean call() throws Exception {
        for (int i = 0; i < 20000; i++) {
            System.out.println("aaa"+i);
        }
        return true;
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException{
        TestCallable12 testCallable12 = new TestCallable12();
        //创建执行服务
        ExecutorService ser = Executors.newFixedThreadPool(1);
        //提交执行
        Future<Boolean> r1 = ser.submit(testCallable12);
        for (int i = 0; i < 20000; i++) {
            System.out.println("xin"+i);
        }
        //获取结果
        Boolean a1Boolean = r1.get();

        //关闭服务
        ser.shutdown();

    }
}

三种实现方式的对比

继承Thread类

  • 子类继承Thread类具备多线程能力
  • 启动线程:子类对象.start()
  • 不建议使用:避免OOP单继承局限性

实现Runnable接口

  • 实现Runnable接口具有多线程能力
  • 启动线程:传入目标对象+Thread对象.start()
  • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个对象使用

实现Callable接口

  • 实现Callable接口具有多线程能力
  • 可以定义返回值
  • 可以抛出异常
  • 实现复杂

线程状态

  1. 创建状态
  2. 就绪状态
  3. 阻塞状态
  4. 运行状态
  5. 死亡状态

创建状态

线程一旦创建就进入到了创建状态

就绪状态

当线程调用start()方法,线程立即进入就绪状态,但不意味着立即调度执行

运行状态

当cpu进行调度执行后才进入运行状态,线程才真正执行线程体的代码块

阻塞状态

当调用sleep,wait或同步锁定时线程进入阻塞状态,就是代码不往下 执行,阻塞事件解除后,重新进入就绪状态,等待cpu调度执行

死亡状态

线程中断或结束,一旦进入死亡状态,就不能再次启动

线程操作

线程停止

方法
  1. 线程正常停止(利用次数,不建议死循环)
  2. 使用标志位
  3. 不建议使用stop或destroy等JDK不推荐的方法
public class TestStop implements Runnable {
    //1.设置一个标志位
    private boolean flag = true;

    @Override
    public void run() {
        int i = 0;
        while (flag) {
            System.out.println("run..Thread" + i++);
        }
    }
    //设置一个方法切换标志位,达到停止线程的目的
    public void stop() {
        this.flag = false;
    }

    public static void main(String[] args) {
        TestStop testStop = new TestStop();
        new Thread(testStop).start();
        for (int i = 0; i < 10000; i++) {
            System.out.println("--第" + i);
            if (i == 6000) {
                //调用停止线程方法
                testStop.stop();
                System.out.println("停止了");
            }
        }
    }
}

线程休眠

sleep()
  • sleep(时间)指定当前线程阻塞的毫秒数
  • sleep存在异常InterruptedException
  • sleep时间到达后线程进入就绪状态
  • sleep可以模拟网络延时,倒计时等
  • 每个对象都有一个锁,sleep不会释放锁
public class TestSleep implements Runnable {
    private int ticketNums = 10;

    @Override
    public void run() {
        while (true) {
            if (ticketNums <= 0) {
                break;
            }
            try {
                //模拟延时
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "---第" + ticketNums + "个");
        }
    }

    public static void main(String[] args) {
        TestSleep testSleep = new TestSleep();
        new Thread(testSleep, "A").start();
        new Thread(testSleep, "B").start();
        new Thread(testSleep, "C").start();
    }
}

线程礼让

线程礼让
  • 礼让线程,让当前执行的线程暂停,但不堵塞
  • 让线程从运行状态转为就绪状态
  • 让cpu重新调度,礼让不一定成功
public class TestYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();
        new Thread(myYield,"a").start();
        new Thread(myYield,"b").start();
    }
}

class MyYield implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"开始");
        //礼让
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"停止");
    }
}

线程强制执行

join()

合并线程,待此线程执行完成后,在执行其他线程,其他线程堵塞

public class TestJoin implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("强制执行" + i);
        }
    }

    public static void main(String[] args) {
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();
        for (int i = 0; i < 500; i++) {
            if (i == 200) {
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("main" + i);
        }
    }
}

观测线程状态

public class TestState {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("-----");
        });

        //1监测状态
        Thread.State state = thread.getState();
        System.out.println(state);

        thread.start();
        //2监测状态
        state = thread.getState();
        System.out.println(state);
        //3监测状态
        while (state != Thread.State.TERMINATED) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            state = thread.getState();
            System.out.println(state);
        }
    }
}

线程优先级

  • Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有进程,线程调度器按照优先级决定调度那个线程来执行

  • 线程优先级用数字表示,范围1~10

    • Thread.MAX_PRIORITY =10
    • Thread.MIN_PRIORITY = 1
    • Thread.NORM_PRIORITY = 5
  • 使用setPriority()修改线程优先级

  • 使用getPriority()获取线程优先级

  • 优先级的高低只是代表调度的概率高低

public class TestPriority {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
        MyPriority myPriority = new MyPriority();
        Thread t1 = new Thread(myPriority);
        Thread t2 = new Thread(myPriority);
        Thread t3 = new Thread(myPriority);
        t1.start();

        t2.setPriority(1);
        t2.start();

        t3.setPriority(Thread.MAX_PRIORITY);
        t3.start();
    }

    static class MyPriority implements Runnable {

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
        }
    }
}

守护线程

  • 线程分为用户线程与守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
  • 守护线程如:垃圾回收,监控内存,后台记录操作日志等
public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();
        Thread thread = new Thread(god);
        thread.setDaemon(true);//默认是false表示是用户线程
        thread.start();//启动守护线程

        new Thread(you).start();//启动用户线程
    }
}
class You implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 3650; i++) {
            System.out.println("~~~aa~~~");
        }
        System.out.println("-----------end---------");
    }
}
class God implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println("god");
        }
    }
}

线程同步

并发

同一个对象被多个线程同时操作。

线程同步

线程同步就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。

锁机制

为了保护数据在方法中被访问的正确性,在访问时加入锁机制,当一个线程获得对象的排他锁,独占资源,其他线程必须等待,使用后释放锁即可。

synchronized

针对线程访问提出了一套机制,这套机制就是synchronized关键字,它包括了两种用法:synchronized方法和synchronized块。

同步方法

同步方法:public synchronized void method(int args){}

synchronized方法控制对"对象"的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会堵塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面的堵塞的线程才能获得这个锁,继续执行。

public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        new Thread(buyTicket,"A").start();
        new Thread(buyTicket,"B").start();
        new Thread(buyTicket,"C").start();
    }
}
class BuyTicket implements Runnable{
    private int ticketNums =10;
    private boolean flag=true;
    @Override
    public void run() {
        while (flag){
            buy();
        }
    }
    //synchronized同步方法
    private synchronized void buy(){
        if(ticketNums<=0){
            flag=false;
            return;
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"第"+ticketNums--);
    }
}

同步块

  • synchronized(Obj){}
  • Obj称之为同步监视器
    • Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
    • 同步方法中无需指定同步监视器,因为v同步方法的同步监视器就是this,就是这个对象本身,或者是class
  • 同步监视器的执行过程
    1. 第一个线程访问,锁定同步监视器,执行其中代码
    2. 第二个线程访问,发现同步监视器被锁定,无法访问
    3. 第一个线程访问完毕,解锁同步监视器
    4. 第二个线程访问,发现同步监视器没有锁,然后锁定访问
public class UnsafeBank {
    public static void main(String[] args) {
        Account account = new Account(100, "基金");
        Drawing you = new Drawing(account, 50, "你");
        Drawing girlFriend = new Drawing(account, 100, "女朋友");
        you.start();
        girlFriend.start();
    }
}

class Account {
    int money;
    String name;

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

class Drawing extends Thread {
    Account account;
    int drawingMoney;
    int nowMoney;

    public Drawing(Account account, int drawingMoney, String name) {
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    @Override
    public void run() {
        synchronized (account) {
            //判断有没有钱
            if (account.money - drawingMoney < 0) {
                System.out.println(this.getName() + "Not enough money!");
                return;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //卡内余额
            account.money -= drawingMoney;
            nowMoney += drawingMoney;
            System.out.println(account.name + "余额为" + account.money);
            System.out.println(this.getName() + "手里的钱" + nowMoney);
        }
    }

}

死锁

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情景。某一个同步块同时拥有“两个及以上对象的锁”时,就可能会发生“死锁”的问题。

简单来说就是“多个线程互相占用着对方需要的资源,而形成的僵持”

死锁情况

public class DeadLock {
    public static void main(String[] args) {
        MakeUp g1 = new MakeUp(0, "A");
        MakeUp g2 = new MakeUp(1, "B");
        g1.start();
        g2.start();
    }
}

class Lipstick {

}

class Mirror {

}

class MakeUp extends Thread {
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();
    int choice;
    String girlname;

    public MakeUp(int choice, String girlname) {
        this.choice = choice;
        this.girlname = girlname;
    }

    @Override
    public void run() {
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void makeup() throws InterruptedException {
        if (choice == 0) {
            synchronized (lipstick) {//口红锁
                System.out.println(this.girlname + "口红锁");
                Thread.sleep(1000);
                synchronized (mirror) {//获取镜子
                    System.out.println(this.girlname + "镜子锁");
                }
            }
        } else {
            synchronized (mirror) {//镜子锁
                System.out.println(this.girlname + "镜子锁");
                Thread.sleep(2000);
                synchronized (lipstick) {//获取口红锁
                    System.out.println(this.girlname + "口红锁");
                }
            }
        }
    }
}

解除

public class DeadLock {
    public static void main(String[] args) {
        MakeUp g1 = new MakeUp(0, "A");
        MakeUp g2 = new MakeUp(1, "B");
        g1.start();
        g2.start();
    }
}

class Lipstick {

}

class Mirror {

}

class MakeUp extends Thread {
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();
    int choice;
    String girlname;

    public MakeUp(int choice, String girlname) {
        this.choice = choice;
        this.girlname = girlname;
    }

    @Override
    public void run() {
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void makeup() throws InterruptedException {
        if (choice == 0) {
            synchronized (lipstick) {//口红锁
                System.out.println(this.girlname + "口红锁");
                Thread.sleep(1000);
//                synchronized (mirror) {//获取镜子
//                    System.out.println(this.girlname + "镜子锁");
//                }
            }
            synchronized (mirror) {//获取镜子
                System.out.println(this.girlname + "镜子锁");
            }
        } else {
            synchronized (mirror) {//镜子锁
                System.out.println(this.girlname + "镜子锁");
                Thread.sleep(2000);
//                synchronized (lipstick) {//获取口红锁
//                    System.out.println(this.girlname + "口红锁");
//                }
            }
            synchronized (lipstick) {//获取口红锁
                System.out.println(this.girlname + "口红锁");
            }
        }
    }
}

死锁避免

产生死锁的条件

  1. 互斥条件:一个资源每次只能被一个进程使用
  2. 请求与保持条件:一个进程请求资源而堵塞时,对已获得的资源保持不放
  3. 不剥夺条件:进程以获得的资源,在未使用完之前,不能强制剥夺
  4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

Lock锁

import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
    public static void main(String[] args) {
        TestLock2 testLock2 = new TestLock2();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
    }

}

class TestLock2 implements Runnable {
    int ticketNums = 10;
    //定义lock锁
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();//加锁
                if (ticketNums > 0) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(ticketNums--);
                } else {
                    break;
                }
            } finally {
                lock.unlock();
            }

        }
    }
}

synchronized对比lock

  • lock是显示锁(手动开启与关闭锁,需要自己关闭)synchronized是隐式锁,出了作用域自动释放
  • lock只有代码块锁,synchronized有代码块锁与方法锁
  • 使用lock锁,jvm将花费较少的时间来调度线程,性能更好。具有更好的扩展性(有更多的子类)
  • 优先使用顺序 lock>同步代码块>同步方法

线程协作

生产者与消费者问题

生产者与消费者共享同一资源,并且生产者与消费者之间互相依赖,互为条件。

  • 对于生产者,没有生产产品之前,要通知消费者等待,生产产品后需要通知消费者消费
  • 对于消费者,在消费后,要通知生产者消费已经结束,需要继续生产以供消费

线程通信

  • wait():表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
  • wait(long timeout):指定等待秒数
  • notify():唤醒一个处于等待的线程
  • notifyAll():唤醒同一个对象上所有调用wait()方法的线程,优先级高的线程优先调度

管程法

并发协作模型“生产者/消费者模型”

在这里插入图片描述

  • 生产者:负责生产数据的模块
  • 消费者:负责处理数据的模块
  • 缓冲区:数据存放的缓冲
  • 生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
public class TestPC {
    public static void main(String[] args) {
        SyContainer container = new SyContainer();
        new Productor(container).start();
        new Consumer(container).start();
    }
}
class Productor extends Thread{
SyContainer container;

    public Productor(SyContainer container) {
        this.container = container;
    }

    @Override
    public void run() {

        for (int i = 0; i < 100; i++) {
            System.out.println("生产"+i+"鸡");
            container.push(new Chicken(i));
        }
    }
}
class Consumer extends Thread{
    SyContainer container;

    public Consumer(SyContainer container) {
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消费了--"+container.pop().id);
        }
    }
}

class Chicken{
    int id;

    public Chicken(int id) {
        this.id = id;
    }
}

class SyContainer{
    Chicken[] chickens=new Chicken[10];
    int count=0;
    //生产者放入
    public synchronized  void push(Chicken chicken){
    if (count== chickens.length){
        try {
            //生产停止
            this.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    chickens[count]=chicken;
    count++;
    //消费
    this.notifyAll();
    }

    public synchronized  Chicken pop(){
        if(count==0){
            try {
                //消费等待
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        count--;
        Chicken chicken = chickens[count];
        this.notifyAll();
        return chicken;
    }
}

信号灯法

通过设置标志位解决

public class TestPc2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}

//生产者 演员
class Player extends Thread {
    TV tv;

    public Player(TV tv) {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i % 2 == 0) {
                this.tv.play("aaa");
            } else {
                this.tv.play("bb");
            }
        }
    }
}

//消费者 观众
class Watcher extends Thread {
    TV tv;

    public Watcher(TV tv) {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}

//产品 节目
class TV {
    //演员表演,观众等待 t
    //观众观看,演员等待 f
    String voice;
    boolean flag = true;

    //表演
    public synchronized void play(String voice) {
        if (!flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("表演  " + voice);
        this.notifyAll();
        this.voice = voice;
        this.flag = !this.flag;
    }

    //观看
    public synchronized void watch() {
        if (flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观看了:" + voice);
        this.notifyAll();
        this.flag = !this.flag;
    }
}

线程池

提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁的创建销毁,实现重复使用。

好处

  • 提高相应速度(减少创建新线程时间)
  • 降低资源消耗(重复利用线程池中线程)
  • 便于线程管理

使用

  • ExecutorService:线程池接口
    • void execute(Runnable command):执行任务
    • < >Future< >submit(Callable< >task):执行任务有返回值
    • void shuutdown():关闭连接池
  • Executors:工具栏,线程池工厂
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestPool {
    public static void main(String[] args) {
        //创建线程池 参数为:线程池大小
        ExecutorService service = Executors.newFixedThreadPool(10);
        //执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        //关闭连接
        service.shutdown();
    }
}
class MyThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"   --"+i);
        }
    }
}

放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁的创建销毁,实现重复使用。

好处

  • 提高相应速度(减少创建新线程时间)
  • 降低资源消耗(重复利用线程池中线程)
  • 便于线程管理

使用

  • ExecutorService:线程池接口
    • void execute(Runnable command):执行任务
    • < >Future< >submit(Callable< >task):执行任务有返回值
    • void shuutdown():关闭连接池
  • Executors:工具栏,线程池工厂
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestPool {
    public static void main(String[] args) {
        //创建线程池 参数为:线程池大小
        ExecutorService service = Executors.newFixedThreadPool(10);
        //执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        //关闭连接
        service.shutdown();
    }
}
class MyThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"   --"+i);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值