IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> java多线程技术整理 -> 正文阅读

[Java知识库]java多线程技术整理

首先明确几个概念:

  1. 进程与线程

进程:简单来讲,一个运行的程序可称为一个进程,每个进程在内存中都有一个独立的运行空间。

线程:是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行.。一个进程最少 有一个线程。

  1. 并行与并发

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

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

  1. 同步与异步

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

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

  1. 分时调度与抢占式调度

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

抢占式调度:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),在java中多线程采用的是抢占式调度。

**在java中创建多线程有三种方式:**1、继承Thread重写run()方法;2、实现Runnable接口重写run()方法;3、实现Callable接口重写cll()方法,创建FutureTask对象。

Thread

java.lang.Thread类的构造方法:

public Thread()
public Thread(Runnable target)
public Thread(String name)
public Thread(Runnable target, String name)

常用方法:

方法描述
public static native void sleep(long millis);线程睡眠指定毫秒
public static void sleep(long millis, int nanos);线程睡眠指定毫秒+纳秒
public synchronized void start();启动线程
public void interrupt();中断线程
public static boolean interrupted();测试线程是否是总中断的
public final void setPriority(int newPriority);设置线程优先级
public final String getName();获取线程名称
public State getState();获取线程状态
public static native Thread currentThread();返回当前线程
public final void setDaemon(boolean on);将此线程标记为守护线程或用户线程

举例如下:

public class ThreadDemo {
	private static final int LOOP_NUMBER = 10; //循环次数

	public static void main(String[] args) {
		Thread thread = new MyThread(); //创建MyThread对象,采用多态
		thread.start(); //调用strat()方法启动线程
		for (int i = 0; i < LOOP_NUMBER; i++) {
			System.out.println(Thread.currentThread().getName() + "汗滴禾下土" + i);
		}
	}
}
//创建MyThread类继承Thread类
class MyThread extends Thread {
	private static final int LOOP_NUMBER = 10; //循环次数
    //重写run()方法,为该线程要执行的任务
	@Override
	public void run() {
		for (int i = 0; i < LOOP_NUMBER; i++) {
			System.out.println(Thread.currentThread().getName() + "锄禾日当午" + i);
		}
	}
}

Runnable

实现Runnable接口重写run()方法,将创建实现对象,创建Thread传入实现对象。

public class RunnableDemo {
	private static final int LOOP_NUMS = 10;

	public static void main(String[] args) {
        //采用了Lambda表达式的方式
		Thread thread = new Thread(() -> {
			for (int i = 0; i < LOOP_NUMS; i++) {
				System.out.println(Thread.currentThread().getName() + "窗前明月光" + i);
			}
		});
		thread.start();

		for (int i = 0; i < LOOP_NUMS; i++) {
			System.out.println(Thread.currentThread().getName() + "疑是地上霜" + i);
		}
	}
}

Callable

java.util.concurrent.Callable接口使用步骤:

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

3. 通过Thread,启动线程 
    new Thread(future).start()

Runnable与Callable相同点

  • 都是接口 都可以编写多线程程序

  • 都采用Thread.start()启动线程

**Runnable 与 Callable的不同点 **

  • Runnable没有返回值;

  • Callable可以返回执行结果

  • Callable接口的call()允许抛出异常;Runnable的run()不能抛出

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

守护线程

java中的线程分为用户线程和守护线程,当最后一个用户线程死亡是,守护线程自动死亡。

设置线程为守护线程:

public static void main(String[] args) {
		Thread thread = new MyThread(); //创建MyThread对象,采用多态
		thread.setDaemon(true); //设置守护线程
  
}

线程的状态

Thread.State 线程有6中状态,6种状态可以转换。

public enum State {
        // 尚未启动的线程的线程状态。
       NEW,
        //可运行线程的读状态
        RUNNABLE,
        // 等待监视器锁定而阻塞的线程的线程状态
        BLOCKED,
		// 等待线程的线程状态。
        WAITING,
        // 具有指定等待时间的等待线程的线程状态 
        TIMED_WAITING,
        // 终止线程的线程状态。线程已经完成执行。
        TERMINATED;
    }

线程安全问题

多线程可能会造成数据不安全问题,因此需要采用同步的方式。

卖票案例:如果不采用同步技术,会造成线程不安全,最终余票会出现为负。

public class SynchronizedThreadDemo {
	public static void main(String[] args) {
		Ticket ticket = new Ticket(10);
		new Sale(ticket).start();
		new Sale(ticket).start();
	}
}

//卖票线程
class Sale extends Thread {
	Ticket ticket;
	Sale(Ticket ticket) {
		this.ticket = ticket;
	}
	@Override
	public void run() {
		while(true) {
            if(ticker.nums>0){
			System.out.println(Thread.currentThread().getName() + "正在卖票");
			ticket.nums--;
			System.out.println("卖出1张票,余票" + ticket.nums);
           }else{
                break;
            }
		}
	}
}

