前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java多线程基础

Java多线程基础

原创
作者头像
橘子又加强了么
发布2023-09-25 09:38:11
2260
发布2023-09-25 09:38:11
举报

创建线程的几个方式

  • 继承Thread类继承 Thread 类: 可以创建一个类,继承自 Thread 类,并重写其 run() 方法来定义线程的任务逻辑。然后,通过创建该类的实例并调用 start() 方法来启动线程
  • 实现Runnable接口
  • 使用匿名内部类
  • 使用callable接口和future接口
代码语言:txt
复制
class MyThread extends Thread {
    public void run() {
        // 线程的任务逻辑
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
    }
}

实现 Runnable 接口

可以创建一个类,实现 Runnable 接口,并实现其 run() 方法来定义线程的任务逻辑。然后,通过创建 Thread 类的实例,将实现了 Runnable 接口的对象作为参数传递,并调用 start() 方法来启动线程。

代码语言:txt
复制
class MyRunnable implements Runnable {
    public void run() {
        // 线程的任务逻辑
    }
}

public class Main {
    public static void main(String[] args) {
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
    }
}

使用匿名内部类

可以使用匿名内部类的方式来创建线程。这种方式适用于只需在某个地方定义一个简单的线程任务逻辑,而不需要在其他地方重复使用该线程

代码语言:txt
复制
public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            public void run() {
                // 线程的任务逻辑
            }
        });
        thread.start();
    }
}

使用 Callable 和 Future:

在 Java 5 及以后的版本中,引入了 Callable 接口和 Future 接口,允许线程执行有返回值的任务。可以创建一个类,实现 Callable 接口,并实现其 call() 方法来定义线程的任务逻辑。然后,通过创建 ExecutorService 线程池,并提交 Callable 任务来启动线程,并通过 Future 对象获取线程的返回值。

代码语言:txt
复制
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

class MyCallable implements Callable<String> {
    public String call() {
        // 线程的任务逻辑
        return "Hello, World!";
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(new MyCallable());
        String result = future.get();
        System.out.println(result);
        executor.shutdown();
    }
}

创建线程池:

在Java中,可以使用 Executors 工具类来创建线程池

在下面的示例中:首先使用 Executors.newFixedThreadPool() 方法创建了一个固定大小为3的线程池。然后,我们通过循环提交了5个任务给线程池执行,这些任务由实现了 Runnable 接口的 MyTask 类表示。每个任务都会打印出一个简单的消息和任务ID。最后,我们调用 executor.shutdown() 方法关闭线程池

代码语言:txt
复制
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExample {
    public static void main(String[] args) {
        // 创建线程池,使用 Executors.newFixedThreadPool() 方法创建一个固定大小的线程池,大小为3
        ExecutorService executor = Executors.newFixedThreadPool(3);

        // 提交任务给线程池执行
        for (int i = 0; i < 5; i++) {
            Runnable task = new MyTask(i);
            executor.execute(task);
        }

        // 关闭线程池
        executor.shutdown();
    }
}

class MyTask implements Runnable {
    private int taskId;

    public MyTask(int taskId) {
        this.taskId = taskId;
    }

    public void run() {
        System.out.println("Task " + taskId + " is running.");
        // 线程的任务逻辑
    }
}

说一下线程的几种状态

  1. 创建状态:在生成线程对象,并没有调用该对象的start方法,这是线程处于创建状态。
  2. 就绪状态:当调用了线程对象的start方法之后,该线程就进入了就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态
  3. 运行状态:线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行run函数当中的代码
  4. 阻塞状态:线程正在运行的时候,被暂停,通常是为了等待某个时间的发生(比如说某项资源就绪)之后再继续运行。sleep,suspend,wait等方法都可以导致线程阻塞
  5. 死亡状态。如果一个线程的run方法执行结束或者调用stop方法后,该线程就会死亡。对于已经死亡的线程,无法再使用start方法令其进入就绪

线程之间如何通信,Java实现的方式是什么

