Java多线程
以下内容是狂神Java课堂笔记
线程简介
一个进程中可以有多个线程
线程实现
线程创建
main线程
gc线程
三种创建方式
Thread类就实现了Runnable接口,所以Runnable接口最重要
Callable接口在工作后使用得比较多
Thread类
第一种创建方式:
交替执行
如果不是用start方法,直接run(),则会先执行run()方法体再执行主线程。
注意:线程开启不一定执行,由cpu进行调度。
**多线程下载图片的样例(略过)
安装commons-io的那个包:https://blog.csdn.net/rngweskt/article/details/84181530
狂神对应讲解的视频:https://www.bilibili.com/video/BV1V4411p7EF/?p=4&spm_id_from=pageDriver&vd_source=778e40652a97cf1a0f96297054b101d7
Runnable接口
之所以要在代理Thread的括号中加入runnable类,是因为:
对比:
**多线程下载图片样例改用runnable接口:
https://www.bilibili.com/video/BV1V4411p7EF/?p=5&spm_id_from=pageDriver&vd_source=778e40652a97cf1a0f96297054b101d7
小结:
多线程并发问题
package thread;
// 多个线程同时操作同一个对象
// 买火车票为例
// 发现问题:多个线程操作同一资源的情况下,线程不安全,数据紊乱
public class TestThread3 implements Runnable{
// ticketNum
private int ticketNums = 10;
@Override
public void run() {
while (true) {
if (ticketNums <= 0) {
break;
}
// 模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + " --> 拿到了第" + ticketNums-- + "张票");
}
}
public static void main(String[] args) {
TestThread3 ticket = new TestThread3();
new Thread(ticket, "小米姑娘").start();
new Thread(ticket, "老师").start();
new Thread(ticket, "黄牛").start();
}
}
Callable
*Callable:通过创建执行服务来实现多线程
静态代理
package thread;
// 静态代理模式总结:
// 1. 真实对象和代理对象实现同一个接口
// 2. 代理对象要代理真实角色
// 好处
// 1. 代理对象可以做真实对象做不了的事情
// 2. 真实对象专注于自己的事情
public class StaticProxy {
public static void main(String[] args) {
// You you = new You();
// you.inductMarry();
Company wedding = new Company(new You());
wedding.inductMarry();
}
}
interface Marry {
void inductMarry();
}
// 真实角色
class You implements Marry {
@Override
public void inductMarry() {
System.out.println("I will be married!");
}
}
// 代理
class Company implements Marry {
// 代理-》真实目标角色
public Marry target;
public Company(Marry target) {
this.target = target;
}
@Override
public void inductMarry() {
System.out.println("decorating...");
this.target.inductMarry(); // 真实对象
System.out.println("end...");
}
}
其中
Company wedding = new Company(new You());
wedding.inductMarry();
可以写成
new Company(new You()).inductMarry();
这就类似于(Runnable接口实现,用到了Lamda表达式)
new Thread(() -> System.out.println("I will be married!")).start();
Lamda表达式
函数式接口:
interface ILike {
void lambda();
}
传统的一个接口声明和调用:
package lambda;
// 推导lambda表达式
public class TestLambda1 {
public static void main(String[] args) {
ILike like = new Like(); // 这里不用Like like = new Like();是为了不暴露Like类的实现
like.lambda();
}
}
// 1. 定义一个函数式接口
interface ILike {
void lambda();
}
// 2. 实现类
class Like implements ILike {
@Override
public void lambda() {
System.out.println("I like lambda.");
}
}
使用静态内部类进行简化:
package lambda;
// 推导lambda表达式
public class TestLambda1 {
// 3. 静态内部类
static class Like2 implements ILike {
@Override
public void lambda() {
System.out.println("I like lambda2.");
}
}
public static void main(String[] args) {
ILike like = new Like(); // 这里不用Like like = new Like();是为了不暴露Like类的实现
like.lambda();
like = new Like2();
like.lambda();
}
}
// 1. 定义一个函数式接口
interface ILike {
void lambda();
}
// 2. 实现类
class Like implements ILike {
@Override
public void lambda() {
System.out.println("I like lambda.");
}
}
转换成局部内部类:
package lambda;
// 推导lambda表达式
public class TestLambda1 {
// 3. 静态内部类
static class Like2 implements ILike {
@Override
public void lambda() {
System.out.println("I like lambda2.");
}
}
public static void main(String[] args) {
ILike like = new Like(); // 这里不用Like like = new Like();是为了不暴露Like类的实现
like.lambda();
like = new Like2();
like.lambda();
// 4. 局部内部类
class Like3 implements ILike {
@Override
public void lambda() {
System.out.println("I like lambda3.");
}
}
like = new Like3();
like.lambda();
}
}
// 1. 定义一个函数式接口
interface ILike {
void lambda();
}
// 2. 实现类
class Like implements ILike {
@Override
public void lambda() {
System.out.println("I like lambda.");
}
}
再转换成匿名内部类:
package lambda;
// 推导lambda表达式
public class TestLambda1 {
// 3. 静态内部类
static class Like2 implements ILike {
@Override
public void lambda() {
System.out.println("I like lambda2.");
}
}
public static void main(String[] args) {
ILike like = new Like(); // 这里不用Like like = new Like();是为了不暴露Like类的实现
like.lambda();
like = new Like2();
like.lambda();
// 4. 局部内部类
class Like3 implements ILike {
@Override
public void lambda() {
System.out.println("I like lambda3.");
}
}
like = new Like3();
like.lambda();
// 5. 匿名内部类
like = new ILike() {
@Override
public void lambda() {
System.out.println("I like lambda4.");
}
};
like.lambda();
}
}
// 1. 定义一个函数式接口
interface ILike {
void lambda();
}
// 2. 实现类
class Like implements ILike {
@Override
public void lambda() {
System.out.println("I like lambda.");
}
}
进一步简化,即lambda:
package lambda;
// 推导lambda表达式
public class TestLambda1 {
// 3. 静态内部类
static class Like2 implements ILike {
@Override
public void lambda() {
System.out.println("I like lambda2.");
}
}
public static void main(String[] args) {
ILike like = new Like(); // 这里不用Like like = new Like();是为了不暴露Like类的实现
like.lambda();
like = new Like2();
like.lambda();
// 4. 局部内部类
class Like3 implements ILike {
@Override
public void lambda() {
System.out.println("I like lambda3.");
}
}
like = new Like3();
like.lambda();
// 5. 匿名内部类
like = new ILike() {
@Override
public void lambda() {
System.out.println("I like lambda4.");
}
};
like.lambda();
// 6. lambda表达式简化
like = () -> {
System.out.println("I like lambda5.");
};
like.lambda();
}
}
// 1. 定义一个函数式接口
interface ILike {
void lambda();
}
// 2. 实现类
class Like implements ILike {
@Override
public void lambda() {
System.out.println("I like lambda.");
}
}
如果有参数:
interface ILike {
void lambda(int a, int b);
}
那么对应的lambda表达式变为:
ILike like = (int a, int b) -> {
System.out.println("I like lambda.");
};
可以去掉类型进行简化:(要去掉都去掉)
ILike like = null;
like = (a, b) -> {
System.out.println("I like lambda.");
};
可以去掉括号进一步简化:(仅有一个参数)
ILike like = null;
like = a -> {
System.out.println("I like lambda.");
};
可以去掉花括号进一步简化:(仅有一行语句)
ILike like = null;
like = (a, b) -> System.out.println("I like lambda.");
线程状态
线程方法
线程停止
表示这个方法已经过时了不建议使用
模拟线程停止:
package thread;
// 测试线程停止
// 1. 建议线程正常停止 --- 利用次数,不建议死循环
// 2. 建议使用标志位 --- 设置标志位
// 3. 不建议使用stop或者destroy等过时或JDK不建议使用的方法
public class TestStop implements Runnable{
// 1. set flag
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag) {
System.out.println("run..." + i++);
}
}
// 2. set a public method to change flag
public void stop() {
this.flag = false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main..." + i);
if (i == 900) {
testStop.stop();
System.out.println("Thread stop!");
}
}
}
}
执行到900轮,线程flag设置为false,但是有可能存在之前线程还没执行结束的情况
线程休眠
模拟网络延时:放大问题的发生性
模拟倒计时:
打印系统时间:
每个对象都有一个锁,sleep操作不会释放锁。
线程礼让 yield
示例:礼让成功(不一定成功)
线程合并 join
示例:
线程状态观测
Thread.State state = thread.getState();
线程死亡或结束后不能再被启动了
线程优先级
示例:
package thread;
public class TestPriority {
public static void main(String[] args) {
// main
System.out.println(Thread.currentThread().getName() + " --- " +Thread.currentThread().getPriority());
MyPriority myPriority = new MyPriority();
Thread t1 = new Thread(myPriority);
Thread t2 = new Thread(myPriority);
Thread t3 = new Thread(myPriority);
Thread t4 = new Thread(myPriority);
// 先设置优先级再启动
t1.start();
t2.setPriority(Thread.MAX_PRIORITY); // 10
t2.start();
t3.setPriority(4);
t3.start();
t4.setPriority(8);
t4.start();
}
}
class MyPriority implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " --- " +Thread.currentThread().getPriority());
}
}
守护线程 daemon
示例:
package thread;
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
User user = new User();
// set daemon
Thread thread = new Thread(god);
thread.setDaemon(true);
thread.start();
new Thread(user).start();
}
}
// daemon
class God implements Runnable {
@Override
public void run() {
while (true) {
System.out.println("God bless!");
}
}
}
// user thread
class User implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("Yolo!");
}
System.out.println("---END---");
}
}
即使守护线程没有推出循环,但是用户线程结束,程序就结束了。
线程同步(重点)
多个线程操作同一个资源
并发:统一对象被多个线程同时操作。
使用队列和锁解决线程同步的问题。
同步方法
弊端:
示例1:只有一个对象
示例2:不只一个对象
同步块:Obj
是需要修改的资源
示例3:
// 原来不安全的时候,list
始终无法到达10000
// 锁上list
,线程安全了
JUC
// 安全的类 CopyOnWriteArrayList
死锁
多个线程抱着对方需要的资源,形成僵持
Lock锁
示例:
// 不安全的情况
package syn;
public class TestLock {
public static void main(String[] args) {
Tickets tickets = new Tickets();
new Thread(tickets).start();
new Thread(tickets).start();
new Thread(tickets).start();
}
}
// buy tickets
class Tickets implements Runnable {
private int ticketNum = 10;
@Override
public void run() {
while (ticketNum > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(ticketNum--);
}
}
}
加上锁后:
package syn;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args) {
Tickets tickets = new Tickets();
new Thread(tickets).start();
new Thread(tickets).start();
new Thread(tickets).start();
}
}
// buy tickets
class Tickets implements Runnable {
private int ticketNum = 10;
// 定义lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock(); // 上锁
if (ticketNum > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(ticketNum--);
} else {
break;
}
} finally {
lock.unlock(); // 解锁
}
}
}
}
注意这里不是:
package syn;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args) {
Tickets tickets = new Tickets();
new Thread(tickets).start();
new Thread(tickets).start();
new Thread(tickets).start();
}
}
// buy tickets
class Tickets implements Runnable {
private int ticketNum = 10;
// 定义lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (ticketNum > 0) {
try {
lock.lock(); // 上锁
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(ticketNum--);
} finally {
lock.unlock(); // 解锁
}
}
}
}
当多个线程同时访问共享资源(在这里是Tickets
对象)时,使用锁是一种常见的方式来确保线程安全。在你的例子中,使用了ReentrantLock
来实现线程同步。
在第一个代码片段中,每个线程的执行顺序如下:
- 线程1进入
run
方法,执行lock.lock()
,获取锁。 - 线程1检查
ticketNum
大于0,然后打印ticketNum--
,然后释放锁lock.unlock()
。 - 线程2进入
run
方法,执行lock.lock()
,获取锁。 - 线程2检查
ticketNum
大于0,然后打印ticketNum--
,然后释放锁lock.unlock()
。 - 线程3进入
run
方法,执行lock.lock()
,获取锁。 - 线程3检查
ticketNum
大于0,然后打印ticketNum--
,然后释放锁lock.unlock()
。
因为每个线程在获取锁后都会连续执行直到释放锁,所以输出是按顺序递减的。
在第二个代码片段中,每个线程的执行顺序如下:
- 线程1进入
run
方法,执行lock.lock()
,获取锁。 - 线程1打印
ticketNum--
,然后释放锁lock.unlock()
,然后执行while
条件检查。 - 线程2进入
run
方法,执行lock.lock()
,获取锁。 - 线程2打印
ticketNum--
,然后释放锁lock.unlock()
,然后执行while
条件检查。 - 线程3进入
run
方法,执行lock.lock()
,获取锁。 - 线程3打印
ticketNum--
,然后释放锁lock.unlock()
,然后执行while
条件检查。
因为while
条件检查发生在锁内部,每个线程在释放锁后会重新检查while
条件,所以输出是同时递减的。
在第二个代码片段中,while
条件检查发生在锁内部,这可能导致检查时机的混乱。因为每个线程在释放锁之后会重新检查while
条件,所以在释放锁和再次检查条件之间,其他线程可能已经修改了共享的ticketNum
值。
线程通信问题
线程协作
生产者消费者模式
- 管程法
示例:
package thread;
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Producer(container).start();
new Consumer(container).start();
}
}
// producer
class Producer extends Thread {
SynContainer synContainer;
public Producer(SynContainer synContainer) {
this.synContainer = synContainer;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
synContainer.push(new Chicken(i));
System.out.println("生产了" + i + "只鸡");
}
}
}
// consumer
class Consumer extends Thread {
SynContainer synContainer;
public Consumer(SynContainer synContainer) {
this.synContainer = synContainer;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了" + synContainer.pop().id + "只鸡");
}
}
}
// chicken
class Chicken {
int id;
public Chicken(int id) {
this.id = id;
}
}
// synContainer
class SynContainer {
// 设置容器大小
Chicken[] chickens = new Chicken[10];
// 设置容器计数器
int count = 0;
// 生产者放入产品
public synchronized void push (Chicken chicken) {
// 如果容器满
if (count == chickens.length) {
// 等待生产
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 如果没满
chickens[count++] = chicken;
// 通知消费者消费
this.notify();
}
// 消费者消费产品
public synchronized Chicken pop() {
// 判断是否能消费
if (count == 0) {
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 可以消费
count--;
Chicken chicken = chickens[count];
// 通知生产者已消费
this.notify();
return chicken;
}
}
- 信号灯法
package thread;
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();
new Watcher(tv).start();
}
}
// 生产者 -- 演员
class Player extends Thread {
TV tv;
public Player(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (i % 2 == 0) tv.play("show!!!");
else tv.play("ad!!!");
}
}
}
// 消费者 -- 观众
class Watcher extends Thread {
TV tv;
public Watcher(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
// 产品 -- 节目
class TV {
// 演员表演,观众等待
// 观众观看,演员等待
String voice; // 节目
boolean flag = true;
// 表演
public synchronized void play(String voice) {
if (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("表演了" + voice);
// 通知观看
this.notifyAll();
this.voice = voice;
this.flag = !this.flag;
}
// 观看
public synchronized void watch() {
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("观看了" + voice);
// 通知表演
this.notifyAll();
this.flag = !this.flag;
}
}
线程池
package thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestPool {
public static void main(String[] args) {
// 1. 创建服务,创建线程池
// newFixedThreadPool(线程池大小)
ExecutorService service = Executors.newFixedThreadPool(10);
// 执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
// 2. 关闭服务
service.shutdown();
}
}
class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
很类似collable
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!