Java-多线程

多线程

什么是多线程

  • 如果一个进程中同时运行了多个线程,用来完成不同的工作,则称之为"多线程"
  • 多个线程交替占用CPU资源,而非真正的并行执行
    线程
多线程的好处
  • 充分利用CPU的资源
  • 简化编程模型
  • 带来良好的用户体验

主线程

Thread类
  • Java提供了java.lang.Thread类支持多线程编程
主线程
  • main()方法即为主线程入口
  • 产生其他子线程的线程
  • 必须最后完成执行,因为它执行各种关闭动作
public static void main(String args[]){
    Thread t=Thread.currentThread();
    System.out.println("当前线程是"+t.getName());
    t.setName("MyJavaThread");
    System.out.println("当前线程名是:"+t.getName());
}
线程的创建和启动
  • 在Java中创建线程的两种方式
    • 继承java.lang.Thread类
    • 实现java.lang.Runnable接口
  • 使用线程的步骤
      1. 定义线程
      1. 创建线程对象
      1. 启动线程
      1. 终止线程
继承Thread类创建线程2-1
  • 定义MyThread类继承Thread类
  • 重写run()方法.编写线程执行体
  • 创建线程对象,调用start()方法启动线程
public class MyThread extends Thread{
    //重写run()方法
        public void run(){
            for(int i=1;i<100;i++){
                System.out.println(Thread.currentThread().getName()+":"+i)
            }
        }
}
public static void main(String[] args){
    MyThread thread=new MyThread();
    thread.start();//启动线程
}
继承Thread类创建线程2-2
  • 多个线程交替执行,不是真正的"并行"
  • 线程每次执行时长由分配的CPU时间片长度决定
MyThread t1=new MyThread();
MyThread t2=new MyThread();
t1.start();
t2.start();
启动线程是否可以直接调用run()方法?

Java-多线程_第1张图片

实现Runnable接口创建线程

  • 定义MyRunnable类实现Runnable接口
  • 实现run()方法,编写线程执行体
  • 创建线程对象,调用start()方法启动线程
