Java基础_多线程相关

本文详细介绍了Java中的线程与并发概念,包括并行与并发的区别,进程与线程的特性。通过实例展示了如何通过Thread类和Runnable接口创建线程,讨论了线程的五种状态,并通过售票问题阐述了线程安全问题,给出了同步锁的解决方案,如方法锁、同步代码块和Lock锁。最后,提到了线程死锁的条件和线程池的使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 并行:指两个或多个事件在同一时刻发生(同时发生);
  • 并发:指两个或多个事件在同一个时间段内发生(在多个CPU系统中 可以分配到多个处理器上 并行处理的任务越多、效率越高;单核处理器的计算机不能并行处理多个任务)。

 

  • 进程:内存中一个运行的应用程序,每个进程都有一个独立的内存空间,一个程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位,系统运行一个程序即是一个进程从创建、运行到消亡的过程;
  • 线程:进程中的一个执行单位、负责当前进程中程序的执行、一个进程至少有一个线程,一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。

区别:

  • 进程:有独立的内存空间,进程中的数据存放空间(堆空间 栈空间)是独立的,至少有一个线程;
  • 线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。

线程调度:多个线程执行时,线程会被JVM控制以某种方式执行,我们把这种情况称之为线程调度。JVM采用的是抢占式调度,没有采用分时调度,因此可以造成线程执行结果的随机性。

一、创建线程

java使用java.lang.Thread类代表线程,所有的线程对象都必须是Thread类或者其子类的实例,每个线程的作用是完成一个定的任务。Java使用线程执行体来代表这段程序流

线程方式1 :单继承,线程与任务在一起

// 线程的入门
public class DemoThread {
    public static void main(String[] args) {
    /*
        并行:指两个或多个事件在同一时刻发生
        并发:指两个或多个事件在同一时间内发生
        进程:系统运行的基本单位、任务管理器进程 (有独立的内存空间)
        线程:一个进程可以有多个线程、(栈空间独立、堆公用)
    */
    
        myThread tm1 = new myThread();
        myThread tm2 = new myThread("旺财");

        // 线程开启 交替输出
        tm1.start();
        tm2.start();

        for (int i = 0; i < 100; i++) {
            System.out.println("小强:"+i);
        }
    }
}


// 线程方式1 单继承,线程与任务在一起,
class myThread extends Thread{

    // 无参构造
    public myThread() {
        super();
    }
    // 有参构造
    public myThread(String name) {
        super(name);//指定线程名字
    }

    @Override
    public void run() {  // 线程任务开始的地方
        for (int i = 0; i < 100; i++) {
            System.out.println(this.getName()+":"+i);
        }
    }
}

线程方式2 :声明实现Runnable接口类

步骤:自定义类、实现run方法、创建任务类对象、创建Thread对象-传递任务对象

方式2的好处:避免Java单继承的局限性、解耦合(线程与任务分离、拓展性好)

public class DemoRunnable {
    public static void main(String[] args) {
        // 3 创建任务类对象
        MyTask task = new MyTask();
        // 2 使用自定义类 把任务对象当作参数传递 ,分配一个有指定目标的线程对象
        Thread thread1 = new Thread(task);
        Thread thread2 = new Thread(task, "小强哥");

        // 0 匿名内部类
        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                //  获取执行当前线程任务的名字
                String threadName = Thread.currentThread().getName();
                // 任务
                for (int i = 0; i < 10; i++) {
                    System.out.println(threadName + " : " + i);
                }
            }
        },"匿名内部类线程");

        // 3 开启线程(任务)
        thread1.start();
        thread2.start();
        thread3.start();
    }
}


// 1、自定义类,实现Runnable接口 这个类就是任务类
class MyTask implements Runnable {

    public MyTask() {}

    // 2 实现run方法 run函数中写的是线程要执行的任务代码
    @Override
    public void run() { //  线程要完成的任务

        //  获取执行当前线程任务的名字
        Thread thread = Thread.currentThread();
        String threadName = thread.getName();
        // 任务
        for (int i = 0; i < 10; i++) {
            System.out.println(threadName + " : " + i);
        }
    }
}

二、线程状态

