一、进程、线程
1.进程:正在运行的程序,是操作系统分配资源的基本单位。
目前操作系统都是支持多进程,介意同时执行多个进程,通过进程id区分。
2.线程:轻量级进程 是进程的一个执行路径也是CPU的基本调度单位,进程是多个线程组成。 一个进程至少有一个线程。
进程间不能共享数据地址,但同进程的线程可以。
二、线程的组成
cpu时间片、内存空间、线程的逻辑代码。
三、线程执行特点
抢占式执行,结果具有随机性;
在单核CPU中宏观伤同时执行,微观上顺序执行、多核cpu可实现真正的并发执行。
四、创建线程
1.继承Thread类重写run方法
2.实现Runnable接口
3.线程池创建线程
Executors工厂类 newFixedThreadPool(int nThreads) newCachedThreadPool()
4.Callable接口 具有泛型返回值,可以声明异常
public interface Callable<V>{ public V call() throws Exeption;}
public class MyThread extends Thread{
MyThread(String name){
super.setName(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
// 1. 获取线程名 和 线程ID 只适用于继承Thread类的方法使用
// this.getId() this.getName()
// 2. Thread.currentThread().getId() getName() [推荐]
// 获取当前执行的线程的id和名字
System.out.println(this.getId() + ":" + this.getName() + "子线程");
}
}
}
public class TestMyThread {
public static void main(String[] args) {
MyThread mT = new MyThread("AAAAAAAAAAAA");
// 修改线程名
mT.setName("a");
// 还可通过构造函数传递名
mT.start(); // 不要使用run 不然不是多线程
for (int i = 0; i < 100; i++) {
System.out.println("main线程");
}
}
}
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "----------------------");
}
}
}
public class TestRunnable {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread th = new Thread(mr);
th.start();
}
}
案例一、卖票
需求:四个窗口各卖100张票
public class SaleTicket extends Thread {
SaleTicket(String name){
super.setName(name);
}
private int ticket = 100;
@Override
public void run() {
while(true){
if(ticket <= 0){
break;
}
System.out.println(Thread.currentThread().getName() + "卖了第" + ticket + "张票");
ticket--;
}
}
}
五、线程的状态(基本)
-----等待状态------
到期 | sleep |
new(初始状态)------> 就绪状态 ------> 运行状态 -------->终止状态
| |
-----阻塞状态-------
六、线程方法
1.休眠 sleep(long millis)
当前线程主动休眠millis毫秒释放了cpu 不再争抢CPU
2. 放弃yield()
当前线程主动放弃cpu时间片回到就绪状态竞争下一次时间片
3. 加入 join()
允许其他线程加入到当前线程中。当前线程会阻塞,直到加入线程执行完毕。
4.优先级 setPriority(int)
线程优先级 1-10 默认为5 优先级越高 表示获取CPU机会越多
5.线程打断 interrupt()
打断线程抛出InterruptedException异常
6.守护线程 setDaemon(true) start之前设置
用户线程(前台线程) 守护线程(后台线程)
如果程序中所有前台线程都执行完毕了 后台线程会自动结束
public class TestThread2 {
public static void main(String[] args) {
DaemonThread dT = new DaemonThread("a");
dT.setDaemon(true); // 守护线程
dT.start();
for (int i = 0; i < 100; i++) {
System.out.println("主线程" + " ---------- "+ i);
}
}
static class DaemonThread extends Thread{
DaemonThread(String name){
super.setName(name);
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println(Thread.currentThread().getName() + "----" + i);
}
}
}
}
七、线程同步
同步:多个线程一个一个等待执行
异步:线程之间不需要等待执行
同步代码块:
synchrenized(锁对象){ // 对临界共享资源对象加锁
// 同步代码
}
注:任何引用类型对象都可以作为锁,一般使用临界资源或唯一应用类型对象作为锁。每个对象都有一个互斥锁标记,只是拥有对象互斥锁标记的线程,才能进入同步代码块。线程退出同步代码块时,会释放相应的互斥锁标记。
同步方法: 如果是非静态方法 锁是this 如果是静态方法 时 类名.class
synchrenized 返回值类型 方法名称(形参列表){
同步代码
}
八、死锁
当第一个线程拥有A对象锁标记 并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁。
九、 线程通信
等待:
public final void wait()
public final void wait(long timeout)
必须在对obj加锁的同步代码块中
调用obj.wait()时 此线程会释放拥有的所有锁标记 释放cpu进入等待队列。
通知:
public final void notify() 从等待队列中随机唤醒一个
public final void notfyAll() 唤醒所有等待线程
必须在对obj加锁的同步代码块中
从obj的等待队列中随机唤醒一个或全部线程。
案例 使用线程通信 实现存一次 取一次
package day04;
public class BankCard {
private double money;
private boolean flag; // true 有钱 可以取 不能存
public synchronized void save(double m){
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
this.money = this.money + m;
System.out.println(Thread.currentThread().getName() + "存了" + m + "余额是:" + this.money);
flag = true;
this.notify();
}
public synchronized void take(double m){
if(!flag){ // false 没钱不能取
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
this.money = this.money - m;
System.out.println(Thread.currentThread().getName() + "取了" + m + "余额是:" + this.money);
flag = false;
this.notify();
}
}
package day04;
public class Save implements Runnable{
private BankCard bankC;
Save(BankCard bc){
this.bankC = bc;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
bankC.save(1000);
}
}
}
package day04;
public class Take implements Runnable{
private BankCard bankC;
Take(BankCard bc){
this.bankC = bc;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
bankC.take(1000);
}
}
}
package day04;
public class TestBankCard {
public static void main(String[] args) {
BankCard bc = new BankCard();
Save s = new Save(bc);
Take t = new Take(bc);
Thread ts = new Thread(s);
Thread tt = new Thread(t);
ts.start();
tt.start();
}
}
十、生产者消费者
十一、线程池
线程容器,可设定线程分配的数量上限。
将预先创建的线程对象存入池中,并重用线程池中的线程对象。
避免频繁的创建和销毁。
常用的线程池接口和类 (java.util.concurrent)
Executor: 线程池的顶级接口
ExecutorService :线程池接口 可通过submit提交任务代码
Executors工厂类:通过此类可以获得一个线程池
通过newFixedThread(int nThreads)获取固定数量的线程池
通过newCachedThreadPool() 获得动态数量的线程池(不够了就创建)
通过newSingleThreadExecutor(); 创建单线程池
通过newScheduleThreadPool(int nThreads) 调度线程池
通过 newWorkStealingPool 采用工作窃取算法 双端队列 先查看自己的队列中是否有任务 没有的话 去别的线程窃取一个
package day04_5;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// Executor
/* ExecutorService
submit
shutDown
isTerminated
executors 工具类
newFixedThreadPool()
newCacheThreadPool()
newScheduledThreadPool()// 周期执行
* */
public class TestExecutors {
public static void main(String[] args) {
//创建线程池
// ExecutorService es = Executors.newFixedThreadPool(4);
ExecutorService es = Executors.newCachedThreadPool();
ExecutorService es = Executors.newWorkStealingPool();
// 创建任务
Runnable ticket = new Runnable() {
private int ticket = 100;
@Override
public void run() {
while (true) {
if (ticket <= 0) {
break;
}
System.out.println(Thread.currentThread().getName() + "卖了第" + ticket + "票");
ticket--;
}
}
};
// 提交
for (int i = 0; i < 4; i++) {
es.submit(ticket);
}
es.shutdown();
}
}
线程池的七个参数
ThreadPoolExecutor类的参数
corePoolSize: 核心线程数 maximumPoolSize :最大线程数
keepAliveTime 非核心线程的存活时间 unit :时间单位
workQueue: 工作队列 threadFactory 线程工厂
handler 拒绝策略 (AboryPolicy 中断 DiscardPolicy直接抛弃 DiscardOldestPolicy 把旧的抛弃 CallerRunsPolicy 线程池创建者执行 )
package day04_5;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/*
* 七个参数
* 1.核心线程数
* 2.最大线程数
* 3.非核心线程存过时间
* 4.时间单位
* 5.工作队列
* 6.线程工厂
* 7.拒绝策略
* */
public class TestThreadPoolExecutor {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1,
3,
60,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(1),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
// 创建任务并提交
for (int i = 0; i < 4; i++) {
int count = i;
executor.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始执行" + count);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "执行完毕");
}
});
}
executor.shutdown();
}
}
十二、Callable 接口
有返回值 可以抛出异常
需要将Callable 转换珵任务 然后交给Thread 通过FutureTask类
package day05;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
// 计算 1-100的和
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
System.out.println(Thread.currentThread().getName() + "计算结束");
return sum;
}
}
package day05;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class TestCallable {
public static void main(String[] args) {
MyCallable mc = new MyCallable();
// 需要将Callable 转换成任务
FutureTask task = new FutureTask(mc);
Thread th = new Thread(task);
th.start();
// 获取结果
Integer res = null;
try {
res = (Integer) task.get();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
System.out.println("结果:" + res);
}
}
(Callable接口实现多线程看上去是有点麻烦 不过好消息是 这个方法不会经常使用!!!)
十三、Callable接口更多的是和线程池配合使用
Future 代表人物将要执行完毕的结果
概念 异步接收ExecutorService.submit()所返回的状态结果,当中包含了call的返回值
package day05;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class TestCallable2 {
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(1);
MyCallable mc = new MyCallable();
Future<Integer> res = es.submit(mc);
try {
Integer i = res.get();
System.out.println("结果是:" + i);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
}
十四、Lock接口
jdk1.5之前的syn效率低 从无锁升级到重量级锁,从用户态切换到内核态
jdk1.6syn优化为四种锁 无锁 偏向锁 轻量级锁,重量级锁
常用方法
void lock() //获取锁
boolean tryLock()// 尝试获取锁 不阻塞
void unlock()
ReenTranLock可重入锁
package day05;
import java.util.concurrent.locks.ReentrantLock;
public class TestReentranLock {
public static void main(String[] args) {
Runnable r = new Runnable() {
private Integer ticket = 100;
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
try {
if (ticket < 0) {
break;
}
System.out.println(Thread.currentThread().getName() + "卖了第" + ticket);
ticket--;
} finally {
lock.unlock();
}
}
}
};
}
}
通过可重入锁实现存取钱
package day05;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;
public class TestReenTranLock3 {
public static void main(String[] args) {
Runnable take = new Runnable() {
private Bankcard bankCard = new Bankcard();
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
System.out.println(bankCard.getMoney() );
for (int i = 0; i < 10; i++) {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "存了1000余额是" + bankCard.getMoney());
bankCard.setMoney(bankCard.getMoney() + 1000);
} finally {
lock.unlock();
}
}
}
};
Runnable save = new Runnable() {
private Bankcard bankCard = new Bankcard();
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
System.out.println(bankCard.getMoney() );
for (int i = 0; i < 10; i++) {
lock.lock();
try {
if (bankCard.getMoney() < 1000) {
System.out.println("余额不足");
i--;
}
bankCard.setMoney(bankCard.getMoney() - 1000);
System.out.println(Thread.currentThread().getName() + "取了1000余额是" + bankCard.getMoney());
} finally {
lock.unlock();
}
}
}
};
// 线程池创建两个线程
ExecutorService es = Executors.newFixedThreadPool(2);
es.submit(save);
es.submit(take);
}
}
读写锁-- ReentrantReadWriteLock
一种支持一写多读的同步锁,读写分离,可分别分配读锁,写锁
package day05;
import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class readWrite {
private Integer number = 0;
// private ReentrantLock lock = new ReentrantLock(); // 可重入锁
private ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
Runnable read = new Runnable() {
@Override
public void run() {
// 18个线程读取
rw.readLock().lock();
// lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 读取到的数 " + number);
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
rw.readLock().unlock();
}
}
};
Runnable wrire = new Runnable() {
@Override
public void run() {
// 2个线程写入
rw.writeLock().lock();
// lock.lock();
try {
number = new Random().nextInt(100);
System.out.println(Thread.currentThread().getName() + " 写入到的数 " + number);
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
rw.writeLock().unlock();
// lock.unlock();
}
}
};
}
package day05;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestReadWriter {
public static void main(String[] args) {
readWrite rw = new readWrite();
ExecutorService es = Executors.newFixedThreadPool(20);
long start = System.currentTimeMillis();
for (int i = 0; i < 18; i++) {
es.submit(rw.read);
}
for (int i = 0; i < 2; i++) {
es.submit(rw.wrire);
}
long end = System.currentTimeMillis();
es.shutdown();
while (!es.isTerminated()) {
}
System.out.println("reentlock 用时 " + (end - start));
}
}
十五、Condition-条件队列
Condition接口也提供了类似Object的监视器方法,与Lock配合可以实现等待\通知模式
Condition 可以通俗的理解为条件队列,当一个线程在调用了await方法以后,直到线程等待的某个条件为真的时候才会被唤醒。
await —》当前线程进入等待状态
signal---->唤醒一个等待线程
使用Lock和Condition实现生产者和消费者
package day05_1;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Container {
private Bread[] breads = new Bread[6];
private int size;
private ReentrantLock lock = new ReentrantLock();
// 创建条件队列
// condition实例必须绑定在锁上
private Condition produceCondition = lock.newCondition();
private Condition consumerCondition = lock.newCondition();
public void input(Bread b) {
lock.lock();
try {
if (size >= breads.length) {
try {
produceCondition.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
breads[size] = b;
size++;
System.out.println(Thread.currentThread().getName() + "生产了" + b.getId());
consumerCondition.signal();
} finally {
lock.unlock();
}
}
public void output() {
lock.lock();
try {
if (size <= 0) {
try {
consumerCondition.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
size--;
Bread b = breads[size];
breads[size] = null;
System.out.println(Thread.currentThread().getName() + "消费了" + b.getId());
produceCondition.signal();
} finally {
lock.unlock();
}
}
}
案例 交替输出ABC
package day05_1;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Example {
private ReentrantLock lock = new ReentrantLock();
private Condition a = lock.newCondition();
private Condition b = lock.newCondition();
private Condition c = lock.newCondition();
private int num = 1;
public void printA() {
lock.lock();
try {
if (num != 1) {
try {
a.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} else {
System.out.println("A");
num = 2;
b.signal();
}
} finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
if (num != 2) {
try {
b.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} else {
System.out.println("B");
num = 3;
c.signal();
}
} finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
if (num != 3) {
try {
c.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} else {
System.out.println("C");
num = 1;
a.signal();
}
} finally {
lock.unlock();
}
}
}
package day05_1;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] args) {
Example e = new Example();
Runnable ra = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
e.printA();
}
}
};
Runnable rb = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
e.printB();
}
}
};
Runnable rc = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
e.printC();
}
}
};
ExecutorService es = Executors.newFixedThreadPool(3);
es.submit(ra);
es.submit(rb);
es.submit(rc);
es.shutdown();
}
}
到这里结束!欢迎大家补充!