help —> Keymap Reference 找到快捷键
Java多线程
1.进程和线程
1.1串行与并行
- 串行:多个任务按顺序执行;
- 并行:多个任务同时执行;
1.2并行与并发
- 并行:多个CPU实例或者多台机器同时执行一段逻辑处理,是真正的同时;
- 并发:通过CPU调度算法,让用户看上去同时执行,实际上CPU操作层面不是真正的同时;
1.3进程与线程
- 进程:
- 具有独立的执行环境;
- 具有一套完整的私有基本运行时资源;
- 每个进程都有自己的存储空间;
- 线程:
- 有时称为轻量级进程;
- 创建新线程所需要的资源少于创建新进程的资源;
- 一个进程至少有一个线程;
1.4 线程状态
- 一般说的是线程并发处理,进程并发处理太复杂;
- 实例化
- new状态(初始):创建一个线程对象;
- start状态
- 运行状态:
- 就绪状态:start之后直接先来就绪状态;
- 运行中状态:
- 阻塞状态:同步语句 synchronized
- 等待状态:
- 等待:写出参数就是超时等待;
- 超时等待:
- 终止状态:表示该线程已经执行完毕
2.线程对象
2.1 线程定义:
- 实现Runnable接口:提供一个Runnable兑现个,该接口定义了一个方法,run用于包含在线程对象
public class HelloRunnable implements Runnable {
public void run() {
System.out.println("HelloRunnable");
}
- 继承Thread类:子类Thread。在Thread类自身实现Runnable,但其run方法不起作用。应用程序可以子类Thread提供自己的实现run。
public class HelloThread extends Thread {
public void run() {
System.out.println("HelloThread");
}
2.2 线程命名
所有的线程程序的执行,每一次都是不同的运行结果,如果要想区分每一个线程,那么久必须依靠线程的名字。对于线程的名字一般而言会在启动之前进程定义,不建议对已经启动的线程,进行更改名称,或者为不同线程设置重名的情况。
//Runnable接口:
public class HelloRunnableName implements Runnable {
@Override
public void run() {
System.out.println("你好:" + Thread.currentThread().getName());
}
//Thread类
public class HelloThreadName extends Thread {
public HelloThreadName(String name) {
// super(name);
this.setName(name);
}
@Override
public void run() {
super.run();
System.out.println("你好:" + Thread.currentThread().getName());
}
2.3 线程优先级
- 线程优先级的范围是1~10,默认的优先级是5;最高是10;
//Runnable接口:
public static void main(String[] args) {
ThreadPriority threadPriority = new ThreadPriority();
Thread thread1 = new Thread(threadPriority);
thread1.setName("线程1");
thread1.setPriority(Thread.MIN_PRIORITY);
Thread thread2 = new Thread(threadPriority);
thread2.setName("线程2");
thread2.setPriority(Thread.MAX_PRIORITY);
thread1.start();
thread2.start();
}
}
2.4 线程休眠
Thread.sleep使当前线程在指定时间段内暂停执行。这是使处理器时间可用于应用程序的其他线程或计算机系统上可能正在运行的其他应用程序的有效方法。
- sleep(long millis)
2.5 线程礼让:
Thread类中提供了一种礼让方法,使用yield()方法表示,它只是给当前正处于运行状态下的线程一个提醒,告知它可以将资源礼让给其他线程,但这仅仅是一种暗示,没有任何一种机制保证当前线程会将资源礼让。
- yield()方法
2.6 线程联合:
join方法允许一个线程等待另一线程的完成。如果t是Thread正在执行其线程的对象,t.join();导致当前线程暂停执行,直到t的线程终止。重载join允许程序员指定等待时间。但是,与一样sleep,join依赖于OS进行计时,因此不应该假定join它将完全按照您指定的时间等待。
- t.join();
2.7 线程停止
在 Java 中有以下 3 种方法可以终止正在运行的线程:
- 使用退出标示,使得线程正常退出,即当run方法完成后终止进程;
- 使用stop强行中断线程(此方法为作废过期方法),不推荐使用。
- 使用interrupt方法中断线程,不推荐使用
2.8 守护线程
- Java线程分为两种:用户线程和守护线程;
- 用户线程:系统的工作线程,它会完成这个程序要完成的业务员操作;
- 守护线程:一种特殊的线程,就和它的名字一样,它是系统的守护者,在后台默默完成一些系统性的服务,比如垃圾回收线程。
如果用户线程全部结束,则意味着这个程序无事可做。守护线程要守护的对象已经不存在了,那么整个应用程序就结束了。
此处栗子:龟兔赛跑
3.线程同步
3.1 线程冲突
- 当在不同线程中运行作用于相同数据的两个操作时,就会发生干扰,这意味着这两个操作由多个步骤组成,并且步骤顺序重叠;
- 检索的当前值c;
- 将检索到的值加1;
- 将增加的值存储回c;
此处栗子:售票员们卖100张票
3.2 同步语句
- 同步语句(synchronized statements):创建同步代码的一种方法是使用同步语句。与同步方法不同,同步语句必须指定提供内部锁的对象。同步语句对于通过细粒度同步提高并发性很有用。
- 同步方法(synchronized methods):只需要将synchronized关键字添加到其声明中
此处栗子:增加同步后的售票员卖票
4.线程死锁
- 死锁是一种特定的程序状态,在实体之间,由于循环依赖导致彼此一直处于等待自重,没有任何个体可以继续前进;
- 死锁不仅仅在线程之间会发生,存在资源独占的进程之间同样也可能出现死锁。通常来说,我们大多是聚焦在多线程场景中的死锁,指两个或多个线程之间,由于互相持有对方需要的锁,互相等待,而永久处于阻塞状态。
5.线程协调
- wait()方法和notify()方法;
- 发生死锁的条件
- 互斥条件
- 请求保持条件
- 不剥夺条件
- 循环等待条件
public class ThreadCoordinate {
public static void main(String[] args) {
CoordinateA coordinateA = new CoordinateA();
Thread threadA = new Thread(coordinateA, "线程1");
CoordinateB coordinateB = new CoordinateB();
Thread threadB = new Thread(coordinateB, "线程2");
threadA.start();
threadB.start();
}
}
class CoordinateA implements Runnable {
@Override
public void run() {
synchronized ("A") {
System.out.println(Thread.currentThread().getName()
+ "持有了A锁,等待B锁。。。");
try {
"A".wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized ("B") {
System.out.println(Thread.currentThread().getName()
+ "持有了A锁和B锁");
}
}
}
}
class CoordinateB implements Runnable {
@Override
public void run() {
synchronized ("B") {
System.out.println(Thread.currentThread().getName()
+ "持有了B锁,等待A锁。。。");
synchronized ("A") {
System.out.println(Thread.currentThread().getName()
+ "持有了B锁和A锁");
"A".notify();
}
}
}
}
懒汉单例模式
6.高级并发对象
6.1线程定义
- 实现Callable接口
6.2线程同步:锁对象
同步代码依赖于一种简单的可重入锁。这种锁易于使用,但有很多限制。java.util.concurrent.locks软件包支持更复杂的锁定习惯用法 。Lock对象的工作方式非常类似于同步代码所使用的隐式锁。与隐式锁一样,一次只能有一个线程拥有一个Lock对象。Lock对象还wait/notify通过其关联的Condition对象支持一种机制 。
- reentrantLock.lock()
- reentrantLock.unlock()
6.3线程池
Java中创建和销毁一个线程是比较昂贵的操作,需要系统调用。频繁创建和销毁线程会影响系统性能。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new ThreadPool());
executorService.execute(new ThreadPool());
executorService.execute(new ThreadPool());
executorService.shutdown();
}
}
6.4并发集合:BLockingQueue
BlockingQueue实现被设计为主要用于生产者-消费者队列,如果BlockingQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态,直到BlockingQueue进了东西才会被唤醒。同样,如果BlockingQueue是满的,任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有空间时才会被唤醒。
//举个栗子:消费者消费产品的栗子
import java.util.concurrent.BlockingQueue;
public class Consumer implements Runnable {
BlockingQueue<Product> blockingQueue;
public Consumer(BlockingQueue<Product> blockingQueue) {
this.blockingQueue = blockingQueue;
}
@Override
public void run() {
try {
while (true) {
Product product = blockingQueue.take();
System.out.println("消费产品: " + product.getName());
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
6.5静态代理
静态代理就是在编译时就确定了代理类与被代理类的关系。
6.6Lambda表达式
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用 Lambda 表达式可以使代码变的更加简洁紧凑。
public class LambdaExpression {
// 2、静态内部类
static class Love2 implements ILove {
@Override
public void lambda() {
System.out.println("I Love Lambda 2");
}
}
public static void main(String[] args) {
// 1、外部类
ILove love = new Love();
love.lambda();
// 2、静态内部类
ILove love2 = new Love2();
love2.lambda();
// 3、局部内部类
class Love3 implements ILove {
@Override
public void lambda() {
System.out.println("I Love Lambda 3");
}
}
ILove love3 = new Love3();
love3.lambda();
// 4、匿名内部类
ILove love4 = new ILove() {
@Override
public void lambda() {
System.out.println("I Love Lambda 4");
}
};
love4.lambda();
// 5、Lambda
ILove love5 = () -> {
System.out.println("I Love Lambda 5");
};
love5.lambda();
}
}
6.7ArrayList
ArrayList不是线程安全的,他的add方法没有synchronized同步锁控制
6.8CopyOnWriteArray
CopyOnWriteArrayList是ArrayList 的一个线程安全的变体,其中所有可变操作(add、set等等)都是通过对底层数组进行一次新的复制来实现的。