1、NEW           新建 线程刚被创建 但未启动。还没有调用start()方法,MyThread t = new Thread() 只有线程对象 没有线程特征
2、Runnable      就绪 线程在Java虚拟机中运行的状态,可能正在运行自己的代码、也可能没有,这取决于操作系统处理器 调用了t.start()方法
3、Block         阻塞状态 当一个线程试图获取一个对象锁 而该对象锁被其他的线程持有,则线程进入Block状态;当线程持有锁时,该线程将变成Runnable状态
4、Waiting       无限等待 一个线程在等待另一个线程执行(唤醒)动作时,该线程进入Waiting状态,进入这个状态后是不能自动唤醒的,必须等另一线程调用notify、notifyAll方法才能唤醒
5、TimedWaiting  计时等待 同Waiting状态,有几个方法有超时参数 调用他们将进入TimedWaiting状态,这一状态一直保持到超时期满或者接到唤醒通知。带有超时参数的常用方法有Thread.sleep、Object.wait
6、Terminated    被种终止 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡

限时等待

 public static void main(String[] args) {
        // 被锁对象
        Object obj = new Object();
        //  创建新线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程开启~ 开始获取锁");
                synchronized (obj){
                    System.out.println("进入限时等待");// 进入等待 释放锁对象 程序阻塞,不会往下执行
                    try {
                        obj.wait(2000);// 有限等待
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println("被唤醒 并获取了锁");
                }

            }
        }).start();

    }

waiting & notify (无限等待 唤醒)

public class DemoThreadStateWaitNotify {
    public static void main(String[] args) {
        // 无限等待
        Object obj = new Object();

        //  线程A
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("【AAA】线程开启~ 开始获取锁");
                synchronized (obj){
                    System.out.println("【AAA】进入无限等待");// 进入等待 释放锁对象 程序阻塞,不会往下执行
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("【AAA】被唤醒 并获取了锁");
                }
                System.out.println("【AAA】 完成锁释放");
            }
        }).start();


        // 线程B
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("【BBB】线程开启~ 开始获取锁");
                synchronized (obj){
                    System.out.println("【BBB】占有资源,2s后 唤醒AA");
                    sleep(); // sleep 2s
                    obj.notify(); // 唤醒线程AA 不释放锁
                    System.out.println("【BBB】已经唤醒A 2s后 释放锁");
                    sleep(); // sleep 2s
                }
                System.out.println("【BBB】 完成锁释放");
            }

            private void sleep() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }//main
}//class

三、线程安全

问题引出:金典案例卖票问题。解决方案:同步锁

1-方法锁(详见代码)

// 线程安全案例
public class DemoSellTicket {
    public static void main(String[] args) {
        SellTicketTask task = new SellTicketTask(); // 执行同一个任务
        // 三个线程执行同一个任务
        new Thread(task, "窗口1").start();  // 窗口1
        new Thread(task, "窗口2").start();  // 窗口2
        new Thread(task, "窗口3").start();  // 窗口3

        /* 同步锁是谁
        1、非静态方法同步锁就是this 一定要保证多个线程中调用方法是同一个对象
        2、静态方法同步锁 字节码对象*/
    }// main end
}

class SellTicketTask implements Runnable {
    static int ticket = 100;
    @Override
    public void run() {

        for (int i=0;i<100;i++) {
            //sellT();
            sellT2();
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    // this就是当前任务对象,多个线程都用同一个任务 可以保证多个线程使用的this是同一个锁
    // this就是调用方法的对象
    public synchronized void sellT() {
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + " 正在卖票-> " + ticket);
            ticket--;
        }
    }

    // 如果是静态方法 锁 就是当前类型的字节码文件,也就只有一份   SellTicketTask.class
    public static synchronized void sellT2(){ //
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + " 正在卖票-> " + ticket);
            ticket--;
        }
    }
}

2-同步代码块(详见代码)

// synchronized 方法二 同步代码块
class SellTicketTask1 implements Runnable {
    int ticket = 100;
    final Object lockObj = new Object();

    @Override
    public void run() {

        synchronized (lockObj) { // 把操作资源 放入操作代码块 代码互斥访问
            for (int i = 0; i < 100; i++) {
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + " 正在卖票: " + ticket);
                    ticket--;
                }
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }//run
}//class

3-Lock 锁(详见代码)

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

