JAVA并发编程代码使用的相关知识点梳理

本文介绍了JAVA并发编程的相关知识点,包括创建和运行线程的方法,如直接使用Thread、Runnable配合Thread、FutureTask配合Thread。详细讲解了线程的常用方法如sleep、yield、interrupt,以及守护线程、synchronized的使用。此外,还讨论了变量的线程安全、线程池的创建和配置、ReentrantLock的特点,以及线程安全类和线程池的拒绝策略等。
摘要由CSDN通过智能技术生成

创建和运行线程

方法一:直接使用Thread

// 构造方法的参数是给线程指定名字,,推荐给线程起个名字
Thread t1 = new Thread("t1") {
 @Override
 // run 方法内实现了要执行的任务
 public void run() {
 log.debug("hello");
 }
};
t1.start();

方法二:使用Runnable配合Thread

把【线程】和【任务】(要执行的代码)分开,Thread 代表线程,Runnable 可运行的任务(线程要执行的代码)

// 创建任务对象
Runnable task2 = new Runnable() {
 @Override
 public void run() {
 log.debug("hello");
 }
};
// 参数1 是任务对象; 参数2 是线程名字,推荐给线程起个名字
Thread t2 = new Thread(task2, "t2");
t2.start();

方法三:FutureTask配合Thread

FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 实现多线程的第三种方法可以返回数据
        FutureTask futureTask = new FutureTask<>(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                log.debug("多线程任务");
                Thread.sleep(100);
                return 100;
            }
        });
        // 主线程阻塞,同步等待 task 执行完毕的结果
        new Thread(futureTask,"我的名字").start();
        log.debug("主线程");
        log.debug("{}",futureTask.get());
    }

注意:get()方法会阻塞线程,等待任务返回结果

常用方法

sleep与yield

sleep

  1. 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)
  2. 其它线程可以使用 interrupt 方法打断正在睡眠的线程,那么被打断的线程这时就会抛出 InterruptedException异常【注意:这里打断的是正在休眠的线程,而不是其它状态的线程】
  3. 睡眠结束后的线程未必会立刻得到执行(需要分配到cpu时间片)
  4. 建议用 TimeUnit 的 sleep() 代替 Thread 的 sleep()来获得更好的可读性

yield

  1. 调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程
  2. 具体的实现依赖于操作系统的任务调度器(就是可能没有其它的线程正在执行,虽然调用了yield方法,但是也没有用)

interrupt方法

  1. 打断sleep的线程,会清空打断状态
  2. 打断正常运行的线程,不会清空打断状态,正常运行被打断后,可由线程自己通过打断状态判断后续操作
  3. 打断park进程,不会清空打断状态

守护线程

默认情况下,java进程需要等待所有的线程结束后才会停止,但是有一种特殊的线程,叫做守护线程,在其他线程全部结束的时候即使守护线程还未结束代码未执行完java进程也会停止。普通线程t1可以调用t1.setDeamon(true); 方法变成守护线程

垃圾回收器线程就是一种守护线程

Tomcat 中的 Acceptor 和 Poller 线程都是守护线程,所以 Tomcat 接收到 shutdown 命令后,不会等待它们处理完当前请求

synchronized

临界区

  1. 一个程序运行多线程本身是没有问题的
  2. 问题出现在多个线程共享资源的时候(读写)
  3. 先定义一个叫做临界区的概念:一段代码内如果存在对共享资源的多线程读写操作,那么称这段代码为临界区

竞态条件

多个线程在临界区执行,由于代码指令的执行不确定而导致的结果问题,称为竞态条件

synchronized实现阻塞式解决

synchronized(对象) // 线程1获得锁, 那么线程2的状态是(blocked)
{
 临界区
}

保证了临界区代码的原子性

synchronized加在方法上

    class Test{
        public synchronized void test() {

        }
    }
    //等价于
    class Test{
        public void test() {
            synchronized(this) {

            }
        }
    }
//------------------------------------------------------------------------------------------------
    class Test{
        public synchronized static void test() {
        }
    }
   // 等价于
    class Test{
        public static void test() {
            synchronized(Test.class) {

            }
        }
    }
  • 分为类锁和对象锁
  • 使用对象锁的情况,只有使用同一实例的线程才会受锁的影响
  • 类锁是所有线程共享的锁,所以同一时刻,只能有一个线程使用加了锁的方法或方法体,不管是不是同一个实例。
  • 静态变量和类信息一样也是存在方法区的并且整个 JVM 只有一份,所以加在静态变量上可以达到类锁的目的
  • 类锁和对象锁不互相影响

park&unpark

与Object的wait&notify相比:

  • wait,notify必须配合Object Monitor一起使用,而park、unpark不必
  • park&unpark是以线程为单位来阻塞和唤醒线程,比notify的随机唤醒精确
  • park&unpark可以先unpark

变量的线程安全

成员变量和静态变量的线程安全

  • 如果没有变量没有在线程间共享,那么变量是安全的
  • 如果变量在线程间共享
  1. 如果只有读操作,则线程安全
  2. 如果有读写操作,则这段代码是临界区,需要考虑线程安全

局部变量的线程安全

线程安全的情况

局部变量被初始化为基本数据类型,此时是安全的

每个线程调用 test1() 方法时局部变量 i,会在每个线程的栈帧内存中被创建多份,因此不存在共享

线程不安全的情况

如果局部变量引用的对象逃离方法的范围,那么要考虑线程安全

public class Test15 {
    public static void main(String[] args) {
        UnsafeTest unsafeTest = new UnsafeTest();
        for (int i =0;i<100;i++){
            new Thread(()->{
                unsafeTest.method1();
            },"线程"+i).start();
        }
    }
}
class UnsafeTest{
    ArrayList<String> arrayList = new ArrayList<>();
    public void method1(){
        for (int i = 0; i < 100; i++) {
            method2();
            method3();
        }
    }
    private void method2() {
        arrayList.add("1");
    }
    private void method3() {
        arrayList.remove(0);
    }
}

method2和method3中的arryList前都省略了th

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值