多线程学习

发布于:2024-04-17 ⋅ 阅读:(28) ⋅ 点赞:(0)

1.同步代码块,同步方法

同步代码块

  • 代码块又称为初始化块,属于类中的成员[即是类的一部分],类似于方法,将逻辑语句分装在方法体中,通过{}包围起来。

  • 和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不通过对象或类显示调用,而是加载类时,或创建对象时隐形调用

public class MyThread extends Thread{
    static int ticket=0;
    static Lock lock=new ReentrantLock();
    @Override
    public void run(){
        while(true){
            lock.lock();
            try {
                if(ticket==50){
                    break;
                }else {
                    Thread.sleep(100);
                    ticket++;
                    System.out.println(Thread.currentThread().getName()+"   "+ticket);
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }finally {
                lock.unlock();
            }
        }
    }
}

为什么要用同步代码块(好处)
优点:

1.相当于另一种形式的构造器(对构造器的补充机制),可以做初始化的操作。
应用场景:假设在类中多个构造器中有重复语句,可以抽取到初始化代码块中,提高代码的重用性。
2.和设计模式中的"单例设计模型"有关(与static修饰符关联有奇效)。
注意:代码块与构造器的调用规则:代码块的执行是优先于构造器的。

同步方法

public class MyRunnable implements Runnable{
    int ticket=0;
    public String name;


    @Override
    public void run(){
        /*
        1.循环
        2.同步代码块(或者同步方法)
        3.判断共享数据是否到了末尾,如果到了末尾
        4.判断共享数据是否到了末尾,如果没到末尾
         */

        while(true){
          synchronized (this){
              if (method()) break;
          }
        }
    }

    private boolean method() {
        if(ticket==50){
            return true;
        }else{
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            ticket++;
            System.out.println(Thread.currentThread().getName()+"   "+ticket);
        }
        return false;
    }


}

2.等待唤醒机制

等待唤醒机制中的方法
等待唤醒机制就是用于解决线程通信的问题的,使用到的3个方法的含义如下:

① wait方法:线程不再活动,不再参与调度,进入wait set中(一个等待的集合中),因此不会浪费CPU资源,也不会去竞争锁,这时的线程状态为WAITING(等待状态)。处于WAITING状态的线程需要等待别的线程的通知(notify),然后才能从wait set中释放出来,重新进入调度队列(ready queue)
② notify方法:通知在wait set中的一个线程释放,等待时间越久的约先释放
③ notifyAll方法:释放所有在wait set中的全部线程

注意:
哪怕只通知了一个处于等待中的线程,被通知的线程也不能立即回复执行,因为它是在判断了是否含有锁对象发现没有后才中断的,所以他需要再次尝试获取锁(这个过程很可能会面临其他线程的竞争),成功后才能在最开始调用wait方法之后的地方恢复执行

package com.lc.test02;
 
import java.util.concurrent.TimeUnit;
 
/**
 * @author liuchao
 * @date 2023/4/8
 */
public class ThreadWaitOne {
    /**
     * 注意:必须使用同一把锁
     */
    static Object lock = new Object();
 
    public static void main(String[] args) {
 
        /**
         * 线程1
         */
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("进入" + Thread.currentThread().getName());
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                System.out.println(Thread.currentThread().getName() + "被唤醒");
            }
        }, "t1");
 
        t1.start();
 
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
 
        /**
         * 线程2
         */
        Thread t2 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("进入" + Thread.currentThread().getName());
                lock.notify();
                System.out.println("唤醒通知已发");
            }
        }, "t2");
 
        t2.start();
    }
}