多线程

1.多线程技术概括

(1)线程与进程

进程

  • 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间

线程

  • 是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行. 一个进程最少有一个线程
  • 线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程

(2)线程调度

分时调度

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

抢占式调度

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

    Java使用的为抢占式调度。

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

(3)同步与异步

同步:排队执行 , 效率低但是安全.

异步:同时执行,效率高但是数据不安全.

2.Callable

(1)Runnable与Callable

//接口定义 
//Callable接口

public interface Callable<V> {
    V call() throws Exception;
}
//Runnable接口
public interface Runnable {
    public abstract void run();
}

(2)Callable使用步骤

//1.编写类实现Callable接口 , 实现call方法
class XXX implements Callable<T> {
    @Override
    public <T> call() throws Exception {
        return T;
    }
}
//2.创建FutureTask对象 , 并传入第一步编写的Callable类对象
FutureTask<Integer> future = new FutureTask<>(callable);
//3. 通过Thread,启动线程
new Thread(future).start();

(3)Runnable 与 Callable的相同点

  • 都是接口
  • 都可以编写多线程程序
  • 都采用Thread.start()启动线程

(4)Runnable 与 Callable的不同点

  • Runnable没有返回值;Callable可以返回执行结果
  • Callable接口的call()允许抛出异常;Runnable的run()不能抛出

(5)Callable获取返回值

Callable接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。

3.线程池 Executors

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

(1)线程池的好处

  • 降低资源消耗。
  • 提高响应速度。
  • 提高线程的可管理性。

(2)Java中的四种线程池 . ExecutorService

1缓存线程池

/**
 * 缓存线程池.
 * (长度无限制)
 * 执行流程:
 *		1. 判断线程池是否存在空闲线程
 *		2. 存在则使用
 *		3. 不存在,则创建线程 并放入线程池, 然后使用
 */
	ExecutorService service = Executors.newCachedThreadPool();
	//向线程池中 加入 新的任务
	service.execute(new Runnable(){
    	@Override
        public void run(){
            System.out.println("线程的名称:"+Thread.currentThread().getName());
        }
	});
	service.execute(new Runnable(){
    	@Override
        public void run(){
            System.out.println("线程的名称:"+Thread.currentThread().getName());
        }
	});
	service.execute(new Runnable(){
    	@Override
        public void run(){
            System.out.println("线程的名称:"+Thread.currentThread().getName());
        }
	});

2定长线程池

/*** 定长线程池. 
 * (长度是指定的数值)
 * 执行流程:
 * 	1. 判断线程池是否存在空闲线程 
 * 	2. 存在则使用 
 * 	3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
 * 	4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
 */
	ExecutorService service = Executors.newFixedThreadPool(2); 
	service.execute(new Runnable() {
    	@Override 
        public void run() { 
        	System.out.println("线程的名称:"+Thread.currentThread().getName());
    	}
	});
	service.execute(new Runnable() { 
    	@Override 
    	public void run() { 
        	System.out.println("线程的名称:"+Thread.currentThread().getName()); 
    	} 
	});

3单线程线程池

//效果与定长线程池 创建时传入数值1 效果一致. 
    /*** 单线程线程池. 
    * 执行流程: 
    * 1. 判断线程池 的那个线程 是否空闲 
    * 2. 空闲则使用 
    * 3. 不空闲,则等待 池中的单个线程空闲后 使用 
    */
	ExecutorService service = Executors.newSingleThreadExecutor(); 
	service.execute(new Runnable() { 
        @Override 
        public void run() { 
            System.out.println("线程的名称:"+Thread.currentThread().getName()); 
        } 
    }); 
	service.execute(new Runnable() { 
    	@Override 
    	public void run() { 
        	System.out.println("线程的名称:"+Thread.currentThread().getName()); 
    	} 
	});

4周期性任务定长线程池

