多线程

多线程

四种实现方式

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状态,

线程池执行流程

img

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

这里两个线程SyncThread1SyncThread2 持有同一个对象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

×

喜欢就点赞,疼爱就打赏

相册 说点什么 简历