//票
class Ticket {
	int nums;

	public Ticket(int nums) {
		this.nums = nums;
	}
}

同步代码块

对以线程不安全代码采用同步代码块进行线程同步处理:

public class SynchronizedThreadDemo {
	public static void main(String[] args) {
		Ticket ticket = new Ticket(50);
		new Sale(ticket).start();
		new Sale(ticket).start();
	}

}

class Sale extends Thread {
	Ticket ticket;

	Sale(Ticket ticket) {
		this.ticket = ticket;
	}

	@Override
	public void run() {
		while (true) {
			synchronized (ticket) {
				if (ticket.nums > 0) {
					System.out.println(Thread.currentThread().getName() + "正在卖票");
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					ticket.nums--;
					System.out.println("卖出1张票,余票" + ticket.nums);
				} else {
					break;
				}
			}
		}
	}
}

	class Ticket {
		int nums;

		public Ticket(int nums) {
			this.nums = nums;
		}
	}


同步方法

synchronized 修饰的方法为同步方法,可保证线程安全。

例如StringBuffer类,所有方法同是同步方法,是线程安全的。而StringBuilder是异步的,是线程不安全的。
StringBuffer{
    public synchronized int compareTo(StringBuffer another) {
            return super.compareTo(another);
        }
}
-------------------------------------------------------------------------------------
StringBuilder{
    public int compareTo(StringBuilder another) {
        return super.compareTo(another);
    }
}

对以线程不安全代码采用同步方法进行线程同步处理:

public class SynchronizedThreadDemo {
	public static void main(String[] args) {
		Ticket ticket = new Ticket(50);
		new Sale(ticket).start();
		new Sale(ticket).start();
	}

}

class Sale extends Thread {
	Ticket ticket;

	Sale(Ticket ticket) {
		this.ticket = ticket;
	}
	@Override
	public void run() {
		while (true) {
			if (!ticket.sale()) {
				break;
			}
		}
	}
}

class Ticket {
	int nums;
	public Ticket(int nums) {
		this.nums = nums;
	}
	public synchronized boolean sale() {
		if (nums > 0) {
			System.out.println(Thread.currentThread().getName() + "正在卖票");
			try {
				Thread.sleep(200); //睡眠一定时间便于观察
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			nums--;
			System.out.println("卖出1张票,余票" + nums);
			return true;
		} else {
			return false;
		}
	}
}

显式锁Lock类

java.util.concurrent.locks.Lock 接口,Lock实现提供了比使用synchronized方法和语句可以获得的更广泛的锁定操作。

常用方法:

方法描述
void lock();获取锁
boolean tryLock();只有在调用时锁是空闲的时,才获取锁。
boolean tryLock(long time, TimeUnit unit);如果在给定的等待时间内锁是空闲的,并且当前线程没有被中断,则获取锁。
void unlock();释放锁。

Lock为接口,使用其实现类 java.util.concurrent.locks.ReentrantLock

ReentrantLock类的构造方法

public ReentrantLock()
public ReentrantLock(boolean fair)   //为true为公平锁

对以线程不安全代码采用Lock对象就行处理:

public class SynchronizedThreadDemo {
	public static void main(String[] args) {
		Ticket ticket = new Ticket(50);
		new Sale(ticket).start();
		new Sale(ticket).start();
	}

}

class Sale extends Thread {
	Ticket ticket;

	Sale(Ticket ticket) {
		this.ticket = ticket;
	}
	@Override
	public void run() {
		while (true) {
			try {
				ticket.lock.lock(); //获取锁
				if (ticket.nums > 0) {
					System.out.println(Thread.currentThread().getName() + "正在卖票");
					try {
						Thread.sleep(200);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					ticket.nums--;
					System.out.println("卖出1张票,余票" + ticket.nums);
				} else {
					break;
				}
			} catch (Exception e) {

			} finally {
				ticket.lock.unlock(); //释放锁
			}
		}
	}
}

class Ticket {
	int nums;
	Lock lock;
	{
		lock = new ReentrantLock(); //构造代码块,创建Ticket对象是创建Lock对象
	}
	public Ticket(int nums) {
		this.nums = nums;
	}
}

公平与非公平锁

能保证每个线程都能拿到锁,那么这个锁就是公平锁。相反,如果保证不了每个线程都能拿到锁,也就是存在有线程饿死,那么这个锁就是非公平锁。

构造公平锁:

public ReentrantLock(boolean fair)   //为true为公平锁

死锁

多线程可能会发生死锁,举个栗子:

public class DeadLockDemo {