public static void main(String[] args) { 
    /*** 周期任务 定长线程池. 
    * 执行流程: 
    * 	1. 判断线程池是否存在空闲线程 
    * 	2. 存在则使用 
    * 	3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用 
    * 	4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程 
    *
    * 周期性任务执行时: 
    * 		定时执行, 当某个时机触发时, 自动执行某任务 .
	*/
    
    ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
    /*** 定时执行 
    * 参数1. runnable类型的任务 
    * 参数2. 时长数字 
    * 参数3. 时长数字的单位 
    */
    service.schedule(new Runnable() { 
    	@Override 
    	public void run() { 
    		System.out.println("俩人相视一笑~ 嘿嘿嘿"); 
    	} 
    },5,TimeUnit.SECONDS); 
    
    /*** 周期执行 
    * 参数1. runnable类型的任务 
    * 参数2. 时长数字(延迟执行的时长) 
    * 参数3. 周期时长(每次执行的间隔时间) 
    * 参数4. 时长数字的单位 
    */
    service.scheduleAtFixedRate(new Runnable() { 
        @Override 
        public void run() { 
            System.out.println("俩人相视一笑~ 嘿嘿嘿"); 
        } 
    },5,2,TimeUnit.SECONDS); 
}

4.多线程的通信问题

Object中的几个主要方法

类型方法描述
voidnotify()唤醒正在此对象监视器上等待的单个线程。
voidnotifyAll()唤醒等待此对象监视器的所有线程。
voidwait()导致当前线程等待它被唤醒,通常是 通知中断
voidwait(long timeoutMillis)导致当前线程等待它被唤醒,通常是 通知中断 ,或者直到经过一定量的实时。
voidwait(long timeoutMillis,int nanos)导致当前线程等待它被唤醒,通常是 通知中断 ,或者直到经过一定量的实时。

生产者与消费者问题的源代码

package com.java.demo;

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

public class Demo4  {

    /**
     * 多线程通信问题, 生产者与消费者问题
     * @param args
     */
    public static void main(String[] args) {
        Food f = new Food();
        new Cook(f).start();
        new Waiter(f).start();
    }

    //厨师
    static class Cook extends Thread{
        private Food f;
        public Cook(Food f) {
            this.f = f;
        }

        @Override
        public void run() {
            for(int i=0;i<100;i++){
                if(i%2==0){
                    f.setNameAndSaste("老干妈小米粥","香辣味");
                }else{
                    f.setNameAndSaste("煎饼果子","甜辣味");
                }
            }
        }
    }
    //服务生
    static class Waiter extends Thread{
        private Food f;
        public Waiter(Food f) {
            this.f = f;
        }
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                f.get();
            }
        }
    }
    //食物
    static class Food{
        private String name;
        private String taste;

        //true 表示可以生产
        private boolean flag = true;

        public synchronized void setNameAndTaste(String name,String taste){
            if(flag) {
                this.name = name;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.taste = taste;
                flag = false;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        public synchronized void get(){
            if(!flag) {
                System.out.println("服务员端走的菜的名称是:" + name + ",味道:" + taste);
                flag = true;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

5.Thread

成员方法

变量和类型方法描述
StringgetName()返回此线程的名称。
voidsetName(String name)将此线程的名称更改为等于参数 name
static voidsleep(long millis)导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序的精度和准确性。
static voidsleep(long millis,int nanos)导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数加上指定的纳秒数,具体取决于系统定时器和调度程序的精度和准确性。

线程的六种状态(Enum Thread.State)

  • NEW

    尚未启动的线程处于此状态。

  • RUNNABLE

    在Java虚拟机中执行的线程处于此状态。

  • BLOCKED

    被阻塞等待监视器锁定的线程处于此状态。

  • WALTING

    无限期等待另一个线程执行特定操作的线程处于此状态。

  • TIMED_WAITING

    正在等待另一个线程执行最多指定等待时间的操作的线程处于此状态。

  • TERMINATED

    已退出的线程处于此状态。

6.线程安全

隐式锁

synchronized关键字
1.同步代码块
2.同步方法(在方法返回值前加synchronized)

显式锁

软件包 java.util.concurrent.locks
Interface Lock

方法

变量和类型方法描述
voidlock()获得锁。
voidunlock()释放锁定。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值