Java中线程之间的通信方式包括以下几种:

  1. 共享变量(Shared Variables):多个线程可以通过读写共享变量来进行通信和数据共享。通过对共享变量进行适当的同步操作(如synchronized关键字、volatile关键字等),可以确保线程之间对共享变量的读写是线程安全的。
  2. 管程(Monitor):管程是一种高级的线程通信机制,用于解决共享资源的并发访问问题。通过在共享资源上定义进入、退出、等待和通知等操作,线程可以按照规定的方式进行协作和同步。Java中的wait()notify()notifyAll()方法以及synchronized关键字提供了管程的实现。
  3. 条件变量(Condition):条件变量用于在线程之间进行等待和通知的机制。线程可以通过条件变量等待某个条件的满足,并在条件满足时进行通知。Java中的Condition接口及其实现类提供了条件变量的实现,通常与ReentrantLock结合使用。
  4. 阻塞队列(Blocking Queue):阻塞队列是一种特殊的队列,支持线程在队列为空或队列已满时进行阻塞等待和唤醒。线程可以通过阻塞队列进行数据的安全传递和同步。Java中的BlockingQueue接口及其实现类提供了阻塞队列的功能。
  5. 信号量(Semaphore):信号量是一种计数器,用于控制同时访问某个资源的线程数量。线程在访问资源之前要获取信号量的许可,许可数量有限。当一个线程完成访问后,释放许可,其他线程可以获取许可并访问资源。Java中的Semaphore类提供了信号量的实现。

这些线程通信方式提供了不同的机制和语义,用于解决多线程编程中的同步、互斥和协作问题。根据具体的场景和需求,选择适当的线程通信方式可以确保线程之间的正确协作和数据共享。

HashMap是线程安全的吗?如果不是该如何解决?

HashMap 不是线程安全的,它是非线程安全的数据结构。当多个线程同时访问和修改 HashMap 时,可能会导致不一致的状态、数据丢失或无限循环等问题。

如果需要在多线程环境中使用类似 HashMap 的数据结构,并且要求线程安全,可以考虑以下几种解决方案:

  1. 使用 ConcurrentHashMap:ConcurrentHashMap 是 Java 提供的线程安全的哈希表实现。它可以用作 HashMap 的替代品,并且提供了更好的并发性能。ConcurrentHashMap 使用分段锁(Segment)的方式来实现线程安全,不同的线程可以同时访问不同的分段,从而提高并发性能。
  2. 使用 Collections.synchronizedMap:可以使用 Collections.synchronizedMap 方法将 HashMap 包装成线程安全的 Map。该方法返回一个线程安全的 Map,对于所有对 Map 的操作都会进行同步,从而保证线程安全。需要注意的是,虽然使用了同步机制,但在多线程高并发的场景下,性能可能不如 ConcurrentHashMap。
  3. 使用并发工具类:可以使用 java.util.concurrent 包中提供的并发工具类,例如 ConcurrentHashMapCopyOnWriteHashMapConcurrentSkipListMap 等,它们提供了针对不同需求的线程安全的数据结构。
  4. 使用锁机制:可以显式地使用锁机制,如使用 ReentrantLocksynchronized 关键字来保护对 HashMap 的访问。通过在访问 HashMap 之前获取锁,并在操作完成后释放锁,可以确保线程安全。但需要注意锁的粒度和正确的加锁顺序,以避免死锁和性能问题。

选择适当的解决方案取决于具体的需求和场景。如果只是读取操作较多,而写入操作较少的情况,可以考虑使用读写分离的数据结构,如 ConcurrentHashMapCopyOnWriteHashMap。如果需要更高的并发性能,可以使用分段锁的方式,如 ConcurrentHashMap。对于特定的线程安全需求,也可以使用其他并发工具类来满足要求。

如何实现多线程中的同步