//实现Runnable接口
public class MyRunnable implements Runnable{
    public void run(){
        for(int i=1;i<100;i++){
            //run()方法中编写线程执行的代码
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
//创建线程对象
public static void main(String[] args){
    MyRunnable myRunnable=new MyRunnable();
    Thread myThread=new Thread(myRunnable);
    thread.start();//启动线程
}

比较两种创建线程的方式

  • 继承Thread类
    • 编写简单
    • 适用于单继承
  • 实现Runnable接口
    • 避免单继承局限性
    • 便于资源共享
      推荐使用实现Runnable接口方式创建线程

示例

public class MyThread extends Thread {
    //自定义线程的第一种方式 继承Thread类

    public void run() {
        //可以写线程要做的事
        for (int i = 0; i < 100; i++) {
            if (i == 5) {
                MyRunnable myRunnable = new MyRunnable();
                Thread thread = new Thread(myRunnable);
                thread.start();
                Thread.yield();//礼让
//                try {
//                    thread.join();
//
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }


//                try {
//                    //使当前线程进入休眠状态 单位是毫秒
//                    Thread.sleep(1000);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//            }
                if (i==20){
                    Thread t=currentThread();
                    t.interrupt();//中断当前线程
                }

                System.out.println(getName() + ": " + i);
            }
        }
    }
}

public class Test02 {
    public static void main(String[] args) {
//        MyThread myThread=new MyThread();
//        myThread.run();//放弃使用的

        Thread thread=new MyThread();
        Thread thread1=new MyThread();
        thread.setName("a");
        thread1.setName("b");

        //设置优先级设置优先级1-10 默认是5 数字越大优先级越高
        thread.setPriority(1);
        thread1.setPriority(10);
        thread.start();//保留的

        thread1.start();
    }
}
public class Test03 {
    public static void main(String[] args) {

            Thread thread=new MyThread();
            Thread thread1=new MyThread();
            thread1.start();
            try {
                thread1.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            thread.start();
    }
}

public class MyRunnable implements Runnable{
    //创建线程的第二种方式

    @Override
    public void run() {
        for(int i=0;i<100;i++){
            //获取当前线程对象
            Thread thread=Thread.currentThread();
            System.out.println( thread.getName()+": "+i);
        }
    }
}

public class MyRunnableTest {
    public static void main(String[] args) {
        //通过实现Runnable接口的方式创建线程
        //1.创建接口对象
        MyRunnable myRunnable=new MyRunnable();
        //2.创建线程对象
        Thread thread=new Thread(myRunnable);
        //3.调用该线程对象的start方法
        thread.start();
    }
}

中断线程interrupt 判断 isInterrupted
    public void run(){
        Thread t=Thread.currentThread();//获取当前线程

        for (int i = 0; i < 20; i++) {
            System.out.println(i);
            if (i==5){
                t.interrupt();//中断线程 修改线程状态
            }
            if (t.isInterrupted()){//  t.isInterrupted();//获取当前状态是否被中断
              break;
            }


        }
    }
锁 synchronized

public class TicketRunnable implements Runnable{
    private  int ticket=10;//总票数
    private static int num=0;//表示买了多少张票
    private boolean flag=false;
    private  static Object object=new Object();

    @Override
    public void run() {

        while (!flag){
            sale();

        }
    }

    /**
     * 卖票
     */
    public synchronized void sale(){
        //synchronized (this){
            if (ticket>0){
                ticket--;
                num++;
                System.out.println(Thread.currentThread().getName()+":抢到第"+num+"张票"+",剩余"+ticket+"张票");
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                flag=true;
            }
        //}

        //1.可以修饰代码块 也可以修饰方法
        //2.修饰代码块时 需要传一个对象 这个对象可以是任意对象 一般是Object或者this
    }
}
锁 ReentrantLock

import java.util.concurrent.locks.ReentrantLock;

public class TicketRunnable implements Runnable{
    private  int ticket=10;//总票数
    private static int num=0;//表示买了多少张票
    private boolean flag=false;
    private  static Object object=new Object();
    private ReentrantLock reentrantLock=new ReentrantLock();//创建锁对象
    @Override
    public void run() {

        while (!flag){
            sale();

        }
    }

    /**
     * 卖票
     */
    public  void sale(){
        //synchronized (this){
        reentrantLock.lock();//上锁
            if (ticket>0){
                ticket--;
                num++;
                System.out.println(Thread.currentThread().getName()+":抢到第"+num+"张票"+",剩余"+ticket+"张票");
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                flag=true;
            }
            reentrantLock.unlock();//开锁
        //}

        //1.可以修饰代码块 也可以修饰方法
        //2.修饰代码块时 需要传一个对象 这个对象可以是任意对象 一般是Object或者this
    }
}
锁 synchronized 与 Lock 的区别
    1. lock是个接口synchronized是一个关键字
    1. synchronized锁住的方法里发生异常的时候 会释放锁 而lock不行必须手动释放
    1. synchronized 可以修饰方法

Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作
Lock中提供了获得锁和释放锁的方法
void lock():获得锁
void unlock():释放锁

Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
ReentrantLock的构造方法
ReentrantLock():创建一个ReentrantLock的实例

返回值 创建线程对象 通过实现 callable 实现
import java.util.concurrent.Callable;
//创建线程对象 通过实现 callable 实现
public class Mycallable implements Callable<String> {

    @Override
    public String call() throws Exception {
        return "今天中午吃什么";
    }
}
public class Test {
    public static void main(String[] args) {
        //1.创建callable对象
        Callable callable=new Mycallable();
        //2.创建FutureTask
        FutureTask futureTask=new FutureTask(callable);
        Thread thread=new Thread(futureTask);
        thread.start();//启动线程
        //3.获取返回值
        //futureTask.isDone(); 判断线程是否结束 结束为true 不结束为false 运行时是false
        while (!futureTask.isDone()){
            try {
                System.out.println(futureTask.get());//表示获取线程的返回值
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
}
ExecutorService 创建一个有十条线程的线程池 ThreadPool
public class Test {
    public static void main(String[] args) {
        //创建一个有十条线程的线程池
        ExecutorService es= Executors.newFixedThreadPool(10);

        Mycallable mycallable=new Mycallable();

        //Future f = es.submit(mycallable);
        Future f = es.submit(mycallable);

        while (!f.isDone()){
            try {
                System.out.println(f.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        es.shutdown();
    }
}
public class MyRunnable implements Runnable{

    //四种创建线程方式
    //sleep join
    //锁
    //wait notify
    @Override
    public void run() {
        System.out.println("线程执行");

    }
}

线程池

线程池是一种用于优化线程管理的技术,它可以在应用程序启动时预先创建一组线程并保存在内存中,以避免频繁地创建和销毁线程。线程池通过限制和管理资源,降低了线程创建和销毁造成的消耗,同时提高了系统的响应速度和稳定性。在Java中,线程池通常使用java.util.concurrent包中的Executor框架来实现。

线程池的优势有以下几点:

  1. 降低资源消耗:通过重复利用已创建的线程,避免线程创建和销毁的开销。
  2. 提高响应速度:当任务到达时,任务不需要等待线程创建,立即执行。
  3. 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性。线程池可以进行统一的分配、调优和监控。

Java的Executor框架是线程池的一种实现方式,它提供了线程工厂、队列以及拒绝策略等,让并发编程变得更加简单。在Java中,常见的线程池有:

  1. FixedThreadPool:固定数量的线程池,当线程到达最大数量后,任务会被放入队列中等待。适用于后台或长时间运行的任务。
  2. CachedThreadPool:可缓存线程池,线程数量未达到最大值时,会立即执行任务。当任务过多导致线程不够用时,会创建新线程,但超过一定时间未被使用的线程会被销毁。适用于执行大量短期异步任务。
  3. ScheduledThreadPool:计划线程池,可以定时执行任务或周期性执行任务。适用于需要定时执行或周期性执行的任务。

使用线程池的好处在于可以更好地控制并发任务的执行,同时避免了大量线程的创建和销毁,提高了系统的性能和稳定性。在实际应用中,需要根据具体场景选择合适的线程池和参数配置,以达到最佳的性能效果。

wait notify

常见方法
方法名称 说明
void wait() 当前线程等待,直到被其他线程唤醒
void notify() 随机唤醒单个线程
void notifyAll() 唤醒所有线程

你可能感兴趣的:(java,java,开发语言)