-
就是独立运行的一个程序, 是一个实体, 每个进程都有它自己的内存空间。
-
进程的状态
- 进程执行时的间断性,决定了进程可能具有多种状态。运行中的进程具有以下三种基本状态
-
- 就绪状态(Ready)
-
- 运行状态(Running)
-
- 阻塞状态(Blocked)
-
线程就是进程当中的一条执行路径,线程之间共享一个内存空间, 线程之间可以自由的切换, 并发执行。一个进程中可以多个线程,但是最少得有一个线程,
-
例子: 在电子厂流水线中, 一条流水线就表示一个进程, 而流水线上的人 就是线程, 也就是真正来组装产品的人, 在映射过来的话就是 线程就是真正来执行代码的。
-
并行: 当两个cpu核心运行两个任务得时候, 那么这个两个任务是同时运行,那么就是并行。(cpu核心数大于或等于运行任务数)
-
并发: 是指两个任务同时请求运行, 而处理器只有一个核心只能接收一个任务, 就会把两个任务轮流运行, 由于
处理器时间片(CPU运行该程序的时间)运行时间较短, 就感觉两个程序同时运行。
// 方法1: 继承Thread 类
class MyThread extends Thread {
public void run() {
// 当前线程要做的事情
for (int i = 0; i < 100; i++) {
// Thread.currentThread() 获取当前线程实例
// .getName() 获取当前线程名字
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
// main
// 创建线程
Thread t1 = new MyThread();
// 运行线程(先是就绪状态, 最终是JVM开启线程)
t1.start();
2) 实现Runnable 接口
// 方法2: 实现Runnable 接口
class MyRunnable implements Runnable {
public void run() {
// 当前线程要做的事情
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
// main
// 创建实例(Running 接口,可以理解为一个任务)
Runnable myRunnable = new MyThread();
// 通过Thread 类对Runnable进行包装(将任务添加到一个线程中)
Thread t2 = new Thread(myRunnable);
t2.start();
(3) 线程休眠
// 放在哪个线程里面,就表示哪个线程要休眠 Thread.sleep(毫秒数);
- 一旦当前线程休眠,则会释放CPU时间片, 一旦释放CPU时间片, 则其他线程会进行争取这个世时间片。
- 加入线程, 让调用的线程先去执行指定的时间或者执行完毕。
- 如果没有给定时间,则表示将该线程执行完毕 等待该线程的死亡。
// 可以指定毫秒数 和纳秒数 thread.join()
public class ThreadDemo2 {
// 主线程
public static void main(String[] args) {
MyRunnable2 myRunnable2 = new MyRunnable2();
// 创建一个子线程
Thread t1 = new Thread(myRunnable2);
t1.start();
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread() + "--" + i);
if (i == 20) {
try {
// 注意:此时主线程是阻塞的, 直到t1执行完毕
t1.join();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class MyRunnable2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + "--" + i);
try {
Thread.sleep(250);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
(5) 线程的中断
1) 方法1 通过interrupt() 方法 来中断
public class InterruptDemo {
public static void main(String[] args) {
MyRunnable3 myRunnable3 = new MyRunnable3();
Thread t1 = new Thread(myRunnable3);
t1.start();
for (int i = 0; i < 50; i++) {
if (i == 20) {
// 1. 中断t1线程
// 注意: 此时在线程中如果调用了sleep()方法 那么该方法会抛出了一个 InterruptedException 异常
t1.interrupt();
}
}
}
}
class MyRunnable3 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + "--" + i);
// 3. isInterrupte() 方法表示当前线程是否已经中断
if (Thread.currentThread().isInterrupted()) {
break;
}else {
try {
Thread.sleep(0);
}catch (InterruptedException e) {
e.printStackTrace();
// 2. 如果线程调用了interrupt方法, 则会在调用sleep方法处抛出InterruptedException异常
// 并且会自动去除线程中断异常, 一旦去除,则线程还是会运行。 所以interrpt() 方法只起到了标记的作用
// 所以需要在中断一次, 是为下一次循环判断
Thread.currentThread().interrupt();
}
}
}
}
}
2) 方法2 自定义中断线程
public static void main(String[] args) {
MyRunnable4 myRunnable4 = new MyRunnable4();
Thread t2 = new Thread(myRunnable4);
t2.start();
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + "--" + i);
try {
Thread.sleep(250);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (i == 20) {
// 2. 中断t1线程
myRunnable4.flag = false;
}
}
}
}
class MyRunnable4 implements Runnable {
// 1. 创建一个中断条件
public boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag){
System.out.println(Thread.currentThread().getName() + "--" + (i++));
try {
Thread.sleep(250);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
(6) 线程的其他方法
-
setDaemon(boolean on) 可以设置为守护线程或用户线程
-
如果运行的线程都是守护线程,则JVM会退出
-
isDaemon() 判断是否是守护线程
-
getId() 返回该线程的标识符
-
getName() 获取 该线程的名字
-
**setName(String name) 设置该线程的名字 **
-
isAlive() 判断该线程是否为活动状态
-
setPriority(int newPriority) 更改该线程的优先级
- 优先级: 可以提高该线程抢到时间片的概率
static int MAX_PRIORITY // 线程可以具有的最高优先级。 static int MIN_PRIORITY // 线程可以具有的最低优先级。 static int NORM_PRIORITY // 分配给线程的默认优先级
-
在多线程操作中, 多个线程同时可以处理同一个资源。
-
先看代码
public class SynchronizeDemo {
public static void main(String[] args) {
Ticket tk = new Ticket();
// 创建线程
Thread t1 = new Thread(tk);
t1.setName("抢票机器1");
Thread t2 = new Thread(tk);
t2.setName("抢票机器2");
// 开始抢票
t1.start();
t2.start();
}
}
// 售票
class Ticket implements Runnable{
private int currentTicket = 10; // 当前剩余票的数量
@Override
public void run() {
// 抢票
while (currentTicket > 0) {
System.out.println(Thread.currentThread().getName()+"成功抢到票" + currentTicket--);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2) 线程同步
-
线程同步就是为了解决数据共享问题, 所谓的同步就是 多个线程在同一时刻只能有一个线程执行指定代码,
其他线程必须等待该线程执行完毕后才可以执行。
// main() 方法不变
// 售票
class Ticket implements Runnable{
private int currentTicket = 10; // 当前剩余票的数量
// 随便定义一个对象
private final Object obj = new Object();
@Override
public void run() {
// 抢票
while (currentTicket > 0) {
// 如果多个线程都在运行当前任务 最简单的可以直接使用 this 当前任务对象, 来当作同步锁
synchronized (obj) {
System.out.println(Thread.currentThread().getName()+"成功抢到票" + currentTicket--);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
二、同步方法2(使用synchronized 修饰的方法)
- 使用这个方法使用锁对象是当前任务的实例,也就是this
// 售票
class Ticket implements Runnable{
private int currentTicket = 10; // 当前剩余票的数量
@Override
public void run() {
// 抢票
while (currentTicket > 0) {
// 使用synchronized 修饰的方法
syncFun();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// synchronized 修饰的方法
public synchronized void syncFun() {
if (currentTicket > 0) {
System.out.println(Thread.currentThread().getName()+"成功抢到票" + currentTicket--);
}
}
}
三、同步方法3(使用Lock互斥锁)
// 售票
class Ticket implements Runnable{
private int currentTicket = 10; // 当前剩余票的数量
private final Object obj = new Object();
// 创建一个互斥锁
private final Lock lock = new ReentrantLock();
@Override
public void run() {
// 抢票
while (currentTicket > 0) {
// 加锁
lock.lock();
if (currentTicket > 0) {
System.out.println(Thread.currentThread().getName()+"成功抢到票" + currentTicket--);
}
// 解锁
lock.unlock();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
四、生产者与消费者示例
- 这里我用两种队列实现的
class Food2 {
private String name;
private String desc;
public Food2(String name) {
this.name = name;
}
public Food2() {
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Food2{" +
"name='" + name + ''' +
", desc='" + desc + ''' +
'}';
}
}
public class ProductorAndConsumer2 {
// 创建一个队列
private static final BlockingQueue q = new linkedBlockingQueue<>();
// 创建一个生产者
static class Productor implements Runnable {
@Override
public void run() {
// 生产10道菜
for (int i = 0; i < 10; i++) {
Food2 food2 = new Food2("红烧肉" + i);
System.out.println("生产者:生产出一道" + food2.getName());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
// 将这道菜添加到队列(类似于将菜放到了取菜口, 让用户取餐)
q.put(food2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 创建一个消费者
static class Consumer implements Runnable {
@Override
public void run() {
// 不停的消费
while (true) { // 这里写了一个死的
try {
// 从队列中取数据(消费者到取餐口取餐,进行消费)
Food2 food = q.take(); // 这个take方法, 如果队列中没有数据,则会阻塞
Thread.sleep(500);
System.out.println("消费者:消费了一道" + food.getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
// 创建任务
Productor p = new Productor();
Consumer c = new Consumer();
// 创建线程
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
// 运行结果只 截取了一部分查看
2) 使用synchronized 和 linkedlist 队列
public class ProductorAndConsumer2 {
// 创建一个队列
private static final Queue q2 = new linkedList<>();
private static final int MAX_CAPACITY = 1; // 仓库最大容量
// 创建一个生产者
static class Productor implements Runnable {
public void run() {
synchronized (q2) {
// 生产10道菜
for (int i = 0; i < 10; i++) {
if (q2.size() == MAX_CAPACITY) { // 仓库已满
try {
// 让当前线程等待
q2.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Food2 food2 = new Food2("红烧肉" + i);
System.out.println("生产者:生产出一道" + food2.getName());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 将这道菜添加到队列(类似于将菜放到了取菜口, 让用户取餐)
q2.add(food2);
// 唤醒其他线程
q2.notify();
}
}
}
}
// 创建一个消费者
static class Consumer implements Runnable {
@Override
public void run() {
// 不停的消费
while (true) {
synchronized (q2) {
if (q2.size() == 0) {
try {
// 当前线程等待
q2.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 从队列中取数据(消费者到取餐口取餐,进行消费)
Food2 food = q2.poll();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者:消费了一道" + food.getName());
q2.notify(); // 唤醒其他线程
}
}
}
}
public static void main(String[] args) {
// 创建任务
Productor p = new Productor();
Consumer c = new Consumer();
// 创建线程
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
3)总结
-
使代码块保持简短。把不随线程变化的预处理和后处理移出synchronized 块。
-
不要阻塞。如InputStream.read()。
-
在持有锁的时候,不要对其它对象调用方法。
-
如果同步过多可能会造成死锁
-
sleep 与 wait 的区别?
- sleep: 让线程进入休眠状态, 让出CPU时间片, 不释放对象的监视器的所有权(对象锁不会被释放)
- wait: 让线程进入等待状态, 让出CPU时间片, 释放对象监视器的所有权, 等待其他线程通过notify来唤醒
-
线程池是预先创建线程的一种技术, 线程池在任务五还没有到来之前, 创建一定数量的线程, 放入空闲队列中,
然后对这些线程进行复用, 减少频繁的创建和销毁对象。
-
在JDK1.5 以后提供了线程的线程池
-
Executor 是线程池的顶级接口
-
具体使用的是Executor 下的子接口 ExecutorService;
-
Executor 接口: 只有一个方法, 就是执行已经提交的Runnable任务对象
-
ExecutorService 接口: 提供了很多方法,来操作先成功池
-
具体的实现类是Executors 类
-
它们都在java.util.concurrent 包下
-
下面展示两个比较常用的线程池做介绍, 其他线程池的使用都差不多
-
创建一个单线程线程池, 这个线程池只有一个线程工作, 也就是单线程串行执行所有任务(按顺序一个一个执行),
如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此 线程池保证所有任务的执行顺序按照任务的提交顺序执行。
-
代码
public class ExecutorDemo {
public static void main(String[] args) {
// 创建一个单线程的线程池
ExecutorService es = Executors.newSingleThreadExecutor();
// 让线程执行指定任务
es.execute(new MyRunnable7());
es.execute(new MyRunnable7());
// 终止线程池
es.shutdown();
}
}
class MyRunnable7 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("run--" + i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3) newFixedThreadPool 类 创建固定大小的线程池
-
创建固定大小的线程池。每次提交一个任务就创建一个线程,直 到线程达到线程池的最大大小。
如果某个线程因为执行异常而结束, 那么线程池会补充一个新线程。
-
代码
public static void main(String[] args) {
// 创建一个指定线程数量的线程池
ExecutorService es = Executors.newFixedThreadPool(2);
// 让线程执行指定任务
es.execute(new MyRunnable7());
es.execute(new MyRunnable7());
// 终止线程池
es.shutdown();
}
class MyRunnable7 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("run--" + i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}



