package com.it.demo01_thread;
/*
案例: 多线程简介.
概述:
指的是进程有多条执行路径, 统称叫: 多线程.
进程: 指的是可执行程序, 文件(例如: .exe)
大白话翻译: 车.
线程: 指的就是 进程的 执行路径(或者叫: 执行单元)
大白话翻译: 车道.
面试题: 多线程并行和多线程并发的区别是什么?
多线程并行:
指的是两个(或以上)的线程同时执行. 前提: 需要多核CPU.
多线程并发:
指的是两个(或以上)的线程请求执行, 但是同一瞬间CPU只能执行一个线程,
于是就安排它们交替执行, 因为时间间隔非常短, 我们看起来好像是同时执行的, 其实不是.
案例: 演示单线程程序, 即: 前边的代码没有执行完毕前, 后边的代码不会执行.
*/
public class Demo01 {
public static void main(String[] args) {
for (int i = 0; i < 200; i++) {
System.out.println("run,……. " + i);
}
for (int i = 0; i < 200; i++) {
System.out.println("main... " + i);
}
}
}
package com.it.demo01_thread;
/*
案例: 演示多线程的实现方式一: 继承Thread类.
在Java程序中, 线程的顶级类是: Thread, 即: 所有的线程类都是Thread类的子类.
多线程的方式如下:
方式一: 继承Thread类.
步骤:
1. 自定义一个线程类(MyThread), 让它继承Thread类.
2. 重写Thread#run()
3. 把要执行的代码放到 run()方法中.
4. 在main方法中创建线程类对象.
5. 开启线程.
方式二: 实现Runnable接口.
方式三: 实现Callable接口, 必须和线程池相结合使用.
细节:
1. 开启线程调用的是 Thread#start()方法, 如果调用run()方法, 只是普通的方法调用而已.
2. 同一线程不能重复开启, 否则会报: IllegalThreadStateException异常.
3. 多线程的执行具有 随机性 和 延迟性.
4. 一台电脑上可以有多个进程, 这些进程之间的数据是相互隔离的.
5. 一个进程可以有多条线程, 这些线程可以共享该进程的数据.
*/
public class Demo02 {
public static void main(String[] args) {
//4. 在main方法中创建线程类对象.
MyThread mt = new MyThread();
//5. 开启线程
//mt.run(); //只是普通的方法调用.
mt.start();
//mt.start(); //同一线程不能重复开启
//main线程的内容, 输出200次 main...
for (int i = 0; i < 200; i++) {
System.out.println("main... " + i);
}
}
}
package com.it.demo01_thread;
/*
案例: 演示多线程的实现方式二: 实现Runnable接口.
多线程的方式如下:
方式一: 继承Thread类.
方式二: 实现Runnable接口.
步骤:
1. 自定义一个资源类(MyRunnable), 让它实现Runnable接口.
2. 重写Runnable#run()
3. 把要执行的代码放到 run()方法中.
4. 在main方法中创建资源类对象, 然后将其作为参数传入Thread类的构造, 从而创建线程对象.
5. 开启线程.
方式三: 实现Callable接口.
细节:
1. 开启线程调用的是 Thread#start()方法, 如果调用run()方法, 只是普通的方法调用而已.
2. 同一线程不能重复开启, 否则会报: IllegalThreadStateException异常.
3. 多线程的执行具有 随机性 和 延迟性.
4. 一台电脑上可以有多个进程, 这些进程之间的数据是相互隔离的.
5. 一个进程可以有多条线程, 这些线程可以共享该进程的数据.
*/
public class Demo03 {
public static void main(String[] args) {
//4. 在main方法中创建资源类对象, 然后将其作为参数传入Thread类的构造, 从而创建线程对象.
//4.1 创建资源类对象
MyRunnable mr = new MyRunnable();
//4.2 将其作为参数传入Thread类的构造, 从而创建线程对象.
Thread th = new Thread(mr);
//5. 开启线程.
th.start();
for (int i = 0; i < 200; i++) {
System.out.println("main... " + i);
}
}
}
package com.it.demo01_thread;
/*
案例: 演示Thread类的构造方法和成员方法
涉及到的Thread类中的成员:
构造方法:
public Thread(): 分配一个新的线程对象。
public Thread(String name) 分配一个指定名字的新的线程对象。
public Thread(Runnable target) 分配一个带有指定目标新的线程对象。
public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字。
常用方法:
public String getName(): 获取当前线程名称。
public void start(): 导致此线程开始执行; Java虚拟机调用此线程的run方法。
public void run(): 此线程要执行的任务在此处定义代码。
public static void sleep(long millis): 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
public static Thread currentThread(): 返回对当前正在执行的线程对象的引用。
*/
public class Demo04 {
public static void main(String[] args) {
//需求: 创建两个线程对象, 并开启.
//方式一: 继承Thread类.
//method01();
//方式二: 实现Runnable接口.
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
t1.setName("乔峰");
Thread t2 = new Thread(mr, "虚竹");
t1.start();
t2.start();
}
public static void method01() {
MyThread mt1 = new MyThread();
mt1.setName("刘亦菲");
MyThread mt2 = new MyThread("赵丽颖");
mt1.start();
mt2.start();
}
}
package com.it.demo01_thread;
/*
案例: 通过匿名内部类的方式实现多线程.
*/
public class Demo05 {
public static void main(String[] args) {
//方式一: 继承Thread类.
new Thread() {
//重写run方法, 把要执行的写到方法中
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("匿名内部类, 继承Thread类版 " + i);
}
}
}.start();
//方式二: 实现Runnable接口.
//格式: new Thread(Runnable接口的子类对象).start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("匿名内部类, 实现Runnable接口.... " + i);
}
}
}).start();
//扩展: 因为Runnable接口中只有一个抽象方法, 所以可以尝试通过Lambda表达式实现.
new Thread(() -> {
for (int i = 0; i < 100; i++) {
System.out.println("Lambda表达式, 实现Runnable接口 " + i);
}
}).start();
}
}
package com.it.demo01_thread;
//1. 自定义一个线程类(MyThread), 让它继承Thread类.
public class MyThread extends Thread{
//细节: 如果要给线程起名字, 记得写构造方法
public MyThread() {
}
public MyThread(String name) {
super(name);
}
//2. 重写Thread#run()
@Override
public void run() {
//3. 把要执行的代码放到 run()方法中.
for (int i = 0; i < 200; i++) {
System.out.println(this.getName() + " run,....... " + i);
};
}
}
package com.it.demo01_thread;
//资源类
//1. 自定义一个资源类(MyRunnable), 让它实现Runnable接口.
public class MyRunnable implements Runnable{
//2. 重写Runnable#run()
@Override
public void run() {
//3. 把要执行的代码放到 run()方法中.
for (int i = 0; i < 200; i++) {
System.out.println(Thread.currentThread().getName() + " run,....... " + i);
}
}
}
package com.it.demo02_tickets;
//自定义的线程类, 用来模拟卖票
public class MyThread extends Thread{
//1. 定义变量, 记录票数.
private static int tickets = 66; //细节1: 4个窗口共享100张票
//2. 定义构造方法.
public MyThread() {
}
public MyThread(String name) {
super(name);
}
//3. 重写run()方法, 里边写是具体的卖票的逻辑.
@Override
public void run() {
//具体的卖票的逻辑.
while(true) {
//做越界处理, 没票就不卖了.
if (tickets <= 0) {
break;
}
//加入休眠线程, 让程序出现非法值的概率大一些.
try {
Thread.sleep(50); //单位是毫秒 线程1, 线程2, 线程3, 线程4
} catch (InterruptedException e) {
e.printStackTrace();
}
//具体的卖票
System.out.println(getName() + " 正在售出第 "+ tickets-- +" 张票");
}
/\*
多线程模拟卖票, 会出现非法值的问题:
出现负数:
核心点: 1. if判断 2. sleep(), 在哪睡, 就在哪醒.
具体流程:
1. 当tickets的值为1的时候, 如果此时4个线程分别都抢到了资源, 那么它们都会处于休眠的状态, 此时, 不管哪个线程醒来, 它们执行流程如下:
2. 假设线程1先醒来, 会打印: 窗口1正在买第 1 张票, 之后tickets--, 即: tickets = 0
3. 假设线程2 醒来, 会打印: 窗口2正在买第 0 张票, 之后tickets--, 即: tickets = -1
4. 假设线程3 醒来, 会打印: 窗口3正在买第 -1 张票, 之后tickets--, 即: tickets = -2
5. 假设线程4 醒来, 会打印: 窗口4正在买第 -2 张票, 之后tickets--, 即: tickets = -3
出现重复值:
核心点: tickets-- , 这行代码相当于: tickets = tickets - 1; 这行代码做了 3 件事儿:
1. 读值. tickets = 66
2. 改值. tickets - 1 = 66 - 1 = 65
3. 赋值. tickets = 65
此时, 当该线程(窗口)打印完售票动作, 还没来得及修改tickets的值之前, 被别的线程抢走了CPU资源,
就会出现重复值的问题.
\*/
}
}
package com.it.demo02_tickets;
/*
案例: 模拟卖票, 4个窗口卖100张票.
*/
public class Demo01 {
public static void main(String[] args) {
//1. 创建4个线程.
MyThread mt1 = new MyThread("窗口1");
MyThread mt2 = new MyThread("窗口2");
MyThread mt3 = new MyThread("窗口3");
MyThread mt4 = new MyThread("窗口4");
//2. 开启线程, 卖票.
mt1.start();
mt2.start();
mt3.start();
mt4.start();
}
}
package com.it.demo03_tickets;
//自定义的线程类, 用来模拟卖票
public class MyThread extends Thread {
//1. 定义变量, 记录票数.
private static int tickets = 100; //细节1: 4个窗口共享100张票
//2. 定义构造方法.
public MyThread() {
}
public MyThread(String name) {
super(name);
}
//3. 重写run()方法, 里边写是具体的卖票的逻辑.
@Override
public void run() {
//具体的卖票的逻辑.
while (true) {
//一次卖票逻辑
synchronized (MyThread.class) { //锁对象一般会写: 该类的字节码文件
//synchronized (this) { //锁对象一般会写: 该类的字节码文件, 锁不住.
//做越界处理, 没票就不卖了.
if (tickets <= 0) {
break;
}
//加入休眠线程, 让程序出现非法值的概率大一些.
try {
Thread.sleep(50); //单位是毫秒 线程1, 线程2, 线程3, 线程4
} catch (InterruptedException e) {
e.printStackTrace();
}
//具体的卖票
System.out.println(getName() + " 正在售出第 " + tickets-- + " 张票");
}
}
}
}
package com.it.demo03_tickets;
/*
案例: 模拟卖票, 4个窗口卖100张票, 解决非法值问题.
解决方案: 采用同步代码块解决
格式:
synchronized(锁对象) {
//这里写的是要加锁的代码.
}
注意事项:
1. 同步代码块的锁对象可以是 任意类型的对象.
2. 必须使用同一把锁, 否则可能出现锁不住的情况.
结论:
线程安全(同步), 效率低, 线程不安全(不同步), 效率高.
*/
public class Demo01 {
public static void main(String[] args) {
//1. 创建4个线程.
MyThread mt1 = new MyThread("窗口1");
MyThread mt2 = new MyThread("窗口2");
MyThread mt3 = new MyThread("窗口3");
MyThread mt4 = new MyThread("窗口4");
//2. 开启线程, 卖票.
mt1.start();
mt2.start();
mt3.start();
mt4.start();
}
}
package com.it.demo04_tickets_runnable;
import com.it.demo03_tickets.MyThread;
//自定义的资源类, 用来模拟卖票.
public class MyRunnable implements Runnable {
//1. 定义变量, 记录票数.
private int tickets = 100;
//2. 我们不用定义构造方法, 用系统默认的就行.
//3. 重写run()方法, 里边写是具体的卖票的逻辑.
@Override
public void run() {
//具体的卖票的逻辑.
while (true) {
//一次卖票逻辑
//synchronized (MyRunnable.class) { //可以
synchronized (this) { //可以
if (tickets <= 0) {
break;
}
//加入休眠线程, 让程序出现非法值的概率大一些.
try {
Thread.sleep(50); //单位是毫秒 线程1, 线程2, 线程3, 线程4
} catch (InterruptedException e) {
e.printStackTrace();
}
//具体的卖票
System.out.println(Thread.currentThread().getName() + " 正在售出第 " + tickets-- + " 张票");
}
}
}
}
package com.it.demo04_tickets_runnable;
/*
案例: 多线程模拟卖票, 实现Runnable接口版.
实现Runnable接口 和 继承Thread版的 卖票代码相比, 以下4个地方可以优化:
1. 定义票数的变量, 可以用不写 static, 因为资源对象就一个.
2. 构造方法无需定义, 用系统默认提供的 无参构造即可.
3. 锁对象可以是this, 因为资源对象就一个.
4. 因为MyRunnable类和Thread类之间无关系, 所以不能直接调用 Thread#getName();
线程同步简介:
概述:
多线程 并发 操作同一数据, 就有可能引发安全问题, 需要用到同步解决.
特点:
线程同步(安全), 效率低, 线程不同步(不安全), 效率高.
分类:
同步代码块:
格式:
synchronized(锁对象) {
//这里写的是要加锁的代码.
}
注意事项:
1. 同步代码块的锁对象可以是 任意类型的对象.
2. 必须使用同一把锁, 否则可能出现锁不住的情况.
同步方法:
说明: 就是在方法的声明上, 加上 synchronized 关键字.
静态同步方法: 锁对象是 该类的字节码文件对象, 即: 类名.class
非静态同步方法: 锁对象是 this
*/
public class Demo01 {
public static void main(String[] args) {
//1. 创建资源类对象.
MyRunnable mr = new MyRunnable();
//2. 创建线程对象.
Thread th1 = new Thread(mr, "窗口1");
Thread th2 = new Thread(mr, "窗口2");
Thread th3 = new Thread(mr, "窗口3");
Thread th4 = new Thread(mr, "窗口4");
//3. 开启线程.
th1.start();
th2.start();
th3.start();
th4.start();
}
public synchronized void show1() { //非静态同步方法, 锁对象: this
System.out.println(1);
System.out.println(2);
System.out.println(3);
System.out.println(4);
System.out.println(5);
}
public synchronized static void show2() { //静态同步方法, 锁对象: 该类的字节码文件
}
}
package com.it.demo05_deadlock;
import com.sun.scenario.effect.LockableResource;
import java.util.concurrent.locks.Lock;
/*
案例: 演示死锁的代码(只在面试用)
大白话翻译:
死锁指的是同步代码块的嵌套, 实际开发中, 我们在写代码的时候, 也要尽量规避这个问题.
原理分析:
1. 死锁需要两个线程, 两把锁.
2. 一个线程先抢锁A, 后抢锁B, 另一个线程先抢锁B, 后抢锁A.
3. 这个时候就有可能发生死锁的现象, 为了让效果更明显, 我们用while(true)改进.
*/
public class DeadLock {
//1. 定义两把锁.
private static final String LOCKA = "锁A";
private static final String LOCKB = "锁B";
public static void main(String\[\] args) {
//2. 创建两个线程对象.
//第一个线程对象
new Thread("关羽") {
@Override
public void run() {
//为了让效果更明显, 我们用while(true)改进.
while (true) {
//一个线程先抢锁A, 后抢锁B,
synchronized (LOCKA) {
System.out.println(getName() + " 获取到 " + LOCKA + ", 等待 " + LOCKB);
synchronized (LOCKB) {
System.out.println(getName() + " 获取到 " + LOCKB + ", 成功进到小黑屋");
}
}
}
}
}.start();
//第二个线程对象
new Thread("张飞") {
@Override
public void run() {
//为了让效果更明显, 我们用while(true)改进.
while (true) {
// 另一个线程先抢锁B, 后抢锁A.
synchronized (LOCKB) {
System.out.println(getName() + " 获取到 " + LOCKB + ", 等待 " + LOCKA);
synchronized (LOCKA) {
System.out.println(getName() + " 获取到 " + LOCKA + ", 成功进到小黑屋");
}
}
}
}
}.start();
}
}
package com.it.demo05_deadlock;
import java.util.Scanner;
/*
案例: 演示IO流阻塞问题.
*/
public class Demo01 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请录入您的账号: ");
String username = sc.nextLine();
System.out.println(username);
}
}
package com.it.demo06_priority;
//自定义的线程类
public class MyThread extends Thread{
//构造方法
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName() + " ... " + i);
}
}
}
package com.it.demo06_priority;
/*
案例: 演示多线程的优先级问题.
涉及到的Thread类中的成员:
成员常量:
MAX\_PRIORITY 10
MIN\_PRIORITY 1
NORM\_PRIORITY 5
成员方法:
public int getPriority(); 获取当前线程的优先级.
public void setPriority(int num); 设置当前线程的优先级.
细节:
1. 默认优先级为: 5, 范围是1-10.
2. 线程的优先级越高, 只是说明该线程 抢到CPU资源的概率会更大, 并不代表它一定第一个执行.
*/
public class Demo01 {
public static void main(String[] args) {
//1. 创建3个线程对象.
MyThread mt1 = new MyThread("飞机 .");
MyThread mt2 = new MyThread("游艇 ..");
MyThread mt3 = new MyThread("高铁 … ");
//2. 打印上述三个线程的默认优先级.
/\* System.out.println(mt1.getPriority()); //默认是: 5
System.out.println(mt2.getPriority());
System.out.println(mt3.getPriority());\*/
//3. 查看线程的优先级有哪些
/\*System.out.println(Thread.MAX\_PRIORITY); //10
System.out.println(Thread.MIN\_PRIORITY); //1
System.out.println(Thread.NORM\_PRIORITY); //5\*/
//4. 设置mt2线程的优先级为10
//mt2.setPriority(Thread.MAX\_PRIORITY);
mt2.setPriority(10);
//2. 开启线程
mt1.start();
mt2.start();
mt3.start();
}
}
package com.it.demo06_priority;
//自定义的线程类
public class MyThread extends Thread{
//构造方法
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName() + " ... " + i);
}
}
}
package com.it.demo06_priority;
/*
案例: 演示多线程的优先级问题.
涉及到的Thread类中的成员:
成员常量:
MAX\_PRIORITY 10
MIN\_PRIORITY 1
NORM\_PRIORITY 5
成员方法:
public int getPriority(); 获取当前线程的优先级.
public void setPriority(int num); 设置当前线程的优先级.
细节:
1. 默认优先级为: 5, 范围是1-10.
2. 线程的优先级越高, 只是说明该线程 抢到CPU资源的概率会更大, 并不代表它一定第一个执行.
*/
public class Demo01 {
public static void main(String[] args) {
//1. 创建3个线程对象.
MyThread mt1 = new MyThread("飞机 .");
MyThread mt2 = new MyThread("游艇 ..");
MyThread mt3 = new MyThread("高铁 … ");
//2. 打印上述三个线程的默认优先级.
/\* System.out.println(mt1.getPriority()); //默认是: 5
System.out.println(mt2.getPriority());
System.out.println(mt3.getPriority());\*/
//3. 查看线程的优先级有哪些
/\*System.out.println(Thread.MAX\_PRIORITY); //10
System.out.println(Thread.MIN\_PRIORITY); //1
System.out.println(Thread.NORM\_PRIORITY); //5\*/
//4. 设置mt2线程的优先级为10
//mt2.setPriority(Thread.MAX\_PRIORITY);
mt2.setPriority(10);
//2. 开启线程
mt1.start();
mt2.start();
mt3.start();
}
}
package com.it.demo07_join;
//自定义的线程类
public class MyThread extends Thread{
//构造方法
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName() + " ... " + i);
}
}
}
package com.it.demo07_join;
/*
案例: 演示 加入线程(相当于: 插队)
涉及到的Thread类中的成员:
public void join(); //等待这个线程死亡, 其实相当于插队, 只有这个线程执行完毕, 其他线程才会执行.
*/
public class Demo01 {
public static void main(String[] args) {
//1. 创建3个线程对象.
MyThread mt1 = new MyThread("康熙");
MyThread mt2 = new MyThread("四阿哥");
MyThread mt3 = new MyThread("八阿哥");
//2. 开启线程
mt1.start();
//设置mt1为: 加入线程, 即: 让它优先执行.
try {
mt1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
mt2.start();
mt3.start();
}
}
package com.it.demo08_daemon;
//自定义的线程类
public class MyThread extends Thread{
//构造方法
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + " ... " + i);
}
}
}
package com.it.demo08_daemon;
/*
案例: 演示 守护线程.
涉及到的Thread类中的成员:
public void setDaemon(boolean flag); //传入true: 表示设置当前线程为守护线程.
细节:
1. 线程默认都是非守护线程.
2. 当非守护线程执行结束后, 所有与其关联的守护线程都会立马结束.
//注意: 稍微会有一点点的延迟性.
*/
public class Demo01 {
public static void main(String[] args) {
//1. 创建3个线程对象.
MyThread mt1 = new MyThread("关羽");
MyThread mt2 = new MyThread("张飞");
//2. 设置上述的两个线程为: 守护线程.
mt1.setDaemon(true);
mt2.setDaemon(true);
//3. 给当前线程(Main主线程)起个别名
Thread.currentThread().setName("刘备");
//4. 开启线程
mt1.start();
mt2.start();
//5. 给主线程分配任务.
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "..." + i);
}
}
}
package com.it.demo09_lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//自定义的线程类, 用来模拟卖票
public class MyThread extends Thread {
//1. 定义变量, 记录票数.
private static int tickets = 100; //细节1: 4个窗口共享100张票
//定义一个Lock锁, 多态
//Lock锁升级版知识点: 多线程的等待唤醒机制问题, 即: 让线程有规律的执行.
static Lock lock = new ReentrantLock(); //必须保证同一把锁, 否则可能锁不住.
//2. 定义构造方法.
public MyThread() {
}
public MyThread(String name) {
super(name);
}
//3. 重写run()方法, 里边写是具体的卖票的逻辑.
@Override
public void run() {
//具体的卖票的逻辑.
while (true) {
//一次卖票逻辑
//synchronized (MyThread.class) { //锁对象一般会写: 该类的字节码文件
lock.lock(); //加锁
//做越界处理, 没票就不卖了.
if (tickets <= 0) {
break;
}
//加入休眠线程, 让程序出现非法值的概率大一些.
try {
Thread.sleep(50); //单位是毫秒 线程1, 线程2, 线程3, 线程4
} catch (InterruptedException e) {
e.printStackTrace();
}
//具体的卖票
System.out.println(getName() + " 正在售出第 " + tickets-- + " 张票");
lock.unlock(); //解锁
}
}
}
package com.it.demo09_lock;
/*
案例: 演示Lock锁
Lock锁简介:
概述:
它是JDK1.5的特性, 也是一个接口, 表示互斥锁, 可以让我们实现精准的加锁和解锁的事情.
成员方法:
unlock(); 解锁
lock(); 加锁
*/
public class Demo01 {
public static void main(String[] args) {
//1. 创建4个线程.
MyThread mt1 = new MyThread("窗口1");
MyThread mt2 = new MyThread("窗口2");
MyThread mt3 = new MyThread("窗口3");
MyThread mt4 = new MyThread("窗口4");
//2. 开启线程, 卖票.
mt1.start();
mt2.start();
mt3.start();
mt4.start();
}
}
package com.it.demo10_executor;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/*
案例: 线程池入门.
线程池简介:
概述:
实际开发中, 当我们需要使用到大量生命周期短的线程对象时, 频繁的创建和销毁线程对象是非常消耗 系统资源的, 针对于这种情况,
我们可以搞一个池子出来, 池子里边放一些线程对象, 用的时候去池子拿, 用完之后再返回去, 这个池子就叫: 线程池. 这样做的好处是,
节约资源, 提高效率.
成员方法:
Executors 线程池工具类中的成员方法:
public static ExecutorService newFixedThreadPool(int nThreads); 创建线程池对象,nThreads: 表示该池子中有几个线程对象.
ExecutorService 它才是具体的 线程池类
public Future<?> submit(Runnable task) 提交线程执行任务,这是实现多线程的第二种方式
public Future<?> submit(Callable call) 提交线程执行任务,这是实现多线程的第三种方式
public void shutdown() 关闭线程池, 实际开发中, 正常情况下线程池是不关的.
Future: 封装的是线程任务执行结束后, 具体的返回值.
public V get(); 获取线程任务执行结束后, 具体的返回值.
*/
public class Demo01 {
public static void main(String[] args) throws Exception {
//1. 创建线程池对象, 指定线程对象的个数.
ExecutorService service = Executors.newFixedThreadPool(5);
//2. 给线程池提交任务.
Future> future = service.submit(new Runnable() {
@Override
public void run() {
System.out.println("1. 我们只要把任务提交给线程池即可, 需要使用线程对象的时候, 它(线程池)会自动帮我们分配");
System.out.println("2. 具体的任务可以写在: Runnable#run()方法中");
System.out.println("3. 当线程对象使用完毕后, 线程池会自动回收它到线程池中.");
}
});
//3. 获取线程任务执行结束后的结果.
System.out.println(future.get()); //null
//4. 关闭线程池. 实际开发中, 正常情况下线程池是不关的.
service.shutdown();
}
}
package com.it.demo10_executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/*
案例: 线程池入门.
线程池简介:
概述:
实际开发中, 当我们需要使用到大量生命周期短的线程对象时, 频繁的创建和销毁线程对象是非常消耗 系统资源的, 针对于这种情况,
我们可以搞一个池子出来, 池子里边放一些线程对象, 用的时候去池子拿, 用完之后再返回去, 这个池子就叫: 线程池. 这样做的好处是,
节约资源, 提高效率.
成员方法:
Executors 线程池工具类中的成员方法:
public static ExecutorService newFixedThreadPool(int nThreads); 创建线程池对象,nThreads: 表示该池子中有几个线程对象.
ExecutorService 它才是具体的 线程池类
public Future<?> submit(Runnable task) 提交线程执行任务,这是实现多线程的第二种方式
public Future<?> submit(Callable call) 提交线程执行任务,这是实现多线程的第三种方式
public void shutdown() 关闭线程池, 实际开发中, 正常情况下线程池是不关的.
Future: 封装的是线程任务执行结束后, 具体的返回值.
public V get(); 获取线程任务执行结束后, 具体的返回值.
Runnable接口和Callable接口的区别:
1. 作用范围不同.
Runnable可以不结合线程池, 直接单独使用.
Callable必须结合线程池相结合使用.
2. 接口中的方法不同.
Runnable#run(), 有异常只能try, 且该方法没有返回值.
Callable#call(), 可以throws抛出异常, 且该方法可以有返回值.
*/
public class Demo02 {
public static void main(String[] args) throws Exception {
//1. 创建线程池对象, 指定线程对象的个数.
ExecutorService service = Executors.newFixedThreadPool(5);
//2. 给线程池提交任务.
//Future> future = service.submit(可以是Runnable接口的子类对象);
//Future> future = service.submit(也可以是Callable接口的子类对象);
Future> future = service.submit(new MyCallable());
//3. 获取线程任务执行结束后的结果.
System.out.println(future.get()); //风和日丽, 晴空万里
//4. 关闭线程池. 实际开发中, 正常情况下线程池是不关的.
service.shutdown();
}
}
package com.it.demo10_executor;
import java.util.concurrent.Callable;
//自定义的资源类, 实现Callable接口
public class MyCallable implements Callable
@Override
public String call() throws Exception {
System.out.println("我是Callable接口封装的 具体任务");
return "风和日丽, 晴空万里";
}
}
package com.it.demo11_signature;
//定义共享数据区, 奶箱
public class Box {
//定义变量, 记录当前是第几瓶奶
private int milk;
//定义变量, 记录奶箱的状态
boolean flag = false; //true: 有奶, false: 无奶
//定义方法, 放奶
public synchronized void put(int milk) {
//判断奶箱是否有牛奶
if (flag) {
//有牛奶, 就等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//具体的放牛奶的逻辑.
this.milk = milk;
System.out.println("送奶工正在放入第 "+ milk +" 瓶奶");
//修改奶箱状态.
flag = true;
//唤醒消费者, 取奶.
this.notify();
}
//定义方法, 取奶.
public synchronized void get() {
//判断奶箱是否有奶
if (!flag) {
//无奶就等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//具体取奶的动作
System.out.println("消费者正在获取第 " + milk + " 瓶奶");
//修改奶箱状态.
flag = false;
//唤醒生产者, 放奶.
this.notify();
}
}
package com.it.demo11_signature;
import java.util.Properties;
/*
案例: 演示消费者设计模式.
需求:
奶箱类(Box):定义一个成员变量,表示第x瓶奶,提供存储牛奶和获取牛奶的操作
生产者类(Producer):实现Runnable接口,重写run()方法,调用存储牛奶的操作
消费者类(Customer):实现Runnable接口,重写run()方法,调用获取牛奶的操作
测试类(BoxDemo):里面有main方法,main方法中的代码步骤如下
①创建奶箱对象,这是共享数据区域
②创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用存储牛奶的操作
③创建消费者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用获取牛奶的操作
④创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
⑤启动线程
设计模式简介:
概述:
设计模式不属于任何语言, 他也不是语法, 而是前辈们总结的用来解决问题的思路和方案, 它是一套用来解决一系列问题的技术方案.
分类: 一共23种
创建型: 需要创建对象, 5种
单例设计模式,
工厂方法设计模式
结构型: 用来描述类与类之间的关系的 7种
装饰设计模式,
BufferedReader br = new BufferedReader(new FileReader("1.txt));
适配器设计模式
行为型: 指的是事物能够做什么 11种
模板方法设计模式
消费者设计模式
推荐一本:
大话设计模式(Java版)
*/
public class BoxTest {
public static void main(String[] args) {
//①创建奶箱对象,这是共享数据区域
Box b = new Box();
//②创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用存储牛奶的操作
Producer p = new Producer(b);
//③创建消费者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用获取牛奶的操作
Customer c = new Customer(b);
//④创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
Thread th1 = new Thread(p); //负责放牛奶的 生产者
Thread th2 = new Thread(c); //负责获取牛奶的 消费者
//⑤启动线程
th1.start();
th2.start();
}
}
package com.it.demo11_signature;
//定义消费者类, 实现Runnable接口, 充当资源类
public class Customer implements Runnable{
//获取奶箱的引用
private Box box;
//创建消费者对象的时候, 必须指定 奶箱.
public Customer(Box box) {
this.box = box;
}
@Override
public void run() {
//取牛奶的动作
while (true)
box.get();
}
}
package com.it.demo11_signature;
//定义生产者类, 实现Runnable接口, 充当资源类
public class Producer implements Runnable{
//获取奶箱的引用
private Box box;
//创建生产者对象的时候, 必须指定 奶箱.
public Producer(Box box) {
this.box = box;
}
@Override
public void run() {
//具体放牛奶的动作
for (int i = 1; i <= 10; i++) {
box.put(i);
}
}
}
package cn.it.demo;
/*
* 定义子类,继承Thread
* 重写方法run
*/
public class SubThread extends Thread{
public void run(){
for(int i = 0; i < 50;i++){
System.out.println("run…"+i);
}
}
}
package cn.it.demo;
/*
* 程序中的主线程
*/
public class Demo {
public static void main(String[] args) {
System.out.println(0/0);
function();
System.out.println(Math.abs(-9));
}
public static void function(){
for(int i = 0 ; i < 10000;i++){
System.out.println(i);
}
}
}
package cn.it.demo;
/*
* 创建和启动一个线程
* 创建Thread子类对象
* 子类对象调用方法start()
* 让线程程序执行,JVM调用线程中的run
*/
public class ThreadDemo {
public static void main(String[] args) {
SubThread st = new SubThread();
SubThread st1 = new SubThread();
st.start();
st1.start();
for(int i = 0; i < 50;i++){
System.out.println("main…"+i);
}
}
}
package cn.it.demo1;
/*
* 获取线程名字,父类Thread方法
* String getName()
*/
public class NameThread extends Thread{
public NameThread(){
super("小强");
}
public void run(){
System.out.println(getName());
}
}
package cn.it.demo1;
/*
* 每个线程,都有自己的名字
* 运行方法main线程,名字就是"main"
* 其他新键的线程也有名字,默认 "Thread-0","Thread-1"
*
* JVM开启主线程,运行方法main,主线程也是线程,是线程必然就是
* Thread类对象
* Thread类中,静态方法
* static Thread currentThread()返回正在执行的线程对象
*/
public class ThreadDemo {
public static void main(String[] args) {
NameThread nt = new NameThread();
nt.setName("旺财");
nt.start();
/\*Thread t =Thread.currentThread();
System.out.println(t.getName());\*/
System.out.println(Thread.currentThread().getName());
}
}
package cn.it.demo2;
public class SleepThread extends Thread{
public void run(){
for(int i = 0 ; i < 5 ;i++){
try{
Thread.sleep(500);
}catch(Exception ex){
}
System.out.println(i);
}
}
}
package cn.it.demo2;
public class ThreadDemo {
public static void main(String[] args) throws Exception{
/*for(int i = 0 ; i < 5 ;i++){
Thread.sleep(50);
System.out.println(i);
}*/
new SleepThread().start();
}
}
package cn.it.demo3;
/*
* 实现线程成功的另一个方式,接口实现
* 实现接口Runnable,重写run方法
*/
public class SubRunnable implements Runnable{
public void run(){
for(int i = 0 ; i < 50; i++){
System.out.println("run…"+i);
}
}
}
package cn.it.demo3;
/*
* 实现接口方式的线程
* 创建Thread类对象,构造方法中,传递Runnable接口实现类
* 调用Thread类方法start()
*/
public class ThreadDemo {
public static void main(String[] args) {
SubRunnable sr = new SubRunnable();
Thread t = new Thread(sr);
t.start();
for(int i = 0 ; i < 50; i++){
System.out.println("main…"+i);
}
}
}
package cn.it.demo4;
/*
* 使用匿名内部类,实现多线程程序
* 前提: 继承或者接口实现
* new 父类或者接口(){
* 重写抽象方法
* }
*/
public class ThreadDemo {
public static void main(String[] args) {
//继承方式 XXX extends Thread{ public void run(){}}
new Thread(){
public void run(){
System.out.println("!!!");
}
}.start();
//实现接口方式 XXX implements Runnable{ public void run(){}}
Runnable r = new Runnable(){
public void run(){
System.out.println("###");
}
};
new Thread(r).start();
new Thread(new Runnable(){
public void run(){
System.out.println("@@@");
}
}).start();
}
}
package cn.it.demo5;
/*
* Callable 接口的实现类,作为线程提交任务出现
* 使用方法返回值
*/
import java.util.concurrent.Callable;
public class ThreadPoolCallable implements Callable
public String call(){
return "abc";
}
}
package cn.it.demo5;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/*
* JDK1.5新特性,实现线程池程序
* 使用工厂类 Executors中的静态方法创建线程对象,指定线程的个数
* static ExecutorService newFixedThreadPool(int 个数) 返回线程池对象
* 返回的是ExecutorService接口的实现类 (线程池对象)
*
* 接口实现类对象,调用方法submit (Ruunable r) 提交线程执行任务
*
*/
public class ThreadPoolDemo {
public static void main(String[] args) {
//调用工厂类的静态方法,创建线程池对象
//返回线程池对象,是返回的接口
ExecutorService es = Executors.newFixedThreadPool(2);
//调用接口实现类对象es中的方法submit提交线程任务
//将Runnable接口实现类对象,传递
es.submit(new ThreadPoolRunnable());
es.submit(new ThreadPoolRunnable());
es.submit(new ThreadPoolRunnable());
}
}
package cn.it.demo5;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/*
* 实现线程程序的第三个方式,实现Callable接口方式
* 实现步骤
* 工厂类 Executors静态方法newFixedThreadPool方法,创建线程池对象
* 线程池对象ExecutorService接口实现类,调用方法submit提交线程任务
* submit(Callable c)
*/
public class ThreadPoolDemo1 {
public static void main(String[] args)throws Exception {
ExecutorService es = Executors.newFixedThreadPool(2);
//提交线程任务的方法submit方法返回 Future接口的实现类
Future
String s = f.get();
System.out.println(s);
}
}
package cn.it.demo5;
public class ThreadPoolRunnable implements Runnable {
public void run(){
System.out.println(Thread.currentThread().getName()+" 线程提交任务");
}
}
package cn.it.demo6;
import java.util.concurrent.Callable;
public class GetSumCallable implements Callable
private int a;
public GetSumCallable(int a){
this.a=a;
}
public Integer call(){
int sum = 0 ;
for(int i = 1 ; i <=a ; i++){
sum = sum + i ;
}
return sum;
}
}
package cn.it.demo6;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/*
* 使用多线程技术,求和
* 两个线程,1个线程计算1+100,另一个线程计算1+200的和
* 多线程的异步计算
*/
public class ThreadPoolDemo {
public static void main(String[] args)throws Exception {
ExecutorService es = Executors.newFixedThreadPool(2);
Future
Future
System.out.println(f1.get());
System.out.println(f2.get());
es.shutdown();
}
}
package cn.it.demo;
/*
* 多线程并发访问同一个数据资源
* 3个线程,对一个票资源,出售
*/
public class ThreadDemo {
public static void main(String[] args) {
//创建Runnable接口实现类对象
Tickets t = new Tickets();
//创建3个Thread类对象,传递Runnable接口实现类
Thread t0 = new Thread(t);
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t0.start();t1.start();t2.start();
}
}
package cn.it.demo;
/*
* 通过线程休眠,出现安全问题
* 解决安全问题,Java程序,提供技术,同步技术
* 公式:
* synchronized(任意对象){
* 线程要操作的共享数据
* }
* 同步代码块
*/
public class Tickets implements Runnable{
//定义出售的票源
private int ticket = 100;
private Object obj = new Object();
public void run(){
while(true){
//线程共享数据,保证安全,加入同步代码块
synchronized(obj){
//对票数判断,大于0,可以出售,变量--操作
if( ticket > 0){
try{
Thread.sleep(10);
}catch(Exception ex){}
System.out.println(Thread.currentThread().getName()+" 出售第 "+ticket--);
}
}
}
}
}
package cn.it.demo1;
/*
* 多线程并发访问同一个数据资源
* 3个线程,对一个票资源,出售
*/
public class ThreadDemo {
public static void main(String[] args) {
//创建Runnable接口实现类对象
Tickets t = new Tickets();
//创建3个Thread类对象,传递Runnable接口实现类
Thread t0 = new Thread(t);
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t0.start();t1.start();t2.start();
}
}
package cn.it.demo1;
/*
* 采用同步方法形式,解决线程的安全问题
* 好处: 代码简洁
* 将线程共享数据,和同步,抽取到一个方法中
* 在方法的声明上,加入同步关键字
*
* 问题:
* 同步方法有锁吗,肯定有,同步方法中的对象锁,是本类对象引用 this
* 如果方法是静态的呢,同步有锁吗,绝对不是this
* 锁是本类自己.class 属性
* 静态方法,同步锁,是本类类名.class属性
*/
public class Tickets implements Runnable{
//定义出售的票源
private int ticket = 100;
public void run(){
while(true){
payTicket();
}
}
public synchronized void payTicket(){
if( ticket > 0){
try{
Thread.sleep(10);
}catch(Exception ex){}
System.out.println(Thread.currentThread().getName()+" 出售第 "+ticket--);
}
}
}
package cn.it.demo2;
/*
* 多线程并发访问同一个数据资源
* 3个线程,对一个票资源,出售
*/
public class ThreadDemo {
public static void main(String[] args) {
//创建Runnable接口实现类对象
Tickets t = new Tickets();
//创建3个Thread类对象,传递Runnable接口实现类
Thread t0 = new Thread(t);
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t0.start();t1.start();t2.start();
}
}
package cn.it.demo2;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
* 使用JDK1.5 的接口Lock,替换同步代码块,实现线程的安全性
* Lock接口方法:
* lock() 获取锁
* unlock()释放锁
* 实现类ReentrantLock
*/
public class Tickets implements Runnable{
//定义出售的票源
private int ticket = 100;
//在类的成员位置,创建Lock接口的实现类对象
private Lock lock = new ReentrantLock();
public void run(){
while(true){
//调用Lock接口方法lock获取锁
lock.lock();
//对票数判断,大于0,可以出售,变量--操作
if( ticket > 0){
try{
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()+" 出售第 "+ticket--);
}catch(Exception ex){
}finally{
//释放锁,调用Lock接口方法unlock
lock.unlock();
}
}
}
}
}
package cn.itdemo3;
public class DeadLock implements Runnable{
private int i = 0;
public void run(){
while(true){
if(i%2==0){
//先进入A同步,再进入B同步
synchronized(LockA.locka){
System.out.println("if…locka");
synchronized(LockB.lockb){
System.out.println("if…lockb");
}
}
}else{
//先进入B同步,再进入A同步
synchronized(LockB.lockb){
System.out.println("else…lockb");
synchronized(LockA.locka){
System.out.println("else…locka");
}
}
}
i++;
}
}
}
package cn.it.demo3;
public class DeadLockDemo {
public static void main(String[] args) {
DeadLock dead = new DeadLock();
Thread t0 = new Thread(dead);
Thread t1 = new Thread(dead);
t0.start();
t1.start();
}
}
package cn.it.demo3;
public class LockA {
private LockA(){}
public static final LockA locka = new LockA();
}
package cn.it.demo3;
public class LockB {
private LockB(){}
public static final LockB lockb = new LockB();
}
package cn.it.demo4;
/*
* 输入的线程,对资源对象Resource中成员变量赋值
* 一次赋值 张三,男
* 下一次赋值 lisi,nv
*/
public class Input implements Runnable {
private Resource r ;
public Input(Resource r){
this.r = r;
}
public void run() {
int i = 0 ;
while(true){
synchronized(r){
//标记是true,等待
if(r.flag){
try{r.wait();}catch(Exception ex){}
}
if(i%2==0){
r.name = "张三";
r.sex = "男";
}else{
r.name = "lisi";
r.sex = "nv";
}
//将对方线程唤醒,标记改为true
r.flag = true;
r.notify();
}
i++;
}
}
}
package cn.it.demo4;
/*
* 输出线程,对资源对象Resource中成员变量,输出值
*/
public class Output implements Runnable {
private Resource r ;
public Output(Resource r){
this.r = r;
}
public void run() {
while(true){
synchronized(r){
//判断标记,是false,等待
if(!r.flag){
try{r.wait();}catch(Exception ex){}
}
System.out.println(r.name+".."+r.sex);
//标记改成false,唤醒对方线程
r.flag = false;
r.notify();
}
}
}
}
package cn.it.demo4;
/*
* 定义资源类,有2个成员变量
* name,sex
* 同时有2个线程,对资源中的变量操作
* 1个对name,age赋值
* 2个对name,age做变量的输出打印
*/
public class Resource {
public String name;
public String sex;
public boolean flag = false;
}
package cn.it.demo4;
/*
* 开启输入线程和输出线程,实现赋值和打印值
*/
public class ThreadDemo{
public static void main(String[] args) {
Resource r = new Resource();
Input in = new Input(r);
Output out = new Output(r);
Thread tin = new Thread(in);
Thread tout = new Thread(out);
tin.start();
tout.start();
}
}
手机扫一扫
移动阅读更方便
你可能感兴趣的文章