多线程笔记


一、多线程概述

1.线程与进程

1.1线程(Thread)

  • 线程是CPU调度的最小单位
  • 是进程中的一个执行路径,一个进程启动后,里面的若干执行路径又可以被划分为若干个线程
  • 一个进程中至少有一个线程
  • 同一个进程中的多个线程可以共享部分内存(方法区,堆),每个线程之间有些内存又是独立的(栈:虚拟机栈,本地方法栈,程序计数器)

1.2进程(Process)

  • 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,进程与进程之间是无法直接共享内存的
  • 每个进程之间是独立的,操作系统分配资源时,是以进程为单位的
  • 两个进程之间进行切换,通信(交换数据)等操作时,成本比较高
  • 进程是线程的容器,进程中可容纳若干个线程。是程序的实体,程序是死的,静态的,进程是正在执行的。

2.线程调度

  • CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核心而言,某个时刻,只能执行一个线程,而CPU的在多个线程间切换速度相对于我们的感觉要快,看上去就是在同一时刻运行。其实,多线程程序并不能提高程序的运行速度,但能提高程序运行效率,让CPU的使用率更高。

2.1分时调度

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

2.2抢占式调度(Java使用)

  • 优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性)

3.同步与异步

同步和异步通常用来形容一次方法的调用

  • 同步:排队执行,效率低但安全。同步调用一旦开始,调用者必须等到方法调用返回后,才能继续执行。

  • 异步:同时执行,效率高但数据不安全。一旦开始,方法调用会立即返回,调用者可以继续后续工作,异步方法通常在另一个线程中真实执行,对于调用者来说似乎是瞬间完成了。

4.并发与并行

  • 并发:指两个或多个事件在同一个时间段内发生
  • 并行:指两个或多个事件在同一时刻发生(同时发生),真正意义上的同时执行

5.守护线程和用户线程

  • 用户线程:当一个进程不包含任何存活的用户线程时,进程结束
  • 守护线程:守护用户线程的,当最后一个用户线程结束时,所有守护线程自动死亡
public static void main(String[] args){
	Thread t=new Thread();
	t.setDaemon(true);//在启动线程前设置该线程为守护线程,当该进程中的用户线程都结束后,守护线程自动死亡
	t.start();
}

二、Java中的多线程

1.如何实现多线程

1.1继承Thread类

public class Demo{
	public static void main(String[] args){
		MyThread my=new MyThread();
		my.start();//启动线程,此时run和main是并发执行的
	}
}

public class MyThread extends Thread{

	@Override
	public void run(){
		//支线线程要执行的任务
	}
}

1.2 实现Runnable接口

public class Demo{
	public static void main(String[] args){
		//1.创建一个任务
		MyRunnable my=new MyRunnable();
		//2.创建一个线程并给他分配一个任务
		Thread t=new Thread(my);
		t.start();
	}
}

public class MyRunnable implements Runnable{

	@Override
	public void run(){
		//支线线程要执行的任务
	}
}

1.3实现Runnable与继承Thread相比的优点

1. 通过创建任务,然后给线程分配任务的方式来实现多线程,更适合多个线程同时执行相同任务的情况
2. 可以避免单继承带来的局限性
3. 任务与线程本身是分离的,提高了程序的健壮性
4. 线程池技术接受Runnable类型的任务,不接受Thread类型的线程

1.4带返回值的线程Callable

public static void main(String[] args){
	Callable<?> my=new MyCallable();
	FutureTask<> future=new FutureTask<>(my);
	new Thread(future).start();
	//task.get();此时主线程会停下来等子线程的返回值
	//task.isDone();判断该线程任务是否执行完毕
}
Class MyCallable implements Callable<T>{
	@Override
	public <T> call() throws Exception{
		return T;
	}
}

1.5Runnable与Callable相比

  • 相同点:
  1. 都是接口
  2. 都可以编写多线程程序
  3. 都采用Thread.start()启动线程
  • 不同点:
  1. Runnable没有返回值;Callable可以返回执行结果
  2. Callable接口的call()允许抛出异常;Runnable的run()不能抛出

2.线程安全问题

2.1同步代码块(隐式锁)

  • 多个线程应持同一个锁对象才有效
  • 线程同步可以解决线程安全问题,但会降低效率(排队减速)
synchronized(锁对象){
	//可能发生线程安全问题的代码
}

2.2同步方法(隐式锁)

  • 非静态方法,同步方法的锁是this
  • 如果是静态修饰的,那么锁对象是类名.class
//将需要同步的部分抽成一个方法
权限修饰符 synchronized 返回值类型 方法名(){
	//可能发生线程安全问题的代码
}

2.3显式锁Lock

//Lock子类ReentrantLock
private  Lock l=new ReentrantLock();
l.lock();
/*要加锁的代码*/
l.unlock;

3.公平锁与非公平锁

  • 公平锁:有先来后到的顺序
