多线程与并发
1、进程与线程
- 1、什么是进程
- 程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念,而进程是程序在处理机上的一次执行过程,他是一个动态的概念。
- 进程是一个具有独立功能的程序,一个实体,每一个进程都有他的自己的地址空间
- 2、进程的状态
- 进程执行的间断性,决定了进程可能具有多种状态,事实上,运行中的进程有以下的三种基本状态
- 3、线程
- 线程实际上是在进程基础上的进一步划分,一个线程启动之后, 里面的若干程序又可以划分成若干个线程
- 线程:是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少又一个线程(单线程程序)
- 4、线程实现的两种方式
- 在java中如果想要实现多线程的操作,可以由以下两个方式
public static void main(String[] args) {
//创建一个线程对象
MyThread t1 = new MyThread();
//t1.run();//直接调用不是启动线程,而是在本线程中执行方法
//开始执行
t1.start(); //线程已经准备好了 就绪状态 等待cpu的调度
}
/**
* 继承Thread实现线程
*
* @author wuyinlei
*
*/
static class MyThread extends Thread {
@Override
public void run() {
// 在此方法中编写要执行的工作
for (int i = 0; i < 10; i++) {
System.out.println(System.currentTimeMillis() + "-" + i);
}
}
}
* (2)实现Runnable接口(推荐使用,面象接口编程)
static class MyRunnable implements Runnable {
@Override
public void run() {
// 在此方法中编写要执行的工作
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "-" + System.currentTimeMillis() + "-" + i);
}
}
}
//要这样调用
MyRunnable r = new MyRunnable();
Thread t2 = new Thread(r);
t2.start();
2、线程的操作方法
- Thread(Runnable target) //分配新的Thread对象
- Thread(Runnable target,String name)//分配新的Thread对象
- Thread(String name) //分配新的对象
- static Thread cuttentThread() //返回对当前正在执行的对象的引用
- long getId()返回线程的标识符
- String getName()返回线程的名称
- void setName(String name) //改变线程的名称,使之与参数name相同
- boolean isAlive() //测试线程是否处于活动状态
- static void sleep(long millis) //休眠指定的毫秒数后继续执行
- static void sleep(long millis,int nanos) //不丢失任何监视器的所有权 //休眠指定的毫秒和纳秒只有继续执行
- void join() //等待线程终止
- void join(long millis) //等待该线程终止的时候最长为millis的毫秒
- void join(long millis,int nanos)
- Thread.yield()//让出当前正在执行的线程对象,并去执行其他线程
- boolean isDaemon()测试该线程是否为守护线程
- void setDaemon(boolean on) //将该线程标记为守护线程或者用户线程
- void interrupt() 终端线程
- static boolean interrupted()测试当前线程是否已经中断
- void setPriority(int newPrioritr) //更改线程的优先级
- static int MAX_PRIORITY //线程可以具有的最高优先级
- static int MIN_PRIORITY //线程可以具有的最低优先级
- static int NORM_PRIORITY //分配给线程的默认的优先级
- void nofify() //唤醒其他线程
3、线程同步
- 1、多线程共享数据
- 在多线程的操作中,多个线程有可能同时处理一个资源,这就是多线程中的共享数据
- 2、线程同步
- 解决线程共享问题,必须使用同步,所谓的同步就是指多个线程在同一个时间内只能有一个线程执行指定的代码,其他的线程要等待此线程执行完之后才可以继续执行
- 线程同步的方法(两种方式)--->同步会牺牲性能,来获取安全
- 1、同步代码块
- synchronized(要同步的对象){ 要同步的操作;}
- 2、同步方法
- public synchronized void method(){要同步的操作;}
- 3、Lock
// 互斥锁
private final ReentrantLock lock = new ReentrantLock();
public void eat() {
lock.lock(); // 上锁
/**
* 这里面做出来的逻辑
*/
lock.unlock(); // 解锁
}
- 同步准则
- 当编写synchronized块的时候,有几个简单的准则可以遵循,这些准则在避免死锁和性能危险的风险方面有很大的帮助
- (1)是代码块保持简短,把不随线程变化的预处理和后处理溢出synchronized块。
- (2)不要阻塞,如InputStream.read()
- (3)在持有锁的时候,不要对其他对象调用方法
4、死锁
- 过多的同步有可能出现死锁,死锁的操作一般是在程序运行的时候才有可能出现
- 多线程中要进行资源的共享,就需要同步,但同步过多,就可能造成死锁
- 中断线程(自定义中断)标志中断 让线程自己停止
5、生产者与消费者应用案例
- 多线程的开发中又一个最经典的操作案例,就是生产者-消费者,生产者不断生产产品,消费者不断的去走产品。
- 这里我们收下描述的是一个错乱的,我们看下运行的代码
* /**
* 生产者和消费者
* @author wuyinlei
*
*/
public class ThreadDemo {
public static void main(String[]args){
Food food = new Food();
Producter p = new Producter(food);
Customer c = new Customer(food);
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
//生产者
class Producter implements Runnable{
private Food food;
public Producter (Food food){
this.food = food;
}
@Override
public void run() {
//生产100份菜
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
food.setName("东北大乱炖");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
food.setEfficasy("好吃不贵真实惠");
} else{
food.setName("糖醋鲤鱼");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
food.setEfficasy("酸甜口");
}
}
}
}
//消费者
class Customer implements Runnable{
private Food food;
public Customer (Food food){
this.food = food;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(food.getName() + "--->" + food.getEfficasy());
}
}
}
//消费的对象
class Food{
public Food() {
super();
}
public Food(String name, String efficasy) {
super();
this.name = name;
this.efficasy = efficasy;
}
private String name; //菜名
private String efficasy; //功效
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEfficasy() {
return efficasy;
}
public void setEfficasy(String efficasy) {
this.efficasy = efficasy;
}
}
- 下面我们来下同步(对food的操作,那么我们就来对food进行同步)
package com.yinlei.thread;
import java.awt.PageAttributes;
/**
* 生产者和消费者
*
* @author wuyinlei
*
*/
public class ThreadDemo {
public static void main(String[] args) {
Food food = new Food();
Producter p = new Producter(food);
Customer c = new Customer(food);
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
// 生产者
class Producter implements Runnable {
private Food food;
public Producter(Food food) {
this.food = food;
}
@Override
public void run() {
// 生产100份菜
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
/*
* food.setName("东北大乱炖"); try { Thread.sleep(500); } catch
* (InterruptedException e) { e.printStackTrace(); }
* food.setEfficasy("好吃不贵真实惠");
*/
food.set("东北大乱炖", "好吃不贵真实惠");
} else {
/*
* food.setName("糖醋鲤鱼"); try { Thread.sleep(500); } catch
* (InterruptedException e) { e.printStackTrace(); }
* food.setEfficasy("酸甜口");
*/
food.set("糖醋鲤鱼", "酸甜口");
}
}
}
}
// 消费者
class Customer implements Runnable {
private Food food;
public Customer(Food food) {
this.food = food;
}
@Override
public void run() {
/*
* for (int i = 0; i < 100; i++) { try { Thread.sleep(500); } catch
* (InterruptedException e) { e.printStackTrace(); }
* System.out.println(food.getName() + "--->" + food.getEfficasy());
*/
food.get();
}
}
// 消费的对象
class Food {
private boolean flag = true;// true表示可以生产 false表示可以消费
public Food() {
super();
}
public Food(String name, String efficasy) {
super();
this.name = name;
this.efficasy = efficasy;
}
private String name; // 菜名
private String efficasy; // 功效
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEfficasy() {
return efficasy;
}
public void setEfficasy(String efficasy) {
this.efficasy = efficasy;
}
// 生产产品
public synchronized void set(String name, String efficasy) {
// 表示不能生产
if (!flag) {
try {
this.wait(); // 当前线程进入等待状态,但是会让出cpu,并且释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.setName(name);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setEfficasy(efficasy);
flag = false; // 表示不能生产
this.notify(); // 唤醒该监视器上的其他线程
}
// 消费产品
public synchronized void get() {
if (flag) {
try {
this.wait(); // 当前线程进入等待状态,但是会让出cpu,并且释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getName() + "--->" + this.getEfficasy());
flag = true; // 表示不能再取
this.notify(); // 唤醒该监视器上的其他线程
}
}
6、线程生命周期
7、线程池
- 线程池是预先创建线程的一种技术,线程池在还没有任务到来之前,创建一定数量的线程,放入空闲队列中,然后对这些资源进行复用,减少频繁的创建和销毁对象,
- JDK1.5版本以上提供了线程池
- Java里面线程池的顶级接口是Executor,是一个执行线程的工具,线程池的接口是ExecutorService
- 在 Executors类里面提供了一些静态工厂,生成一些常用的线程池
- newSingleThreadExecutor
- 创建一个单线程的线程池,这个线程池只有一个线程在工作,也就是相当于单线程执行所有的任务,如果这个唯一的线程因为异常结束,那么会有一个新的线程来代替,此线程池保证所有任务的执行顺序按照任务的提交顺序
- newFixedThreadPool:
- 创建固定大小的线程池,每次提交一个任务就创建一个线程,直到达到线程池的最大大小
- 线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新的线程
- newCacheThreadPool:
- 创建一个可缓存的线程,如果鲜橙汁的大小超过了处理任务的所需要的线程,那么就会回收部分空余时间(60s不执行任务)的线程,当任务增加时,此线程池又可以智能的添加新线程来处理任务,此线程池不会对线程池的大小做限制,线程池的大小完全依赖于操作系统(或者JVM)能够创建的最大线程大小
- newScheduledTheradPool:
- 创建一个大小无限制的线程池,此线程池支持定时以及周期性的执行任务的需求