在 Java 中,可以使用以下几种方式来实现多线程的同步:

  1. synchronized 关键字:使用 synchronized 关键字可以对代码块或方法进行同步,确保同一时间只有一个线程可以执行被同步的代码块或方法。通过使用 synchronized 关键字,可以防止多个线程同时访问共享资源,从而避免数据竞争和不一致性。
代码语言:java
复制

public synchronized void synchronizedMethod() {

代码语言:txt
复制
   // 同步的代码块或方法

}

代码语言:txt
复制
  1. ReentrantLock 类:ReentrantLock 是 Java.util.concurrent 包中提供的一个锁实现。与 synchronized 不同,ReentrantLock 提供了更灵活的锁定机制,例如可重入性、公平性等。使用 ReentrantLock 可以在代码中显式地获取锁,并在使用完后释放锁。
代码语言:java
复制

import java.util.concurrent.locks.ReentrantLock;

private ReentrantLock lock = new ReentrantLock();

public void synchronizedMethod() {

代码语言:txt
复制
   lock.lock();
代码语言:txt
复制
   try {
代码语言:txt
复制
       // 同步的代码块
代码语言:txt
复制
   } finally {
代码语言:txt
复制
       lock.unlock();
代码语言:txt
复制
   }

}

代码语言:txt
复制
  1. volatile 关键字:volatile 关键字用于确保多个线程之间对共享变量的可见性。当一个变量被声明为 volatile 时,它的值将立即被写入到主内存中,并且每次访问该变量时都会从主内存中读取最新的值,而不是从线程的本地缓存中读取。
代码语言:java
复制

private volatile int sharedVariable;

public void setSharedVariable(int value) {

代码语言:txt
复制
   sharedVariable = value;

}

public int getSharedVariable() {

代码语言:txt
复制
   return sharedVariable;

}

代码语言:txt
复制
  1. 使用线程安全的数据结构:Java 提供了一些线程安全的数据结构,如 ConcurrentHashMap、CopyOnWriteArrayList 等。这些数据结构内部实现了同步机制,可以在多线程环境下安全地访问和修改数据。
代码语言:java
复制

import java.util.concurrent.ConcurrentHashMap;

private ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

public void addToMap(String key, int value) {

代码语言:txt
复制
   map.put(key, value);

}

代码语言:txt
复制

使用Synchronized实现线程同步

如果你想使用synchronized关键字来实现多线程同步,可以对代码进行如下修改:

代码语言:java
复制
public class Counter {
    private int counter = 0;

    public synchronized void increment() {
        counter++;
    }

    public synchronized void decrement() {
        counter--;
    }

    public synchronized int getCounter() {
        return counter;
    }
}

在上述代码中,我们使用synchronized关键字修饰了increment()decrement()getCounter()方法。这将确保在任何时刻只有一个线程能够执行这些方法,从而避免了多线程并发访问时的数据竞争问题。

当一个线程进入synchronized方法时,它将获取该方法所属对象的锁。其他线程如果尝试进入同一个对象上的synchronized方法,将被阻塞,直到锁被释放。

需要注意的是,使用synchronized关键字会对整个方法进行加锁,这可能会导致一些性能上的开销,特别是在读多写少的场景下。相比之下,读写锁(ReadWriteLock)可以提供更好的并发性能,允许多个线程同时读取共享数据。

综上所述,synchronized关键字适用于简单的同步需求,但对于复杂的多线程同步场景,读写锁等更高级的同步机制可能更适合。

使用volatile实现线程同步

在之前的多线程代码中,如果你想使用volatile关键字来实现同步,可以对计数器变量进行如下修改:

代码语言:java
复制
public class Counter {
    private volatile int counter = 0;

    public void increment() {
        counter++;
    }

    public void decrement() {
        counter--;
    }

    public int getCounter() {
        return counter;
    }
}

在上述代码中,我们在计数器变量 counter 前面添加了 volatile 关键字。volatile 关键字的作用是确保对该变量的读取和写入操作都是可见的,即保证了线程之间的可见性。

使用 volatile 关键字可以解决某些特定情况下的可见性问题, volatile 关键字是通过在主存中加入内存屏障来达到禁止指令重排序,来保证有序性