//fair默认是false(不公平锁),当传入参数是true时,为公平锁
Lock l=new ReentrantLock(boolean fair);
  • 非公平锁:线程一起抢,谁抢到锁算谁的。

4.多线程通信问题

//生产者与消费者问题
public class Demo {
    public static void main(String[] args) {
        Plant p=new Plant();
        new Waiter(p).start();
        new Cook(p).start();
    }
}

class Cook extends Thread{
    Plant p;

    public Cook(Plant p) {
        this.p = p;
    }
    @Override
    public void run() {
       while(true){
           synchronized (p){
           //假设平台上一次最多可以放十盘菜
               if(p.getCount()>=10){
                   try {
                       p.wait();
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
               p.setCount(p.getCount()+1);
               System.out.println("厨师生产了一盘菜,平台上还有:"+p.getCount()+"盘菜");
               p.notify();
           }
       }
    }
}

class Waiter extends Thread{
    Plant p;

    public Waiter(Plant p) {
        this.p = p;
    }

    @Override
    public void run() {
        while(true){
            synchronized (p){
                if(p.getCount()<=0){
                    try {
                        p.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                p.setCount(p.getCount()-1);
                System.out.println("服务员取走了一盘菜,平台上还有:"+p.getCount()+"盘菜");
                p.notify();
            }
        }
    }
}

class Plant{
    public int count=0;

    public int getCount() {
        return count;
    }

    public  void setCount(int count) {
        this.count = count;
    }
}

5.线程的六种状态

  • NEW 线程刚被创建,但尚未启动
  • RUNNABLE 正在执行的线程
  • BLOCKED 被阻塞等待监视器锁定的线程
  • WAITING 休眠的线程
  • TIMED WAITING 有限期的休眠线程
  • TERMINATED 死亡

三、线程池

1.线程池概述

  • 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。线程池就是一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。
  • 线程池的好处
  1. 降低资源消耗
  2. 提高响应速度
  3. 提高线程的可管理性

2.缓存线程池

  • 长度无限制
//1.判断线程池是否存在空闲线程
//2.存在则使用
//3.不存在,则创建线程 并放入池中,然后使用
public class Demo {
    public static void main(String[] args)  {
        ExecutorService service= Executors.newCachedThreadPool();
        service.execute(new Runnable(){
			@Override
			public void run(){
				//任务代码
			}
		});
    }
}

3.定长线程池

  • 长度是指定的数值
/*
1.判断线程池是否存在空闲线程
2.存在则使用
3.不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池,然后使用
4.不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
*/
public class Demo {
    public static void main(String[] args)  {
        ExecutorService service= Executors.newFixedThreadPool(nThread);
        service.execute(new Runnable(){
			@Override
			public void run(){
				//任务代码
			}
		}); 
    }
}

4.单线程线程池

/*
1.判断线程池的那个线程是否空闲
2.空闲则使用
3.不空闲,则等待池中的单个线程空闲后使用
*/
public class Demo {
    public static void main(String[] args)  {
        ExecutorService service= Executors.newSingleThreadExecutor();
        service.execute(new Runnable(){
			@Override
			public void run(){
				//任务代码
			}
		}); 
    }
}

5.周期定长线程池

/*
周期性任务执行时:定时执行,当某个时机触发时,自动执行某任务
1.判断线程池是否存在空闲线程
2.存在则使用
3.不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池,然后使用
4.不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
*/
public class Demo {
    public static void main(String[] args)  {
        ScheduledExecutorService service = Executors.newScheduledThreadPool(corePoolSize);
        /**
		 * 1.定时执行一次
		 * 参数1.定时执行的任务
		 * 参数2.时长数字
		 * 参数3.时长数字的时间单位,TimeUnit常量指定
         */
        service.schedule(new Runnable(){
        	@Override
			public void run(){
				//任务代码
			}
        },5,TimeUnit.SECONDS);
        
        /**
		 * 2.周期性执行任务
		 * 参数1.任务
		 * 参数2.延迟时长数字(第一次执行在什么时间后)
		 * 参数3.周期时长数字(每隔多久执行一次)
		 * 参数4.时长数字的时间单位,TimeUnit常量指定
         */
		service.scheduleAtFixedRate(new Runnable(){
			@Override
			public void run(){
				//任务代码
			}
		},5,1,TimeUnit.SECONDS);
    }
}

四、Lambda表达式

  • 函数式编程思想
//想要实现Lambda表达式,接口中只能包含一个方法
public class Demo {
    public static void main(String[] args)  {
       /*冗余的做法
       print(new MyMath() {
            @Override
            public int sum(int x, int y) {
                return x+y;
            }
        },1,2);*/
        print((int x,int y)->{
            return x+y;
        },1,2);
    }

    public static void print(MyMath m,int x,int y){
        int num=m.sum(x,y);
        System.out.println(num);
    }

    static interface MyMath{
        int sum(int x,int y);
    }
}

总结

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值