Java多线程与并发-原理

1、获取的锁的分类:获取对象锁和获取类锁

获取对象锁的两种方法:一是同步代码块(synchronized(this),sybchronized(类实例对象)),锁是小括号()中的实例对象;二是同步非静态方法(synchronized method),锁是当前对象的实例对象。

package com.javabasic.bytecode.thread;

import java.text.SimpleDateFormat;
import java.util.Date;

public class SyncThread implements Runnable {
    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        if (threadName.startsWith("A")){
            async();
        } else if (threadName.startsWith("B")){
            syncObjectBlock1();
        } else if (threadName.startsWith("C")){
            syncObjectMethod1();
        }
    }

    /*synchronized修饰非静态方法*/
    private synchronized void syncObjectMethod1() {
        System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
        try {
            System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1_Start:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1_End:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
        } catch (InterruptedException e){
            e.printStackTrace();
        }
    }

    /*方法中有synchronized(this|objrct){}同步代码块*/
    private void syncObjectBlock1() {
        System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
        synchronized (this){
            try {
                System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1_Start:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1_End:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            } catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }

    /*异步方法*/
    private void async() {
        try {
            System.out.println(Thread.currentThread().getName() + "_Async_Start:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + "_Async_End:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
        } catch (InterruptedException e){
            e.printStackTrace();
        }
    }


    public static void main(String[] args) {
        SyncThread syncThread = new SyncThread();
        Thread A_thread1 = new Thread(syncThread,"A_thread1");
        Thread A_thread2 = new Thread(syncThread,"A_thread2");
        Thread B_thread1 = new Thread(syncThread,"B_thread1");
        Thread B_thread2 = new Thread(syncThread,"B_thread2");
        Thread C_thread1 = new Thread(syncThread,"C_thread1");
        Thread C_thread2 = new Thread(syncThread,"C_thread2");
        A_thread1.start();
        A_thread2.start();
        B_thread1.start();
        B_thread2.start();
        C_thread1.start();;
        C_thread2.start();
    }

}

获取类锁的两种方法:一是同步代码块(synchronized(类.class)),锁是小括号()中的类对象(Class对象);二是同步静态方法(synchronized static method),锁是当前对象的类对象(Class对象)。

package com.javabasic.bytecode.thread;

import java.text.SimpleDateFormat;
import java.util.Date;

public class SyncThread implements Runnable {
    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        if (threadName.startsWith("A")){
            async();
        } else if (threadName.startsWith("D")){
            syncClassBlock1();
        } else if (threadName.startsWith("E")){
            syncClassMethod1();
        }
    }


    /*异步方法*/
    private void async() {
        try {
            System.out.println(Thread.currentThread().getName() + "_Async_Start:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + "_Async_End:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
        } catch (InterruptedException e){
            e.printStackTrace();
        }
    }

    private void syncClassBlock1() {
        System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
        synchronized (SyncThread.class){
            try {
                System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1_Start:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1_End:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            } catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }

    private synchronized static void syncClassMethod1() {
        System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
        try {
            System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1_Start:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1_End:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
        } catch (InterruptedException e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Thread A_thread1 = new Thread(new SyncThread(),"A_thread1");
        Thread A_thread2 = new Thread(new SyncThread(),"A_thread2");
        Thread D_thread1 = new Thread(new SyncThread(),"D_thread1");
        Thread D_thread2 = new Thread(new SyncThread(),"D_thread2");
        Thread E_thread1 = new Thread(new SyncThread(),"E_thread1");
        Thread E_thread2 = new Thread(new SyncThread(),"E_thread2");
        A_thread1.start();
        A_thread2.start();
        D_thread1.start();
        D_thread2.start();
        E_thread1.start();
        E_thread2.start();
    }

}

对象锁和类锁的总结:使用对象锁时,多个线程访问同一个对象,会产生竞争关系,但是多个线程访问同一个类实例化的不同对象,则不会发生竞争关系,即多个线程之间互不干扰;使用类锁时,多个线程访问同一对象或者是同一类实例化的不同对象时,都会发生竞争关系;同一个类中的类锁和对象锁不会相互干扰,不会有竞争关系。

2、自旋锁和自适应锁

自旋锁:

  • 在许多情况下,共享数据的锁定状态的持续时间较短,此时切换线程不值得
  • 在线程阻塞的时候,让线程执行忙循环等待锁的释放,不让出CPU
  • 缺点:若等待的锁被其他线程长时间占用,会带来许多性能上的开销

自适应自旋锁:

  • 自选的次数不再固定
  • 由上一次在同一个锁上的自旋时间和锁的拥有着的状态来决定

3、锁消除和锁粗化

锁消除是JVM更彻底的优化,JIT编译时,对上下文进行扫描,去除不可能存在竞争的锁;锁粗化时另一种极端,通过加锁的范围,避免反复加锁和解锁

4、synchronized的四种状态

无锁、偏向锁、轻量级锁、重量级锁

偏向锁:较少同一线程获取锁的代价。大多数情况下,所不存在多线程竞争关系,总是由同一线程多次获得。

轻量级锁:是由偏向锁升级来的,偏向锁运行在一个线程进入同步块的情况下,当第二个线程加入锁争用的时候,偏向锁就会升级为轻量级锁。适用于线程交替执行的同步块。若存在同一时间两个线程访问同一锁的情况,就会导致轻量级锁升级为重量级锁

                   

5、synchronized和ReentrantLock(再入锁)

ReentrantLock(再入锁):能够实现比synchronized更细粒度的控制,如控制fairness(锁的公平);调用lock()之后,必须调用unlock()来释放锁;性能未必比synchronized高,并且也是可重入的。

ReentrantLock公平性的设置 :ReentrantLock fairLock = new ReentrantLock(true);参数为true时,倾向与将锁赋予等待时间最久的线程;公平锁即获取锁的顺序按先后调用lock方法的顺序(慎用);非公平锁即抢占的顺序不一定,看运气;synchronized是非公平锁。

ReentrantLock将锁对象化:判断是否有线程,或者某个特定的线程在排队等待获取锁;带超时的获取锁的尝试;能感知有没有成功获取锁。

总结:synchronzied是关键字,ReentrantLock是类;ReentrantLock可以对对获取锁的时间进行设置,避免线程死锁;ReentrantLock可以获取各种锁的信息;ReentrantLock可以灵活的实现多路通知;锁的机制不同:sync操作的是Mark Word,ReentrantLock调用的是Unsafe类的park()方法。

6、Java线程池

为什么要使用线程池:降低资源消耗,提高线程的可管理性。

7、实现线程池——ThreadPoolExecutor类

ThreadPoolExecutorThreadPoolExecutor的构造函数:

  • corePoolSize:核心线程数量
  • maximumPoolSize:线程不够用时能够创建的最大线程数
  • workQueue:任务等待队列
  • keepAliveTime:线程池维护线程所允许的空闲时间,当线程池中的线程数量大于corePoolSize的时候,如果这是没有新的任务提交,核心线程外的线程不会立即被销毁而是会等待,知道等待的时间超过keepAliveTime才会被销毁
  • threadFactory:创建新线程
  • hander:线程池的饱和策略。如果阻塞队列满了,并且没有空闲的线程,这时如果继续提交任务,会通过hander所指定的策略来处理线程。

线程池的状态:

  • RUNNING:能够接受新提交的任务,并且也能处理阻塞队列中的任务
  • SHUTDOWN:不在接受新提交的任务,但可以处理存量任务
  • STOP:不在接受新提交的任务,也不处理存量任务
  • TIDYING:所有任务都已经终止
  • TERMINATED:terminated()方法执行完便会进入此状态(一般情况下terminated方法中什么操作都没有,只是线程池结束的一个标志)

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值