需要注意的是,volatile关键字 不能保证多线程中的原子性,volatile是轻量级的同步机制,不想synchronized锁一样粒度太大。

volatile关键字不能保证原子性的原因:

简单的说,修改volatile变量分为四步:

1)读取volatile变量到local

2)修改变量值

3)local值写回

4)插入内存屏障,即lock指令,让其他线程可见

这样就很容易看出来,前三步都是不安全的,取值和写回之间,不能保证没有其他线程修改。原子性需要锁来保证。

综上所述,volatile 关键字适用于某些简单的同步需求,但对于复杂的多线程同步场景,需要使用更高级的同步机制,如锁或原子类。

使用ReentrantLock实现线程同步

代码语言:java
复制
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
为什么需要加锁才能实现线程的同步
在没有锁的情况下,两个线程可以同时访问和修改 counter 变量,这可能导致竞态条件(race condition)和数据不一致性问题。
例如,考虑以下情况:
线程1读取 counter 的值为 0。
此时,线程2也读取 counter 的值为 0。
线程1递增 counter,将其值设置为 1。
线程2递减 counter,将其值设置为 -1。
最终,counter 的值不是 0,而是 -1。
这是因为在没有锁的情况下,两个线程可以同时执行递增和递减操作,它们之间的执行顺序是不确定的,从而导致了竞态条件和不一致的结果。
通过使用锁(lock),我们确保在访问和修改 counter 变量之前,只有一个线程能够获得锁并进入临界区。这样,其他线程就会被阻塞,直到锁被释放。
通过对临界区进行加锁,我们保证了线程对 counter 的访问是互斥的,每次只有一个线程执行递增或递减操作,从而避免了竞态条件和不一致性结果的发生。
 */

public class ThreadExample_Lock {
    private int counter = 0;
    private Lock lock = new ReentrantLock(); // 创建一个 ReentrantLock 实例作为锁对象

    public static void main(String[] args) {
        ThreadExample_Lock example = new ThreadExample_Lock();
        example.runThreads();
    }

    public void runThreads() {
        Thread thread1 = new Thread(new IncrementRunnable()); // 创建一个线程并指定 Runnable 对象
        Thread thread2 = new Thread(new DecrementRunnable()); // 创建另一个线程并指定 Runnable 对象

        thread1.start(); // 启动线程1
        thread2.start(); // 启动线程2

        try {
            thread1.join(); // 等待线程1执行完毕
            thread2.join(); // 等待线程2执行完毕,这段代码中,
            // 通过调用 thread1.join() 和 thread2.join(),主线程(运行 runThreads() 方法的线程)会等待 thread1 和 thread2 执行完成,然后再继续执行后面的代码
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Counter: " + counter); // 输出最终的计数器值
    }

    // 递增线程的 Runnable 实现类
    class IncrementRunnable implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10000; i++) {
                //lock.lock(); // 获取锁
                try {
                    counter++; // 递增计数器
                } finally {
                    //lock.unlock(); // 释放锁
                }
            }
        }
    }

    // 递减线程的 Runnable 实现类
    class DecrementRunnable implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10000; i++) {
                lock.lock(); // 获取锁
                try {
                    counter--; // 递减计数器
                } finally {
                    lock.unlock(); // 释放锁
                }
            }
        }
    }
}

使用锁机制,最后输出为0,如果任一个线程没有加锁,输出将随机

使用读写锁实现同步

代码语言:java
复制
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ThreadExample_ReadWriteLock {
    public static void main(String[] args) {
        Counter counter = new Counter();

        Thread incrementThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        Thread decrementThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.decrement();
            }
        });

        incrementThread.start();
        decrementThread.start();

        try {
            incrementThread.join();
            decrementThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Counter value: " + counter.getCounter());
    }
}

class Counter {
    private int counter = 0;
    private ReadWriteLock lock = new ReentrantReadWriteLock();

