线程复习
本章节根据狂神的多线程视频进行整理,狂神多线程->av54171171
1.线程的概念
概念:线程是操作系统能够进行运算调度的最小单位。
2.Java实现线程的3种方式
继承Thread类、实现Runnable接口、实现Callable接口
Callable不常用,容易忘记。所以重新写一遍
ExecutorService ser = Executors.newFixedThreadPool(3);
Future<Boolean> future = ser.submit(new TestCallable());
Boolean b = future.get();
System.out.println(b);
ser.shutdownNow();
线程安全:
多个线程操作同一个对象,会造成线程不安全
3.线程的5种状态
创建状态、就绪状态、阻塞状态、运行状态、死亡状态
new、runnable、blocked、waiting(timed_waiting)、terminated
Java中方法:
线程休眠 sleep
线程礼让 yield
让运行状态转变为就绪状态,让CPU重新调度,礼让不一定成功
测试:
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield, "a").start();
new Thread(myYield, "b").start();
}
}
class MyYield implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始执行");
Thread.yield(); // 礼让
System.out.println(Thread.currentThread().getName() + "结束执行");
}
}
打印结果:
run() 进行到一半进行了礼让,但是这种情况不会总是发生
a开始执行
b开始执行
a结束执行
b结束执行
线程强制执行 join:
在这个例子中,我们看到在200之前,这两个线程还是接受CPU调度的,但是一旦thread进行了join,那么强制执行thread。
public class TestJoin implements Runnable{
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
for(int i = 0; i < 1000; i++) {
System.out.println(i);
if(i == 200) {
thread.join();
}
}
}
@Override
public void run() {
for(int i = 0; i < 100; i++){
System.out.println("线程VIP"+i);
}
}
}
检测线程状态:
public class Observer {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("///////");
});
Thread.State state = thread.getState();
System.out.println(state);
thread.start();
while(state != Thread.State.TERMINATED) {
System.out.println(thread.getState());
Thread.sleep(100);
state = thread.getState();
}
}
}
线程的优先级:
线程默认优先级为5,我们可以通过setPriority和getPriority进行获取和设置优先级。
守护线程:
所有普通线程结束后,也会结束。
public class DaemonTest {
public static void main(String[] args) {
new Thread(new You()).start();
Thread thread = new Thread(new God());
thread.setDaemon(true);
thread.start();
}
}
class You implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("我还活着");
}
}
}
class God implements Runnable {
@Override
public void run() {
while(true) {
System.out.println("保佑你");
}
}
}
4.线程同步机制
三个不安全的案例
- 买票买负数
public class BuyTicket implements Runnable {
private int ticketNums = 10;
private boolean flag = true;
@Override
public void run() {
while (flag) {
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void buy() throws InterruptedException {
if(ticketNums <= 0) {
flag = false;
return;
}
Thread.sleep(10);
System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
}
public static void main(String[] args) {
BuyTicket ticket = new BuyTicket();
new Thread(ticket, "1").start();
new Thread(ticket, "2").start();
new Thread(ticket, "3").start();
}
}
- 银行取钱
public class UnsafeBank {
public static void main(String[] args) {
Account account = new Account(1000,"steveyu");
Drawing drawing = new Drawing(300, account);
Drawing drawing2 = new Drawing(800, account);
drawing.start();
drawing2.start();
}
}
class Account {
public int money;
public String name;
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
class Drawing extends Thread {
int drawing;
Account account;
public Drawing(int drawing, Account account) {
this.drawing = drawing;
this.account = account;
}
@Override
public void run() {
if (account.money - drawing < 0) {
System.out.println(Thread.currentThread().getName() + "钱不够");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money -= drawing;
System.out.println(account.name + "余额为" + account.money);
System.out.println("手里" + drawing);
}
}
- 不安全集合
public class UnSafeList {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
new Thread(() -> list.add(Thread.currentThread().getName())).start();
}
System.out.println(list.size());
}
}
Synchronized同步方法默认锁this,通常用同步方法块
ArrayList在JUC中,是CopyOnWriteArrayList
5.死锁
多个线程互相抱着对象的资源,进行相互对峙
案例:女孩化妆
public class DeadLock {
public static void main(String[] args) {
Makeup makeup = new Makeup(0, "beauty1");
Makeup makeup2 = new Makeup(1, "beauty2");
makeup.start();
makeup2.start();
}
}
class Lipstick { }
class Mirror { }
class Makeup extends Thread {
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;//选择
String girlName;//化妆的人
Makeup(int choice, String girlName) {
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
if (choice == 0) {
synchronized (lipstick) {
System.out.println(this.girlName + "获得口红的锁");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (mirror) {
System.out.println(this.girlName + "获得镜子的锁");
}
}
} else {
synchronized (mirror) {
System.out.println(this.girlName + "获得镜子的锁");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lipstick) {
System.out.println(this.girlName + "获得口红的锁");
}
}
}
}
}
Lock锁,JDK5.0
Lock是一个借口,ReetrantLock是可重用锁。
- lock显式,synchronized隐式
- lock性能更好,有更好的扩展性
- 使用优先顺序,Lock>同步代码块>同步方法
public class TestLock {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket).start();
new Thread(buyTicket).start();
new Thread(buyTicket).start();
}
}
class BuyTicket implements Runnable {
int ticketNums= 10;
private final Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
try {
if(ticketNums > 0) {
Thread.sleep(100);
System.out.println("剩余" + ticketNums--);
}else {
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
lock.unlock();
}
}
}
}
6.线程通信机制(生产者消费者问题)
我们的线程在通讯的时候,存在一个生产者消费者问题。
-
假设仓库中只能存放一件产品,生产者将产品放入仓库,消费者取出。
-
如果仓库没有产品,生产者放入仓库,否则停止生产并等待,直到产品全部取走为止
-
如果仓库中存在产品,消费着可以将产品取走并消费,否则停止消费,直到仓库再放入产品
问题:
在生产者消费者问题中,仅仅有synchronized是不够的
- synchronized可以阻止并发更新同一个共享资源,实现同步
- synchronized不能用来实现不同线程中的消息传递
为了解决这个问题,Java引入一个wait和notify
wait() 表示现场一直等待,直到其他线程通知
wait(long timeout) 等待毫秒数
notify() 唤醒一个处于等待状态的线程
notifyAll() 唤醒同一个对象所有调用wait() 方法的线程,优先级别高的线程优先调度
解决方式1: 引入数据缓冲区(管程法)
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
Producter producter = new Producter(container);
Consumer consumer = new Consumer(container);
producter.start();
consumer.start();
}
}
// 生产者
class Producter extends Thread {
SynContainer container;
public Producter(SynContainer container) {
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("生产了" + i + "只🐔");
container.push(new Chicken(i));
}
}
}
// 消费者
class Consumer extends Thread {
SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了" + container.pop().id + "只🐔");
}
}
}
// 商品
class Chicken {
int id;
public Chicken(int id) {
this.id = id;
}
}
// 缓冲区
class SynContainer {
Chicken[] chickens = new Chicken[10];
int index;
public synchronized void push(Chicken chicken) {
if(index == chickens.length) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
chickens[index++] = chicken;
this.notifyAll();
}
public synchronized Chicken pop() {
if(index == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();
return chickens[--index];
}
}
解决方式2: 使用一个标志位置(信号灯法)
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
Player player = new Player(tv);
Watcher watcher = new Watcher(tv);
player.start();
watcher.start();
}
}
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 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) {
this.tv.play("快乐大本营");
}else {
this.tv.play("抖音");
}
}
}
}
class TV {
String voice;
boolean flag = true;
public synchronized void play(String voice) {
if (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演员表演了" + voice);
this.voice = voice;
this.flag = !this.flag;
this.notifyAll();
}
public synchronized void watch() {
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观众观看了" + voice);
this.flag = !this.flag;
this.notifyAll();
}
}
7.线程池
经常创建和销毁一些资源,在并发的时候会影响很大,所以使用线程池能解决这个问题
- ExecutorService 线程池接口
- void execute(Runnable command) 执行runnable
- <T> Future <T> submit(Callable<T> task) 执行任务,有返回值,一般使用Callable
- void shutdown() 关闭线程池
- Executors 工具类
public class TestPool {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
service.execute(new MyThread());
}
service.shutdownNow();
}
}
class MyThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
ExecutorService ser = Executors.newFixedThreadPool(3);
Future<Boolean> future = ser.submit(new TestCallable());
Boolean b = future.get();
System.out.println(b);
ser.shutdownNow();