多线程

1.什么是进程

真正运行时的程序,才被称为进程。

单核cpu一次只能运行一个进程;宏观并行,微观串行。

2.什么是线程:又称轻量级进程,程序中的一个顺序控制流程,同时也是CPU的基本调度单位。进程又多个线程组成,

彼此间完成不同的工作,交替执行,称为多线程。

例如:迅雷是一个进程,当中的多个下载任务既是多线程。

           java虚拟机是一个进程,当中默认包含主线程(main)。

3.线程的组成:

CPU时间片:操作系统会为每个线程分配执行时间。

运行数据:堆空间:存储线程需使用对象,读多个线程可以共享堆中的对象。

                  栈空间:存储线程需使用局部变量,每个线程都拥有独立的栈。

线程的逻辑代码

4.创建线程:

创建线程的第一种方式:

创建线程的第二种方式:

5.常用的方法:

休眠:public static void sleep(long millis)表示当前线程主动休眠millis毫秒

放弃:public static void yield()表示当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片

结合:public static void join()表示允许其他线程加入到当前线程中。

6.线程的安全

有两种线程安全同步方式:

(1)同步代码块:synchronize(临界资源对象){代码}//对临界资源对象加锁

(2)同步方法:synchronize 返回值类型 方法名称(形参列表){代码}//对当前对象this加锁

JDK中线程安全的类:

StringBuff

Vector

Hashtable

以上类中的公开方法,均为synchronize修饰的同步方法。

死锁:

当第一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁。

一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,由此可能造成死锁。

线程通信:

等待:public final void wait()

通知:public final void notifyAll()

消费者与生产者案例:

package com.qf.t1;

public class TestShop {

	public static void main(String[] args) {
		Shop shop = new Shop();//共享资源对象
		Thread p = new Thread(new Product(shop),"生产者");
		Thread c = new Thread(new Customer(shop),"消费者");
		p.start();
		c.start();
	}
}
class Goods{
	private int id;
	public Goods(int id) {
		this.id = id;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	
}
class Shop{
	Goods goods;
	boolean flag;//标识商品是否充足
	public synchronized void saveGoods(Goods goods)throws  InterruptedException{
		//1.判断商品是否充足
		if(flag == true){
			System.out.println("商品充足");
			this.wait();//商品充足,生产者不用生产,而等待消费者卖完,进入等待状态!
		}
		//商品不充足!生产者生产,并存放商场里
		System.out.println(Thread.currentThread().getName()+"生产并存放了"+goods.getId()+"件商品");
		this.goods = goods;
		flag = true;//已经有商品了
		//消费者消费
		this.notifyAll();//将等待消费的消费者唤醒,前来购买商品!
	}
	public synchronized void buyGoods()throws  InterruptedException{
		if(flag == false){//没有商品了,消费者需要等待
			System.out.println("商品不充足,需等待");
			this.wait();//消费者进入等待!等待生产者生产之后,唤醒!
		}
		System.out.println(Thread.currentThread().getName()+"购买了"+goods.getId()+"件商品");
		this.goods = goods;
		flag = false;
		//并通知生产者生产
		this.notifyAll();
	}
}
//生产者
class Product implements Runnable{
	Shop shop;
	public Product(Shop shop) {
		this.shop = shop;
	}
	public void run(){
		//通过循环,生产商品放到商场里
		for(int i= 0; i<30;i++){
			try {//生产者线程调用存商品的方法。传递一个对象
				this.shop.saveGoods(new Goods(i));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
//消费者
class Customer implements Runnable{
	Shop shop;//商场
	public Customer(Shop shop) {
		this.shop = shop;
	}
	public void run(){
		//循环购买商品
		for(int i= 0; i<30;i++){
			try {
				this.shop.buyGoods();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}



高级多线程

线程池:线程容量,可设定线程分配的数量上限。

              将预先创建的线程对象存入池中,并重用线程池中的线程对象。

              避免频繁的创建和销毁。

通过newFixedThreadPool(int nThread) 传入参数,获取固定数量的线程池。

通过newCachedThreadPool()获得动态数量的线程池,没有上限。

Callable接口:interface Callable<V>{

                             public V call()Throws Exception;

                            }

具有返回值,可以声明异常。

Future接口:异步接收ExecutorService.submit()所返回的状态结果,包含了call()的返回值

V get方法。案例:

package com.qf.t1;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestFuture {

	public static void main(String[] args) throws InterruptedException, ExecutionException {
		ExecutorService es = Executors.newFixedThreadPool(2);
		MyCall mc = new MyCall();
		MyCall2 mc2 = new MyCall2();
		//通过submit执行提交的任务,Future接受返回结果
		Future<Integer> result = es.submit(mc);
		Future<Integer> result2 = es.submit(mc2);
		//通过Future的get方法,获得线程执行完毕后的结果
		Integer value = result.get();
		Integer value2 = result2.get();
		System.out.println(value + value2);
	}
}
//计算1~50的和
class MyCall implements Callable<Integer>{
	@Override
	public Integer call() throws Exception {
		Integer sum = 0;
		for(int i = 1;i<=50;i++){
			sum = sum+i;
		}
		return sum;
	}
}
//计算50~100的和
class MyCall2 implements Callable<Integer>{
	@Override
	public Integer call() throws Exception {
		Integer sum = 0;
		for(int i = 51;i<=100;i++){
			sum = sum+i;
		}
		return sum;
	}
}


Lock接口:与synchronize比较,现实定义,结构更灵活

常用方法:void lock()//获取锁,如锁被占用,则等待。

                  boolean tryLock()//尝试获取锁

                  void unlock()//释放锁

重入锁:

读写锁:

ReentrantReadWriteLock:一种支持一写多读的同步锁,读写分离,可分配读锁,写锁

                                            支持多次分配读锁,使多个读操作可以并发执行

互斥规则:

写-写:互斥,阻塞

读-写:互斥,读阻塞写,写阻塞读

读-读:不互斥,不阻塞

Collections中的工具方法:

CopyOnWriteArrayList:(声明方式List<String> list = new CopyOnWriteArrayList<String>();)

线程安全的ArrayList,加强版读写分离。

写有锁,读无锁,读写之间不阻塞,优于读写锁

写入时,先copy一个容器副本,再添加新元素,最后替换引用

使用方式与ArrayList无异

CopyOnWriteArraySet:(声明方式List<String> list = new CopyOnWriteArraySet<String>();)

线程安全的Set,底层使用CopyOnWriteArrayList实现

唯一不同在于:使用addIfAbsent()添加元素,会遍历数组

如存在元素,则不添加。

ConcurrentHashMap

初始容量默认16段(Segment),使用分段锁设计

不对整个Map加锁,而是为每个Segment加锁

当多个对象存入同一个Segment时,才需要互斥

最理想状态为16个对象分别存入16个Segment,并行数量16

使用方式与HashMap无异

Queue接口(队列)

Queue<String> queue = new ConcurrentLinkedQueue<String>();

BlockingQueue接口(阻塞队列)

Queue的子接口,阻塞的队列,增加了两个线程状态为无限期等待的方法。

void put(E e)//将指定元素插入此队列中,如果没有空间则等待。

E take()//获取并移除此队列头部元素,如果没有可用元素则等待。

阻塞队列:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值