    public void increment() {
        lock.writeLock().lock(); // 获取写锁
        try {
            counter++;
        } finally {
            lock.writeLock().unlock(); // 释放写锁
        }
    }

    public void decrement() {
        lock.writeLock().lock(); // 获取写锁
        try {
            counter--;
        } finally {
            lock.writeLock().unlock(); // 释放写锁
        }
    }

    public int getCounter() {
        lock.readLock().lock(); // 获取读锁
        try {
            return counter;
        } finally {
            lock.readLock().unlock(); // 释放读锁
        }
    }
}

死锁的定义

死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。

死锁产生的必要条件:

死锁是多线程并发编程中常见的问题,它发生在两个或多个线程相互等待对方持有的资源时。死锁产生的四个必要条件是:

  1. 互斥条件(Mutual Exclusion): 至少有一个资源被标记为独占资源,即一次只能被一个线程占用。当一个线程获取了该资源后,其他线程无法同时获取该资源。
  2. 请求与保持条件(Hold and Wait): 线程至少要持有一个资源,并且在等待其他资源的时候继续保持已经持有的资源。换句话说,线程在等待其他资源时不会释放已经占有的资源。
  3. 不可剥夺条件(No Preemption): 资源只能由占有它的线程进行释放,其他线程无法将其强行剥夺。只有线程自愿释放资源后,其他线程才能获取该资源。
  4. 循环等待条件(Circular Wait): 多个线程之间形成一个循环等待资源的链,每个线程都在等待下一个线程所持有的资源。 这四个条件是死锁产生的必要条件,当它们同时满足时,死锁就可能发生。要解决死锁问题,需要打破其中任何一个条件,使得死锁无法发生。

例如,通过合理的资源分配顺序、避免资源持有和等待、引入抢占机制、以及避免循环等待等方法,可以预防死锁的发生。此外,使用同步工具和锁时,确保在获取资源时及时释放,避免一次性持有多个资源,也有助于减少死锁的概率。

同步和异步

同步:按照顺序执行,必须等待前一个操作完成后才能进行下一个操作,阻塞等待。

异步:不需要等待操作完成,可以继续进行其他操作,结果将在后续通知或回调中得到。

在编程中,同步和异步也是类似的概念。同步操作会阻塞当前线程,直到操作完成,而异步操作可以在操作进行的同时,继续执行其他任务,通过回调、通知或轮询等方式获取操作结果。

ThreadLocal

如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变 量副本,而不会对其他线程产生影响。

即每个线程运行的都是一个副本,也就是说存钱和取钱是两个账户,只是名字相同而已,两个线程间的count没有关系。所以就会发生上面的效果。

ThreadLocal与同步机制

a.ThreadLocal与同步机制都是为了解决多线程中相同变量的访问冲突问题

b.前者采用以”空间换时间”的方法,后者采用以”时间换空间”的方式

ThreadLocal并不能替代同步机制,两者面向的问题领域不同。

1:同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式;

2:而threadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享变量,这样当然不需要对多个线程进行同步了。

java.util.concurrent

java.util.concurrent 包提供了一系列用于并发编程的机制和工具,旨在简化多线程编程和处理并发操作。这些机制和工具包含了各种线程安全的集合类、执行器(Executor)框架、同步器、锁、原子类、并发工具等,以帮助开发者更方便地处理并发编程任务。