// 线程安全案例
public class DemoSellTicket2 {
    public static void main(String[] args) {
        // 同步锁实现
        SellTicketTask2 task = new SellTicketTask2();// 执行同一个任务
        // 三个线程执行同一个任务
        new Thread(task, "窗口1").start();  // 窗口1
        new Thread(task, "窗口2").start();  // 窗口2
        new Thread(task, "窗口3").start();  // 窗口3
    }
}

// Lock 锁
// lock锁机制解决线程锁安全问题
// 1 根据子类 ReentrantLock实例化Lock对象
// 2 保证多个线程使用相同的Lock锁对象
// 3 lock() 方法执行就是开始锁
// 4 unlock() 方法执行就是释放锁

class SellTicketTask2 implements Runnable {
    int ticket = 100;
    final Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            lock.lock();  // 上锁
            try{
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "卖票::" + ticket);
                    ticket--;
                }
            }finally {
                lock.unlock();// 可以保证一定释放锁资源
            }


            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }//run
}

 四、死锁

两个或两个以上的线程、两个或两个以上的对象锁、存在锁嵌套 --> 会出现死锁情况

public class DemoThreadDeath {
    /*线程死锁的条件*/
    public static void main(String[] args) {

        // 1 多把锁
        Object lockObj1 = new Object();
        Object lockObj2 = new Object();

        // 2 多线程
        // 3 锁嵌套
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    synchronized (lockObj1) {
                        System.out.println("线程1获取了 obj1");

                        synchronized (lockObj2) {
                            System.out.println("线程1获取了 obj2");
                        }
                    }
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    synchronized (lockObj2) {
                        System.out.println("线程2获取了 obj2");

                        synchronized (lockObj1) {
                            System.out.println("线程2获取了 obj1");
                        }
                    }
                }

            }
        }).start();

    }
}
  • 线程1获取了 obj1
  • 线程1获取了 obj2
  • 线程1获取了 obj1
  • 线程1获取了 obj2
  • 线程1获取了 obj1
  • 线程2获取了 obj2(卡死在此)

五、线程池

1-Runnable

  1. 创建线程池:ExecutorService threadPool = Executors.newFixedThreadPool(3);
  2. 创建任务类:class Student implements Runnable{ run(){} }
  3. 创建任务对象并提交: threadPool.submit(new Student("张三")); threadPool.submit(new Student("张四")); 
  4. 线程池关闭: threadPool.shutdown();
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class DemoThreadPoolRunnable {
    public static void main(String[] args) {
        // 1 创建线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(3);

        // 3 创建任务对象,并提交
        threadPool.submit(new Student("张三"));
        threadPool.submit(new Student("张四"));
        threadPool.submit(new Student("张五"));
        threadPool.submit(new Student("张六"));
        threadPool.submit(new Student("张七"));

        // 4 线程池关闭 【一般不关闭】
        threadPool.shutdown();
        //threadPool.shutdownNow();
    }
}

// 2 创建任务类对象
class Student implements Runnable {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        String coach = Thread.currentThread().getName();
        System.out.println(coach + " 开始教 " + name + " 游泳");
        try {
            Thread.sleep(2000);// 教学中
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(coach + " 教 " + name + " 游泳完成!");
    }
}

2-Callable

  1. 创建线程池:ExecutorService threadPool = Executors.newFixedThreadPool(3);
  2. 创建任务类:class Calculator implements Callable<Integer>{ public Integer call(){} }
  3. 创建任务对象并提交:
    Future<Integer> f = threadPool.submit(new Calculator(100));
    Integer result = f.get();
    System.out.println("result = " + result);
  4. 线程池关闭: threadPool.shutdown();
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.Callable;


public class DemoThreadPoolCallable {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 1 创建线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        // 3 创建任务对象,并提交
        Future<Integer> f = threadPool.submit(new Calculator(100));
        Integer result = f.get();
        System.out.println("result = " + result);
        // 4 线程池关闭 【一般不关闭】
        threadPool.shutdown();
        //threadPool.shutdownNow();
    }
}

// 2 创建任务类对象
class Calculator implements Callable<Integer> {
    private int n;

    public Calculator(int n) {
        this.n = n;
    }

    @Override
    public Integer call() throws Exception {
        int sum =0;
        for (int i = 0; i <= n; i++) {
            sum+=i;
        }
        return sum;
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值