java多线程

进程和线程

进程:一个内存中运行的应用程序,每个进程都有一个独立内存。

线程:进程的一个执行路径,共享一个内存空间,线程之间可以自由切换,互不影响,并发执行,一个进程至少有一个线程。

线程的调度

分时调度:

所有线程轮流使用cpu,平均分配每个线程占用cpu的时间

抢占式调度:

优先让优先级高的西安城使用cpu,如果线程优先级相同,则随机选择一个线程使用cpu,Java里用的是抢占式调度。

多线程的优势:

并不能提高程序运行速度,但是能提高程序运行效率,使cpu使用率更高。

同步和异步

同步:排队执行,效率低但是安全(Vector,Hashtable)

异步:同时执行,效率高但是数据不安全(ArrayList,HashMap)

并发和并行

并发:指两个或多个事件在同一时间段内发生

并行:指两个或多个事件在同一时刻发生(同时发生)

用户线程和守护线程

用户线程(非守护线程):当一个进程不再包含任何的存活的用户线程时,进程结束

守护线程:守护用户线程,当最后一个用户线程结束时,所有守护线程死亡(main线程不能设置为守护线程)

多线程的实现

1.继承Thread类
2.实现Runnable接口

3.实现Callable接口(带有返回值)

Thread类

使用Runnable接口和继承thread实现多线程都要用到Thread类

构造方法:

Thread (Runnable target,String name)//参数1:传入一个Runnable对象,分配任务,参数2:设置此线程名称

常用方法:

setDaemon(boolean on) //将此线程设置为守护线程或用户线程,值为true则设置为i守护线程

start()//线程执行,java虚拟机调用此线程的run方法

interrupt()//中断此线程

sleep(long millis)//让当前正在执行的线程休眠指定毫秒数,毫秒结束之后,继续执行

setPriority()//设置此线程的优先级

获取此线程的线程名称

Thread.currentThread().getName();//currentThread()方法返回对当前正在执行的线程对象的引用,getName()方法获取名称

继承Thread类实现多线程

实现步骤:
        1. 创建一个Thread类的子类
        2. 在Thread类的子类中重写Thread类中的run方法,设置线程任务
        3. 创建Thread类的子类对象
        4. 调用Thread类中的方法 start,开启新的线程,执行run方法 void start()使线程开始执                   行:JVM会自动调用该线程的 run 方法

格式:

static class MyThread extends Thread{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }
public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new MyThread();
        t.start();
        
    }

 MyThread对象只是用了一次,可以使用匿名内部类方式直接写一个线程任务

public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        new Thread("我叫thread"){
            @Override
            public void run() {
                for (int i=0;i<10;i++){
                    System.out.println(Thread.currentThread().getName()+"Thread线程执行了"+i);
                    try {
                        sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("main线程执行了" +i);
            Thread.sleep(1000);
        }
    }

实现Runnable接口

实现步骤:
    1. 创建一个Runnable接口的实现类(可以使用匿名内部类)
    2. 实现类中需重写run方法,设置线程任务
    3. 创建一个Runnable接口的实现类对象
    4. 创建Thread类对象,构造方法中传递Runnable对象
    5. 调用Thread类的start方法,开启run方法

格式1:

static class MyRunnable implements Runnable{

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }
MyRunnable mr = new MyRunnable();
        Thread t =new Thread(mr);
        t.start();

格式2:

public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
       new Thread(new Runnable() {
           @Override
           public void run() {
               System.out.println(Thread.currentThread().getName());
           }
       }).start();

    }

实现Runnable接口比继承Thread类相比有以下优势:

1.通过创建任务,然后分配给现成的方式来实现多线程,更适合多个线程同时执行一个任务的情况

2.可以避免单继承带来的局限性

3.提高了程序的健壮性

4.线程池技术,可以接收Runnable类型的任务,而不能接收Thread类型的线程

线程安全

当多个线程并发执行访问一个共享资源时,一个线程可能还未结束执行,另一个线程已经抢占了cpu的使用权,导致数据变更或者丢失的情况,这样线程是不安全的。

解决方法

1.同步代码块

2.同步方法

3.Lock锁

(1)同步代码块

格式:

synchronized(锁对象){
        可能会引发线程安全问题的代码块
}

注意:

锁可以是任何对象,线程本身也可以是锁对象

所有的线程都要关注同一把锁,锁对象创建不能写在run方法里这样会导致每个线程有一把琐,无法起到同步的效果

举例:三个站台共同卖10张票

public class Dem02 {
    public static void main(String[] args) {
        Ticket t = new Ticket();
        //三个站台一起卖票
        new Thread(t,"一号站台").start();
        new Thread(t,"二号站台").start();
        new Thread(t,"三号站台").start();

    }
    static class Ticket implements Runnable{
        /**
         * Synchronized(锁对象){   需要线程排队执行的任务    }
         */
        //票数
        private int count = 10;
        //创建锁对象
        Object o = new Object();
        @Override
        public void run() {
            //卖票
            while(true) {
                synchronized (o) {
                    if (count > 0) {
                        System.out.println(Thread.currentThread().getName() + "准备卖票。。。");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName() + "卖票成功,还剩票数:" + count);
                    } else {
                        break;
                    }
                }
            }
        }
    }
}

