程序中的错误统称为异常。
可以举例说明,一个公共变量a,三个线程,一个线程修改了a的值,其他两个线程可能看不到a变化后的值,这就是内存可见性问题。
原因是:为了提高处理速度,每个线程都会在 CPU 中维护一份共享变量的本地缓存,而线程对共享变量的所有操作都会在自己的本地缓存中进行。如果线程 A 更改了一个共享变量,线程 B 有可能看不到线程 A 的修改
解决方法:
使用volatile关键字:
将变量声明为volatile,这会告诉Java虚拟机确保所有线程都能看到最新的值。
当一个线程修改了volatile变量的值,这个变化会立即被写入主内存,并且其他线程在读取该变量时会从主内存中获取最新值。
使用synchronized关键字:
使用synchronized块来对读写操作进行同步,确保同一时间只有一个线程能够访问共享变量。
当一个线程获取了锁并修改了共享变量后,其他线程必须等待该线程释放锁才能访问该变量,这样可以确保可见性。
使用java.util.concurrent包中的工具类:
Java提供了一些并发工具类,如AtomicInteger、CountDownLatch、CyclicBarrier等,它们可以用来处理多线程可见性问题,而无需手动编写同步代码。
这些工具类提供了原子操作和同步机制,可以确保对共享变量的修改对其他线程可见。
volatile 是 Java 中的关键字,用于修饰变量。它的主要作用是确保多线程环境下的可见性和有序性,这意味着当一个线程修改了 volatile 变量的值时,其他线程可以立即看到这个修改。
volatile关键字的作用:
适用场景: volatile 适用于一些简单的标志位或状态标识的操作,例如线程之间的信号通知。它不适合复杂的操作,如累加操作。
(1)接口的默认方法和静态方法:之前接口只能够做方法的声明,没有实现,Java8以后允许接口有一个默认的实现,必须使用default修饰符标记;
default void test(){}
static void test2(){}
(2)Lambda表达式:Lambda最直观的是将代码变得整洁。
(3)函数式接口:
(4)方法引用:是用来直接访问类或者实例中的方法或者构造方法,这样代码的可读性会更高一些。
就是使用::
来调用类中的方法:
在这里插入代码片
(5)Stream流:它允许你以声明式的方式处理数据集合。
select.stream().parallel()
(6)Optional:为了解决空指针异常。并且让代码更加简洁,使用它我们不需要显式的进行空指针检测。
Optional + lambda实现比较字符串,并找到最长的字符串
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public class OptionalLambdaExample {
public static void main(String[] args) {
List<String> stringList = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
Optional<String> longestString = stringList.stream()
.reduce((s1, s2) -> s1.length() > s2.length() ? s1 : s2);
longestString.ifPresent(s -> System.out.println("最长的字符串是: " + s));
}
}
(7)Date/Time
(8)重复注解
(9)扩展注解的支持
(10)Base64
(11)JavaFx
synchronized 是Java的关键字,直接内置在Java语言中。您可以使用synchronized关键字来实现同步块或同步方法。
Lock 是一个接口,它在java.util.concurrent.locks包中定义。Java提供了多种Lock的实现,包括ReentrantLock、ReadWriteLock等。您可以使用这些Lock实现来管理同步。
Lock 提供了更多的灵活性。您可以使用Lock接口的不同实现来满足特定的同步需求。例如,ReentrantLock支持可重入锁,而ReadWriteLock支持读写锁。
synchronized更简单,但在某些情况下可能不够灵活,例如无法轻松实现尝试锁定、定时锁定等功能。
synchronized关键字用于锁定整个方法或代码块,这可能会导致性能问题,特别是在高并发情况下。
Lock允许您更细粒度地控制锁的范围,可以只锁定需要同步的关键部分,从而提高并发性能。
使用synchronized关键字时,如果发生异常,锁将自动释放。
使用Lock时,您需要在try-finally块中手动释放锁,以确保在发生异常时锁定资源得到释放。
Lock接口提供了Condition对象,可以用于实现条件等待和通知机制。这使得线程能够更灵活地等待某些条件的发生,而不需要一直忙等。
总之,synchronized适合简单的同步需求,而Lock适用于更复杂、灵活的同步需求。选择哪种同步方式取决于您的具体需求和性能考虑。在Java并发编程中,通常建议优先使用Lock接口,因为它提供了更多的控制和灵活性。但要注意,Lock使用起来相对复杂,需要小心处理异常和锁定资源的释放。
sleep() 用于线程的暂时休眠,不释放锁,通常用于时间延迟。
wait() 用于线程之间的等待和唤醒机制,会释放锁,必须在synchronized块中使用。
sleep()是Thread类的方法,而wait()是Object类的方法。