	public static void main(String[] args) {
		Key1 key1 = new Key1();
		Key2 key2 = new Key2();
		Door1 door1 = new Door1(key1, key2);
		Door2 door2 = new Door2(key1, key2);
		new Thread(door1).start();
		new Thread(door2).start();
	}
}

class Door1 implements Runnable {
	Key1 key1;
	Key2 key2;

	Door1(Key1 key1, Key2 key2) {
		this.key1 = key1;
		this.key2 = key2;
	}

	@Override
	public void run() {
		while (true) {
			key1.lock1.lock();
			System.out.println("Door1打开的,等Door2关门后关门");
			key2.lock2.lock();
			System.out.println("全部关闭");
			key2.lock2.unlock();
			key1.lock1.unlock();
		}
	}
}
class Door2 implements Runnable {
	Key1 key1;
	Key2 key2;

	Door2(Key1 key1, Key2 key2) {
		this.key1 = key1;
		this.key2 = key2;
	}
	@Override
	public void run() {
		while (true) {
			key2.lock2.lock();
			System.out.println("Door2打开的,等Door1关门后关门");
			key1.lock1.lock();
			System.out.println("全部关闭");
			key1.lock1.unlock();
			key2.lock2.unlock();
		}

	}
}
class Key1 {
	Lock lock1;

	{
		lock1 = new ReentrantLock();
	}
}
class Key2 {
	Lock lock2;
	{
		lock2 = new ReentrantLock();
	}
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hqTL4uvf-1628783021802)(C:\Users\TanLong\AppData\Roaming\Typora\typora-user-images\image-20210812225600513.png)]

多线程之间的通信

采用Object类的以下方法;

方法描述
public final void wait();使当前线程等待直到被唤醒
public final native void wait(long timeoutMillis);导致当前线程等待,直到它被唤醒,或直到经过一定数量的实时时间。
public final native void notify();唤醒在此对象的监视器上等待的单个线程。如果有多个线程正在等待这个对象,那么随机其中一个线程将被唤醒
public final native void notifyAll();唤醒所有等待线程

多线程之间通信的经典问题:消费者与生产者问题

public class ProcucerAndComsumer {
	public static void main(String[] args) {
		Plate plate = new Plate();

		new Thread(() -> {
			int j = 10;
			while (j > 0) {
				synchronized (plate) {
						if (j % 2 == 0) {
							plate.setFood("鱼香肉丝");

						} else {
							plate.setFood("宫保鸡丁");
						}
						System.out.println("厨司做好了" + plate.getFood());
						j--;
						plate.notify(); //唤醒顾客线程
						try {
							plate.wait();  //厨司等待直到被唤醒
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
				}
			}
		}, "厨司").start();

		new Thread(() -> {
			int i = 10;
			while (i > 0) {
				synchronized (plate) {
					System.out.println("顾客吃" + plate.getFood());
					plate.setFood("");
					i--;
					plate.notify(); //唤醒厨司
					try {
						plate.wait();  //顾客等待被唤醒
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}, "顾客").start();
	}
}

class Plate {
	private String food;

	public void setFood(String food) {
		this.food = food;
	}

	public String getFood() {
		return food;
	}
}

线程池

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

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

工厂和工具方法Executor, ExecutorService, ScheduledExecutorService , ThreadFactory ,和Callable此包中定义的类。该类支持以下几种方法:

  • 使用常用配置设置创建和返回ExecutorService(ExecutorService.html)设置的方法。
  • 创建和返回ScheduledExecutorService(ScheduledExecutorService.html)设置的方法,使用常用的配置设置。
  • 创建并返回“包装”ExecutorService的方法,通过使特定于实现的方法不可访问来禁用重新配置。
  • 创建并返回将新创建的线程设置为已知状态的ThreadFactory的(ThreadFactory.html)方法。
  • 创建并返回一个方法Callable(Callable.html)出来的其他闭包形式,这样他们就可以在需要的执行方法使用Callable

Java中的四种线程池 . ExecutorService

缓存线程池

/**
	 * 缓存线程池.
	 * (长度无限制)
	 * 执行流程:
	 * 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());
	}
});

定长线程池

/**
	 * 定长线程池.
	 * (长度是指定的数值)
	 * 执行流程:
	 * 3. 单线程线程池
	 * 4. 周期性任务定长线程池
	 * 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());
			}
		});

单线程线程池

/**
 * 单线程线程池.效果与定长线程池 创建时传入数值1 效果一致.
 * 执行流程:
 * 1. 判断线程池 的那个线程 是否空闲
 * 2. 空闲则使用
 * 4. 不空闲,则等待 池中的单个线程空闲后 使用
 */
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());
			}
		});

周期性任务定长线程池

/**
* 周期任务 定长线程池.
* 执行流程:
* 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);
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-13 11:49:15  更:2021-08-13 11:53:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/31 14:15:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码