(2)同步方法

格式:

把需要排队执行的任务代码块封装成方法,再用synchronized修饰方法,就完成了线程排队操作

public synchronized void Sale(){

public class Dem02 {
    public static void main(String[] args) {
        Ticket t = new Ticket();
        //三个站台一起卖票
        new Thread(t,"一号站台").start();
        new Thread(t,"二号站台").start();
        new Thread(t,"三号站台").start();

    }
    static class Ticket implements Runnable{
        /**
         * Synchronized(锁对象){   需要线程排队执行的任务    }
         */
        private int count = 10;
        //创建锁对象
        Object o = new Object();
        @Override
        public void run() {
            //卖票
            while(true) {
              boolean flag = Sale();
              if(!flag){
                  break;
              }

            }

        }
        public synchronized boolean Sale(){
            if (count > 0) {
                System.out.println(Thread.currentThread().getName() + "准备卖票。。。");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println(Thread.currentThread().getName() + "卖票成功,还剩票数:" + count);
                return true;
            }
            return false;
        }
    }
}

(3)显式锁Lock

同步代码块和同步方法都属于隐式锁,Lock属于显式锁

步骤:

1.在任务类里创建Lock对象

2.在需要上锁的代码块前面调用lock()方法获取锁

3.代码块结束的地方调用unlock()方法进行解锁

格式:

Lock lock = new  ReentrantLock();//创建ReentraLock锁对象

lock.lock();

线程安全问题代码块

lock。unlock();

注意:lock 的构造方法里可以传入一个布尔类型的值,为true则是公平锁(先来先执行,排队),值为false,是不公平锁(线程随意争抢,谁先抢到谁先执行)

线程池

为什么要使用线程池

在 Java 语言中,创建一个线程看上去非常简单。实现Runnable接口,然后像创建一个对象一样,直接 new Thread 就可以了。

但实际上线程的创建和销毁远不是创建一个对象那么简单。线程的创建需要调用操作系统内核的 API,然后操作系统为其分配一系列资源,所以整个成本很高,导致线程是一个重量级的对象,应该避免频繁创建和销毁

ThreadPoolExecutor类

ava.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类,用于创建一个线程池对象

构造方法

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);

 CorePoolSize(核心线程数):当创建一个线程池以后,默认线程池内线程数为0,当有一个任务到达时,就会创建一个线程来执行任务,当线程数达到CorePoolSize时,接下来到达的任务会放在阻塞队列中。

maxmumpoolSize(线程池线程数):控制线程池最多创建线程的数量

keepAliveTime(最大存活时间):表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;

unit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:

  1. TimeUnit.DAYS; //天

  2. TimeUnit.HOURS; //小时

  3. TimeUnit.MINUTES; //分钟

  4. TimeUnit.SECONDS; //秒

  5. TimeUnit.MILLISECONDS; //毫秒

  6. TimeUnit.MICROSECONDS; //微妙

  7. TimeUnit.NANOSECONDS; //纳秒

workQueue:一个阻塞队列,用来存储等待执行的任务,线程池的排队策略与BlockingQueue有关

threadFactory:线程工厂,主要用来创建线程;

handler:表示当拒绝处理任务时的策略,

线程池的使用:

创建线程池,通过ThreadPoolExecutor类new一个线程池对象,或者通过来自java.util.concurrent.Executors: 线程池的工厂类,用来生成线程池,然后创建一个实现Runnable接口的实现类,重写run()方法,来设置任务,Executor是一个顶层接口,在它里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型,从字面意思可以理解,就是用来执行传进去的任务的;或者 通过java.util.concurrent.ExecutorService: 线程池接口submit (Runnable task) 提交一个 Runnable 任务执行,shutdown()和shutdownNow()是用来关闭线程池的。

Executors类包含四种常用的线程池:

定长线程池(FixedThreadPool)周期定长线程池(ScheduledThreadPool)单线程线程池(SingleThreadExecutor)缓存线程池(CashedThreadPool)

 Lambda表达式:

jdk1.8以后:

省略创建实现接口的类的操作,适用于只有一个抽象方法的接口

格式:
        (参数列表)-> {重写的方法里的代码块};

注意:参数列表可以有,可以没有,多个参数用逗号分隔开,只有一行代码时可以省略大括号,多行代码加{}并用分号分开,

例子:

new Thread(() -> {

System.Out.Println("Hello world");

})

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值