以下是 java.util.concurrent 包中一些常用的机制和工具:

  1. 线程池(ThreadPool): ThreadPoolExecutor 是一个线程池的实现,它管理着一个线程池并提供了一种将任务提交给线程执行的方式。通过使用线程池,可以避免线程的频繁创建和销毁,提高系统的性能和资源利用率。
  2. 同步器(Synchronizers): CountDownLatchCyclicBarrierSemaphore 等同步器类可以帮助协调多个线程之间的执行顺序和同步操作,以及控制并发线程的访问权限。
  3. 阻塞队列(BlockingQueue): BlockingQueue 是一个线程安全的队列实现,提供了在多线程环境下安全地进行数据交换的机制。它支持阻塞式的添加和获取元素操作,可以用于实现生产者-消费者模式等场景。
  4. 锁(Lock): ReentrantLock 是一个可重入锁的实现,比内置的synchronized关键字提供了更多的灵活性和功能。它支持公平性选择、条件变量等特性,可以解决更复杂的同步需求。
  5. 原子类(Atomic): java.util.concurrent.atomic 包中提供了一系列原子类,如 AtomicIntegerAtomicLong 等,用于在多线程环境下进行原子操作,避免了显式的锁机制,提供了更高效的原子性操作。
  6. 并发集合类(Concurrent Collections): java.util.concurrent 包中提供了一些线程安全的集合类,如 ConcurrentHashMapCopyOnWriteArrayList 等,用于在多线程环境下进行安全的集合操作。
  7. 并发工具(Concurrent Utilities): java.util.concurrent 包中还提供了一些其他的并发工具,如 CountDownLatchCyclicBarrierSemaphore 等,用于解决并发编程中的常见问题。

线程间的通信方式

线程池

Java线程池(Java Thread Pool)是一种管理和复用线程的机制,它能够有效地管理线程的创建、执行和回收,提高线程的利用率和性能。

线程池中包含一组预先创建的线程,这些线程可以被重复使用来执行任务,而不需要每次都创建和销毁线程。当需要执行任务时,可以将任务提交给线程池,线程池会自动选择一个空闲的线程来执行任务。任务执行完毕后,线程会返回线程池中,等待下一个任务的到来。

Java线程池的主要优点包括:

  1. 减少线程创建和销毁的开销:线程的创建和销毁是一项开销较大的操作,使用线程池可以避免频繁地创建和销毁线程,提高系统的性能和响应速度。
  2. 控制并发线程的数量:线程池可以限制系统中并发线程的数量,防止因过多的线程导致系统资源耗尽,从而提高系统的稳定性。
  3. 提高任务执行的速度和效率:线程池可以根据系统的负载情况,合理地分配和调度线程,优化任务的执行顺序和速度,提高任务的并发性和效率。
  4. 提供线程的管理和监控机制:线程池提供了统一的接口和机制来管理和监控线程的状态、执行情况和异常信息,方便线程的管理和故障排查。

Java线程池的实现主要依赖于 java.util.concurrent 包中的 Executor 接口及其实现类,如 ThreadPoolExecutor。通过使用这些类,可以方便地创建和管理线程池,设置线程池的参数和策略,以满足不同的需求。

线程池是多线程编程中一种重要的并发控制机制,可以在高并发的场景下提供良好的性能和可伸缩性。它被广泛应用于各种Java应用程序、服务器端开发和并发框架中。

JVM关于同步的规定:
  • 线程解锁前,必须把共享变量的值刷新回主内存
  • 线程加锁前,必须读取主内存的最新值到自己的工作内存
  • 加锁解锁是同一把锁

由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有的成为栈空间),工作内存是每个线程的私有数据区域,而java内存模型中规定所有变量都存储在主内存.主内存是贡献内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先概要将变量从主内存拷贝到自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,各个线程中的工作内存中存储着主内存的变量副本拷贝,因此不同的线程件无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成 期要访问过程如下图:

image
image

JMM三大特征

原子性,可见性,有序性

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 创建线程的几个方式
  • 说一下线程的几种状态
  • 线程之间如何通信,Java实现的方式是什么
  • HashMap是线程安全的吗?如果不是该如何解决?
  • 如何实现多线程中的同步
  • 使用Synchronized实现线程同步
  • 使用volatile实现线程同步
  • 使用ReentrantLock实现线程同步
  • 使用读写锁实现同步
  • 死锁的定义
  • 同步和异步
  • ThreadLocal
  • java.util.concurrent
  • 线程间的通信方式
  • 线程池
    • JVM关于同步的规定:
    • JMM三大特征
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档