多线程的相关概念
1.线程的两种实现方式
继承Thread类,实现Runnable接口
package com.lili.thread; import sun.awt.windows.ThemeReader; /** * 线程的休眠 * 在当前线程的执行中,暂停指定的毫秒数 释放cpu的时间片 * * @author: QiJingJing * @create: 2021/7/14 */ public class Test1 { public static void main(String[] args) { MyThread myThread = new MyThread(); // 推荐 MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); // 启动线程 myThread.start(); thread.start(); } } /** * 实现线程的第一种方式:继承Thread类 */ class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 50; i++) { System.out.println(Thread.currentThread().getName() + "-" + i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } /** * 实现线程的第二种方式:实现Runnable接口 */ class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 50; i++) { System.out.println(Thread.currentThread().getName() + "-" + i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }
2.Join与中断线程
加入线程,让调用线程先执行完毕,
package com.lili.thread; /** * join方法 * 加入线程,让调用线程先执行完毕 * * @author: QiJingJing * @create: 2021/7/14 */ public class Test2 { public static void main(String[] args) { MyRunnable2 myRunnable2 = new MyRunnable2(); Thread thread2 = new Thread(myRunnable2); thread2.start(); for (int i = 0; i < 50; i++) { System.out.println(Thread.currentThread().getName() + "--" + i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } if (i == 20) { try { // 让thread2线程执行完毕 thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); } thread2.interrupt(); } } } } 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(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }
中断线程适合自定义标记中断线程:
package com.lili.thread; /** * @author: QiJingJing * @create: 2021/7/14 */ public class Test2 { public static void main(String[] args) { MyRunnable2 myRunnable2 = new MyRunnable2(); Thread thread2 = new Thread(myRunnable2); thread2.start(); for (int i = 0; i < 50; i++) { System.out.println(Thread.currentThread().getName() + "--" + i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if (i == 20) { myRunnable2.flag = false; } } } } class MyRunnable2 implements Runnable { public boolean flag = true; @Override public void run() { int i = 0; while (flag) { System.out.println(Thread.currentThread().getName() + "--" + i++); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } } }
3.守护线程与yield
public final void setDaemon(boolean on):将此线程标记为daemon线程或用户线程,当运行的唯一线程是守护线程时,java虚拟机将退出
yield():让出本次cpu执行权,但不一定礼让成功
package com.lili.thread; /** * 守护线程和yield * * @author: QiJingJing * @create: 2021/7/14 */ public class Test3 { public static void main(String[] args) { Runnable3 runnable3 = new Runnable3(); Thread thread = new Thread(runnable3); // 线程可以分为守护线程和用户线程,当进程中没有用户线程时,JVM会退出 // 设置为守护线程 thread.setDaemon(true); //设置优先级可以提高该线程抢得cpu的概率 thread.setPriority(Thread.MAX_PRIORITY); thread.start(); for (int i = 0; i < 50; i++) { System.out.println("main--" + i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if (i == 5) { Thread.yield();//让出本次cpu的执行权 } } } } class Runnable3 implements Runnable { @Override public void run() { for (int i = 0; i < 50; i++) { System.out.println("--" + i); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } }
4.线程同步
(1)同步代码块 (2)使用同步方法 (3)使用Lock锁(更灵活的代码控制)
package com.lili.thread; import java.util.concurrent.locks.ReentrantLock; /** * 1.多线程共享数据时,会发生线程不安全的情况 * 2、多线程共享数据必须实现同步, * 3.实现同步的三种方法: * (1 同步代码块 * (2 使用同步方法 * (3 使用lock(更灵活的代码控制) * 多线程共享数据,会有安全问题,使用同步可以解决安全问题,但同时会牺牲性能,所以同步的代码块尽量保持简短,把不随线程变化的代码移除同步,不要阻塞 * * @author: QiJingJing * @create: 2021/7/15 */ public class Test4 { public static void main(String[] args) { MyRunnable5 myRunnable5 = new MyRunnable5(); Thread t1 = new Thread(myRunnable5); Thread t2 = new Thread(myRunnable5); t1.start(); t2.start(); } } class MyRunnable5 implements Runnable { private int ticket = 10;// 售票 // @Override // // 可以写方法中,表示为同步方法 // public /*synchronized*/ void run() { // for (int i = 0; i < 300; i++) { // synchronized (this) { // if(ticket>0){ // ticket--; // try { // Thread.sleep(1000); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println("你购买的票剩余"+ticket+"张"); // } // } // } // } ReentrantLock lock = new ReentrantLock(); //lock实现同步 @Override // 可以写方法中,表示为同步方法 public void run() { for (int i = 0; i < 300; i++) { lock.lock();//上锁 try { if (ticket > 0) { ticket--; try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("你购买的票剩余" + ticket + "张"); } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock();//解锁 } } } }
5.死锁
过多的同步有可能出现死锁,死锁的操作一般是在程序运行的时候才可能出现,多线程中要进行资源的共享,就需要同步,但同步过多,就可能造成死锁:
package com.lili.thread; /** * 死锁例子 * * @author: QiJingJing * @create: 2021/7/15 */ public class Test5 { private static Object obj1 = new Object(); private static Object obj2 = new Object(); public static void main(String[] args) { new Thread(() -> { synchronized (obj1) { System.out.println("obj1"); synchronized (obj2) { System.out.println("obj2"); } } }).start(); new Thread(() -> { synchronized (obj2) { System.out.println("obj2"); synchronized (obj1) { System.out.println("obj1"); } } }).start(); } }
6,生产者与消费者
(1)管程法
package com.lili.thread; /** * 测试生产者消费者模型(管程法) * * @author: QiJingJing * @create: 2021/7/15 */ public class Test6 { public static void main(String[] args) { Buffer buf = new Buffer(); new Thread(new Productor(buf)).start(); new Thread(new Consumer(buf)).start(); } } // 生产者 class Productor implements Runnable { private Buffer buffer; public Productor(Buffer buffer) { this.buffer = buffer; } // 生产 @Override public void run() { for (int i = 1; i <= 100; i++) { buffer.push(new Chicken(i)); System.out.println("成产了" + i + "只鸡"); } } } // 消费者 class Consumer implements Runnable { private Buffer buffer; public Consumer(Buffer buffer) { this.buffer = buffer; } // 生产 @Override public void run() { for (int i = 1; i <= 100; i++) { System.out.println("消费了第" + buffer.pop().getId() + "只鸡"); } } } // 产品 class Chicken { private int id; public Chicken(int id) { this.id = id; } public int getId() { return id; } public void setId(int id) { this.id = id; } } // 缓冲区 class Buffer { // 需要一个容器大小 private Chicken[] chickens = new Chicken[10]; // 容器计数器 private int count = 0; // 生产者放入产品 public synchronized void push(Chicken chicken) { //如果容器满了,需要等待消费者消费 if (count == chickens.length) { //通知消费者消费,生产者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 如果没满,我们要丢入产品 chickens[count] = chicken; count++; // 可以通知消费者消费了 this.notifyAll(); } //消费者消费产品 public synchronized Chicken pop() { // 判断能否消费 if (count == 0) { //等待生产者生产,消费者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 如果可以消费 count--; Chicken chicken = chickens[count]; // 通知生产者生产 this.notifyAll(); return chicken; } }
(2)信号灯法
package com.lili.thread; /** * 生产者与消费者(信号灯法) * * @author: QiJingJing * @create: 2021/7/15 */ public class Test7 { public static void main(String[] args) { TV tv = new TV(); new Thread(new Player(tv)).start(); new Thread(new Watcher(tv)).start(); } } // 生产者->演员 class Player implements Runnable { private 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 Watcher implements Runnable { private TV tv; public Watcher(TV tv) { this.tv = tv; } @Override public void run() { for (int i = 0; i < 20; i++) { this.tv.watch(); } } } // 产品-节目 class TV { private String voice;// 表演的节目 private boolean flag = true; // 表演 public synchronized void play(String voice) { if (!flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } 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) { e.printStackTrace(); } } System.out.println("观众观看了" + voice); // 通知演员表演 this.notifyAll(); this.flag = !this.flag; } }
7.线程池
背景:经常创建和销毁,使用量特别大的资源,比如并发下的线程,对性能影响很大
思路:提前创建好多个线程,放入线程池,使用时直接获取,使用完放回池中,可以避免频繁创建销毁,实现重复利用,类似生活中的交通工具
package com.lili.thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @author: QiJingJing * @create: 2021/7/15 */ public class Test8 { public static void main(String[] args) { // 1.创建服务,创建线程池 // 参数为线程池大小 ExecutorService service = Executors.newFixedThreadPool(10); service.execute(new MyRunnable7()); service.execute(new MyRunnable7()); service.execute(new MyRunnable7()); service.execute(new MyRunnable7()); //2. 关闭链接 service.shutdown(); } } class MyRunnable7 implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }