多线程
四种实现方式
1,Thread 重写 run 方法
/**
* @Author YIFan
* @Date 2023-2-27 11:11
* @Version 1.0
*/
public class MyThread extends Thread{
@Override
public void run() {
System.out.println("Thread重写run方法");
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}
占用继承名额,类是单继承,接口可以多继承
2,Runnable 重写 run 方法
/**
* @Author YIFan
* @Date 2023-2-27 11:15
* @Version 1.0
*/
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("Thread重写run方法");
}
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start();
new Thread(()-> System.err.println("Lambda 写法")).start();
}
}
3,Callable 重写 call 方法 有返回值
/**
* @Author YIFan
* @Date 2023-2-27 11:21
* @Version 1.0
*/
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "call";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> task = new FutureTask<>(new MyCallable());
Thread thread = new Thread(task);
thread.start();
String s = task.get();
System.err.println(s);
}
}
使用 FutureTask+Thread 可以拿到异步处理结果
4,使用线程池
线程池五大状态
1)RUNNING:
能够接收新任务以及对已添加的任务进行处理,线程池一旦被创建,就处于RUNNING状态,且线程池中的任务数为0
2)SHUTDOWN:
不接收新任务,但能处理已添加好的任务
3)STOP:
不接收新任务,不处理已添加的任务,并且会中断正在处理的任务
4)TIDYING:
线程池中没有线程运行,会自动变成TIDYING状态然后调用terminated方法,该方法为空,留给自己扩展
5)TERMINATED:
线程池彻底终止,就会变成TERMINATED状态,
线程池执行流程

锁
Synchronized & Lock
可以就把它想象成现实中的锁, 所以它只不过是一块
令牌
, 一把尚方宝剑
, 它是木头做的还是金属做的并不重要, 你可以拿任何东西当作锁, 重要的是它代表的含义: 谁持有它, 谁就有独立访问临界区
- synchronized 修饰普通方法时,锁的是当前对象,等价于 synchronized (this),既方法调用所在的对象的方法
- synchronized 修饰静态方法时,锁的是所有对象的方法,等价于 synchronized (Xxx.class),既 Class对象
static 修饰 synchronized 方法后,使这个类的所有实例执行该方法时 都互不中断。synchronized 作用同步了所有的对象
class SyncThread implements Runnable {
private static int count;
public SyncThread() {
count = 0;
}
public void run() {
synchronized(this) {
for (int i = 0; i < 5; i++) {
try {
System.out.println(Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
SyncThread syncThread = new SyncThread();
//线程1和线程2使用了SyncThread类的同一个对象实例
//因此, 这两个线程中的synchronized(this), 持有的是同一把锁
Thread thread1 = new Thread(syncThread, "SyncThread1");
Thread thread2 = new Thread(syncThread, "SyncThread2");
thread1.start();
thread2.start();
}
}
----
SyncThread1:0
SyncThread1:1
SyncThread1:2
SyncThread1:3
SyncThread1:4
SyncThread2:5
SyncThread2:6
SyncThread2:7
SyncThread2:8
SyncThread2:9
这里两个线程SyncThread1
和 SyncThread2
持有同一个对象syncThread
的锁, 因此同一时刻, 只有一个线程能访问同步代码块
public static void main(String[] args) {
Thread thread1 = new Thread(new SyncThread(), "SyncThread1");
Thread thread2 = new Thread(new SyncThread(), "SyncThread2");
thread1.start();
thread2.start();
}
SyncThread1:0
SyncThread2:1
SyncThread1:2
SyncThread2:3
SyncThread1:4
SyncThread2:5
SyncThread1:6
SyncThread2:7
SyncThread1:8
SyncThread2:9
虽然它们都用this
作为锁, 但是this
指代的对象在这两个线程中不是同一个对象, 两个线程各自都能获得锁, 因此各自都能执行这一段同步代码块
synchronized(this) 只能锁住当前对象,多个实例对象只能用类锁(方法用static修饰)
public class SynchronizedStaticBlockTest {
private void print(){
synchronized (SynchronizedStaticBlockTest.class){
String name = Thread.currentThread().getName();
for (int i = 0; i < 5; i++) {
System.out.println("Hello " + name);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
SynchronizedStaticBlockTest s1 = new SynchronizedStaticBlockTest();
SynchronizedStaticBlockTest s2 = new SynchronizedStaticBlockTest();
new Thread(() -> {
System.out.println("Thread1 is start");
s1.print();
System.out.println("Thread1 is end");
}).start();
new Thread(() -> {
System.out.println("Thread2 is start");
s2.print();
System.out.println("Thread2 is end");
}).start();
}
}
Thread1 is start
Hello Thread-0
Thread2 is start
Hello Thread-0
Hello Thread-0
Hello Thread-0
Hello Thread-0
Thread1 is end
Bye Bye Thread-1
Bye Bye Thread-1
Bye Bye Thread-1
Bye Bye Thread-1
Bye Bye Thread-1
Thread2 is end
修饰对象 | 作用域 | 作用对象 | 影响对象 |
---|---|---|---|
普通方法 | 整个方法 | 对象 | 当前对象 |
静态方法 | 整个方法 | 类 | 当前类 |
代码块-类 | 整个代码块 | 类 | 当前类及该类所有对象 |
代码块-对象 | 整个代码块 | 对象 | 指定对象 |
Synchronized 可以实现的锁
可重入锁(Reentrant Lock): synchronized 实现的锁是可重入的,允许同一个线程多次获取同一个锁,而不会造成死锁。这意味着线程在持有锁的情况下可以再次获取相同的锁,而不会被阻塞。
排它锁/互斥锁/独占锁: synchronized 实现的锁是排它的,也就是说,在任意时刻只有一个线程能够获取到锁,其他线程必须等待该线程释放锁才能继续执行。这确保了同一时刻只有一个线程可以访问被锁定的代码块或方法,从而保证了数据的一致性和完整性。
悲观锁: synchronized 实现的锁属于悲观锁,因为它默认情况下假设会发生竞争,并且会导致其他线程阻塞,直到持有锁的线程释放锁。悲观锁的特点是对并发访问持保守态度,认为会有其他线程来竞争共享资源,因此在访问共享资源之前会先获取锁。
非公平锁: 在早期的 Java 版本中,默认实现的 synchronized 是非公平锁,也就是说,线程获取锁的顺序并不一定按照它们请求锁的顺序来进行,而是允许“插队”,即已经在等待队列中的线程可能被后来请求锁的线程抢占。
Synchronized & volatile
1,关键字volatile是线程同步的轻量级实现,所以volatile的性能略胜于synchronized,并且volatile只能修饰变量,而synchronized可以修饰方法、代码块等
2,多线程访问volatile不会发生阻塞,而synchronized会出现阻塞
3,volatile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和共有内存中的数据做同步。
4,关键字volatile解决的是变量在多个线程之间的可见性;而synchronized关键字解决的是多个线程之间访问资源的同步性
日夜颠倒头发少 ,单纯好骗恋爱脑 ,会背九九乘法表 ,下雨只会往家跑 ,搭讪只会说你好 ---- 2050781802@qq.com