天天看点

Java多线程之synchronized&volatile基础篇

一、什么是线程

  • 线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。

                              ——百度百科

二、实现线程的几种方式

  • 继承Thread类重写run()方法
public class ThreadDemo {
     public static void main(String[] args) {
 
         MyThread myThread = new MyThread();
         /*开启线程*/
         myThread.start();
     }
 }
 class MyThread extends Thread{
     @Override
     public void run() {
         // 写入自己的功能代码
     }
 }
           
  • 实现Runnable接口
public class RunnableDemo {

    public static void main(String[] args) {
        /*通过实现Runnable接口开启多线程*/
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
        /*通过匿名内部类实现*/
        new Thread(()-> {
            // 写入自己的功能代码
        }).start();
        
        new Thread(()-> System.out.println("线程4")).start();
        new Thread(()-> System.out.println("线程5")).start();
    }
}
class MyRunnable implements Runnable{
        @Override
        public void run() {
           // 写自己的功能代码
        }
}
           
  • 线程常用方法详解
  • Thread.currentThread()

    获取当前正在执行此代码的线程信息
public class MyThread {
    public static void main(String[] args) {
        /**
        * ()->{
        *    // 获取当前线程信息
        *   Thread thread = Thread.currentThread();
        *   System.out.println(thread.toString());
        *}
        * 此处相当于传了一个实现了Runnable接口类的对象
        * */
        new Thread(()->{
            Thread thread = Thread.currentThread();
            System.out.println(thread.toString());
        }).start();
    }
}
           
  • Thread.currentThread().isAlive()

    判断线程是否处于活动状态
public class MyThread {
    public static void main(String[] args) {
        new Thread(()->{
            System.out.println(Thread.currentThread().isAlive());
        }).start();
    }
}
           
  • Thread.sleep(5000);

    使当前线程进行休眠
public class MyThread {
    /*HelloWorld需要等待5s才能被打印*/
    public static void main(String[] args) {
        new Thread(()->{
            try {
                /*线程休眠*/
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("HelloWorld");
        }).start();
    }
}
           
  • wait()

    使当前线程释放锁
  • notify()

    唤醒其他在等待锁的线程(注意执行完

    notify()

    方法,等待锁的线程不能马上获取到锁,得等待调用

    notify()

    方法的线程执行完同步代码块或同步方法)

三、实现锁的几种方式

  • synchronized
    • synchronized

      锁定的是对象,而不是代码块,

      synchronized

      是可以重入锁,当程序出现异常时会释放锁
    public class MyThread {
          private static Object object = new Object();
          public static void main(String[] args) {
              /*此处锁的是object对象*/
              synchronized (object){
                  // 功能代码
              }
          }
          /*此处锁定的是当前对象*/
          public synchronized void m(){ }
          
          /*在没有new MyThread类对象时使用m1()方法,锁的是MyThread.class 对象*/
          public static synchronized void m1(){ }
      
          /*可重人锁,当线程获取到了执行demo1方法的对象锁,当执行到demo2()时也能获取到对象锁*/
          public synchronized void demo1(){
              // 功能代码
              demo2();
          }
          public synchronized void demo2(){ }
      
      }
               
    • synchronized底层原理(升级锁)
      • 第一个线程进入的时候是偏向锁,所谓偏向锁就是锁对象会记录获取了锁对象的当前线程的id,如果锁没有释放锁,这个时候有另一个线程需要进入,就会用线程id对比一下是不是当前线程如果不是则获取不到锁。
      • 当等待的线程变多就会升级为自旋锁(在用户态进行,在CPU中进行自旋)
      • 进一步升级为重量级锁–OS锁(在内核态进行,不占用CPU)
      • 拓展
        • 线程少,占用锁时间短采用自旋锁,线程多占用锁时间长用系统锁

四、volatile注意看代码注释

  • 防止指令重排序
public class Singleton {
     /*
     * 说明:
     *   Object o = new Object();
     *   此条语句不是原子性操作,并且是分三步进行的
     *   1、申请地址空间
     *   2、初始化
     *   3、将地址赋值给变量o
     *   由于JVM存在指令重排机制一次1,2,3不一定会按顺序执行,如果在高并发情况下可能会有线程拿到还未进行初始化的对象
     * */
     private volatile Singleton instance = null;
 
     private Singleton(){ }
 
     public Singleton getInstance(){
         if(instance == null) {
             synchronized (Singleton.class) {
                 if (instance == null) {
                 instance = new Singleton();
                 }
                 return instance;
             }
         }
         return instance;
     }
 }
           
  • 保证被修饰变量线程间的可见性,底层是通过CPU的缓存一致性协议(MESI)完成的
    public class MyThread extends Thread {
        /*此处不加关键字volatile线程thread1将一直在处在wile循环中,因为对主线程修改的flag不可见*/
        private static volatile boolean flag = false;
    
        public void run() {
            while (!flag);
        }
    
        public static void main(String[] args) throws Exception {
            Thread thread1 = new MyThread();
            thread1.start();
            Thread.sleep(2000);
            flag = true;
        }
    }