Java多线程基础
本文最后更新于:2023年11月16日 下午
Java多线程
1.程序,进程,线程
- 程序:(可以执行的静态代码,是保存在硬盘上的一个文件)是为完成特定任务,用某种语言编写的一组指令的集合。即一段静态的代码,静态对象.
- 进程:(正在执行中的一个程序,在内存中处于激活状态,有生命周期)是程序的一次执行过程,或是正在运行的一个程序。动态过程:有它自身的产生、存在和消亡的过程
- 线程:(进程中的子任务)进程可以进一步细化为线程,是一个程序内部的一条执行路径
2.Java中多线程的创建和使用
- 实现Runnable接口与继承Thread类
Thread类的主要方法
- 每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主题称为线程体
- 通过该Thread对象的start()方法来调用这个线程
- static Thread currentThread(),返回当前方法正在执行此方法所压入的栈的线程对象
- void join()它的作用是调用此方法的另一个线程阻塞,当前线程执行完再执行另一个线程
- static void sleep(long millis)作用是让当前线程(正在执行此方法的栈的线程)进入睡眠状态
- 两种方式结束sleep状态:1.时间到了。2.被其他进程打断 interrupt() 方法
创建并启动线程的方式
实现Runnable的方式
- 写一个具体类,实现Runnable接口,并实现接口中的抽象方法run(),这个run方法就是线程体
- 创建这个具体类对象,并把这个对象作为实参,创建Thread线程对象
- 调用Thread线程对象的start方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package multi_thread;
public class Counter implements Runnable { private int cnt = 200;
@Override public void run() { System.out.println(cnt); for (int i = 0; i < 50; i++) { synchronized ("") {
cnt -= 2; try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ": " + cnt); } } }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package multi_thread;
public class CounterTest {
public static void main(String[] args) { Runnable counter = new Counter(); Thread thread1 = new Thread(counter); Thread thread2 = new Thread(counter); thread1.start(); thread2.start(); }
}
|
继承Thread的方式
- 写一个类,继承Thread,并重写run方法,此方法就是线程体
- 创建这个类的对象,相当于创建了线程对象
- 调用这个线程对象的start方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package multi_thread; class MyThread extends Thread{ @Override public void run() { for(int i=0;i<100;i++) { System.out.println(currentThread().getName()+" "+i); } } } public class ThreadTest2 {
public static void main(String[] args) { Thread myThread1 = new MyThread(); myThread1.start(); for(int i=0;i<100;i++) { System.out.println("-----main "+i); } }
}
|
使用callable接口创建多线程
- 落地方法call()
- Callable接口作为JDK1.5新增的接口,与使用Runnable相比其功能更强大些。
- 相比run()方法,可以有返回值
- 方法可以抛出异常
- 支持泛型的返回值
- 需要借助FutureTask类,比如获取返回结果。
- Callable接口一般用于配合ExecutorService使用
Feture接口
- 可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
- FutrueTask是Futrue接口的实现类
- FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
- 多个线程同时执行一个FutureTask,只要一个线程执行完毕,其他线程不会再执行其call()方法。
- get()方法会阻塞当前线程!
实例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class MyThread implements Callable<Integer> { @Override public Integer call() throws Exception { System.out.println(Thread.currentThread().getName()+" Come in call"); //睡5秒 TimeUnit.SECONDS.sleep(5); //返回200的状态码 return 200; } }
public class CallableTest { public static void main(String[] args) throws InterruptedException, ExecutionException { MyThread myThread = new MyThread(); FutureTask<Integer> futureTask = new FutureTask<>(myThread); new Thread(futureTask, "未来任务").start(); System.out.println("主线程结束!"); Integer integer = futureTask.get(); System.out.println(integer); } }
|
线程池创建多线程
- 线程池:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。因此提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。
- 优点
- 提高响应速度(减少了创建新线程的时间)
- 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
- 便于线程管理
- ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor。
- void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable
- Future submit(Callable task):执行任务,有返回值,一般用来执行Callable
- void shutdown() :关闭连接池
- 创建线程池的方式:
- 直接通过ThreadPoolExecutor实现类new
- 通过工厂类Executors的静态方法创建,本质上也是通过1)创建的线程池
1 2 3 4 5 6 7 8 9 10 11
| public static void main(String[] args) { //创建一个包含10个线程的线程池 ExecutorService executorService = Executors.newFixedThreadPool(10); // ExecutorService executorService = Executors.newSingleThreadExecutor(); for (int i = 0; i < 12; i++) { executorService.execute(()->{ System.out.println(Thread.currentThread().getName()); }); } executorService.shutdown(); }
|
3.线程的同步
- synchronized (lock){}
- ()中是一个锁对象,任意对象都可以做锁,称为互斥锁,作用是只允许一个线程进入执行,其他线程等待
- 具有原子性,不可分割
- synchronized()可重入锁(同一个线程可以无限次获取同一个锁)
- 避免死锁:不要嵌套synchronized,即使有嵌套,锁对象尽量少
synchronized和Lock的区别
- synchronized不需要手动上锁和解锁,lock需要手动上锁和解锁
- synchronized能实现的功能lock都可以实现,且lock更加强大
JUC工具类
ReentrantReadWriteLock
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| package com.atguigu.juc;
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
class MyQueue { ReadWriteLock rwl = new ReentrantReadWriteLock();
private Object obj;
public void readObj() { rwl.readLock().lock(); try { System.out.println(Thread.currentThread().getName() + "当前线程读到的内容是:" + obj);
} finally { rwl.readLock().unlock(); } }
public void writeObj(Object obj) { rwl.writeLock().lock(); try { this.obj = obj; System.out.println(Thread.currentThread().getName() + "当前线程写入的内容是:" + obj); } finally { rwl.writeLock().unlock(); } }
}
public class ReadWriteLockDemo { public static void main(String[] args) throws InterruptedException { MyQueue mq = new MyQueue(); new Thread(() -> { mq.writeObj("写入的内容"); }, "AA").start(); ExecutorService executorService = Executors.newFixedThreadPool(100); for (int i = 0; i < 100; i++) { executorService.execute(() -> { mq.readObj(); }); } executorService.shutdown(); } }
|
CountDownLatch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| package com.atguigu.juc;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { CountDownLatch cd = new CountDownLatch(6); for (int i = 1; i <= 6; i++) { new Thread(()->{System.out.println(Thread.currentThread().getName()+"号同学离开教室");}, String.valueOf(i)).start(); cd.countDown(); } cd.await(); System.out.println(Thread.currentThread().getName()+"班长锁门"); }
}
|
CyclicBarrier
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| package com.atguigu.juc;
import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo { private static final int NUMBER = 7; public static void main(String[] args) { CyclicBarrier cb = new CyclicBarrier(NUMBER, ()-> {System.out.println("可以召唤神龙了");}); for(int i=1;i<=NUMBER;i++) { new Thread(()->{System.out.println(Thread.currentThread().getName()+"号被收集"); try { cb.await(); } catch (Exception e) { e.printStackTrace(); } },String.valueOf(i) ).start(); } } }
|
Semaphore
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| package com.atguigu.juc;
import java.util.Random; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit;
public class SemaphoreDemo { public static void main(String[] args) { Semaphore semaphore = new Semaphore(3); for (int i = 1; i <= 6; i++) { new Thread(() -> { try { semaphore.acquire(); System.out.println(Thread.currentThread().getName() + "号驶入停车位"); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName() + "号驶出停车位"); semaphore.release(); } catch (Exception e) { e.printStackTrace(); } }, String.valueOf(i)).start(); } } }
|