物理CPU、物理核、逻辑核
1.CPU
物理CPU是相对于虚拟CPU而言的概念,指实际存在的CPU处理器,安装在PC主板或服务器上。
2.物理核
CPU中包含的物理内核(核心)个数。
3.逻辑核
所谓的4核8线程,4核指的是物理核心。用Intel的超线程技术(HT)将物理核虚拟而成的逻辑处理单元,
现在大部分的主机的CPU都在使用HT技术,用一个物理核模拟两个虚拟核,即每个核两个线程,总数为8线程。
------------------------------------
为了给电脑更高的性能,一个cup中集成了多个内核,这样电脑的性能就成倍的提升。随着科技的发展,我们
发现每个内核的性能也变的十分强大,于是一个内核又被分成两个线程。但是,我们要注意的是,一个cpu分
成多个内核,这是物理的分隔,拆开cpu是可以看到的;但是一个内核被分成两个线程是一种超线程技术,也
就是串代码,操作系统会认为一个线程也是一个内核,有点欺骗操作系统的感觉。

1.串行、并行、并发
1.串行
多个任务,执行时一个执行完再执行另一个。
2.并发
多个线程在单个核心运行,同一时间一个线程运行,系统不停切换线程,看起来像同时运行,实际上是线程不停切换。
cpu为线程分配时间片,时间片非常短(毫秒级别),cpu不停的切换线程执行,在切换前会保存上一个任务
的状态,以便下次切换回这个任务时,可以再加载这 个任务的状态,让我们感觉是多个程序同时运行的
3.并行
每个线程分配给独立的核心,线程同时运行。
注意:
1、单CPU中进程只能是并发,多CPU计算机中进程可以并行。
2、单CPU单核中线程只能并发,单CPU多核中线程可以并行。
3、无论是并发还是并行,使用者来看,看到的是多进程,多线程。
2、死锁
1.死锁
两个或两个以上的线程在执行过程中,由于竞争资源而造成的一种阻塞的现象。若无外力作用,它们都将无法
推进下去,出现相互等待的状态时成为死锁。
例如:
如果线程A锁住了记录1并等待记录2,而线程B锁住了记录2并等待记录1,这样两个线程就发生了死锁现象。
package org.example;
public class DeathLock {
private static final Object O_A = new Object();
private static final Object O_B = new Object();
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
synchronized (O_A){
try {
Thread.sleep(10L);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (O_B){
System.out.println("A获取到B资源");
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (O_B){
synchronized (O_A){
System.out.println("B获取到A资源");
}
}
}
}).start();
}
}
2、进程和线程
1.进程
是系统进行分配和管理资源的基本单位。
在计算机中,我们把一个任务称为一个进程,浏览器就是一个进程,视频播放器是另一个进程,类似的,音乐播放器和Word都是进程。
进程和线程的关系就是:一个进程可以包含一个或多个线程,但至少会有一个线程。
列如:你打开某个软件,系统会为这个进程分配属于它独立的地址空间、内存、硬盘等资源并且会
建立数据库表和维护代码段、堆栈段和数据段。
2.线程
线程是进程中的一个执行单元,是程序执行的最小单位。是CPU调度和分派的基本单位,是比进程更小的独立
运行的基本单位。线程也被称为轻量级进程。
注意:
进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、
堆栈段和数据段
同一进程下的线程共享全局变量、静态变量等数据
线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间
多进程与多线程优缺点:
缺点:
创建进程比创建线程开销大,尤其是在Windows系统上;
进程间通信比线程间通信要慢,因为线程间通信就是读写同一个变量,速度很快。
优点:
多进程稳定性比多线程高,因为在多进程的情况下,一个进程崩溃不会影响其他进程,而在多线程的情况
下,任何一个线程崩溃会直接导致整个进程崩溃。
3、线程的几种状态
原文出处:https://www.jianshu.com/p/ec94ed32895f
public enum State {
//线程刚创建
NEW,
//这个状态的线程,其正在JVM中执行,但是这个"执行",不一定是真的在运行,也有可能是在等待CPU资源。
RUNNABLE(就绪 | 可运行),
//线程处于阻塞状态,等待监视锁,可以重新进行同步代码块中执行
BLOCKED(阻塞),
//等待状态
//Object.wait()
//Thread.join()
//LockSupport.park()
//WAITING
WAITING(无限期地等待),
//Thread.sleep(long)
//Object.wait(long)
//Thread.join(long)
//LockSupport.parkNanos()
//LockSupport.parkUntil()
//等待是有一定时效的,即可以理解为WAITING状态等待的时间是永久的,即必须等到某个条件符合
//才能继续往下走,否则线程不会被唤醒。但是TIMED_WAITING,等待一段时间之后,会唤醒线程去
//重新获取锁。
TIMED_WAITING,
//线程执行完毕,已经退出
TERMINATED;
}

4、创建线程
1.继承Thread
public class MyThread extends Thread{
@Override
public void run() {
System.out.println("继承Thread");
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
2.实现Runnable接口
public class MyThread2 implements Runnable{
@Override
public void run() {
System.out.println("实现Runnable接口");
}
public static void main(String[] args) {
Thread thread = new Thread(new MyThread2());
thread.start();
}
}
3.匿名内部类
public class MyThread3 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类");
}
}).start();
}
}
4.Lambda表达式
public class MyThread4 {
public static void main(String[] args) {
new Thread(()->{
System.out.println("Lambda表达式");
}).start();
}
}
5.线程池
public class ThreadPool {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程池创建");
}
});
}
}
5.Callable接口
public static void main(String[] args) {
Callable callable = new Callable() {
@Override
public Object call() throws Exception {
System.out.println("callable接口创建");
return "callable接口创建";
}
};
FutureTask ft = new FutureTask(callable);
new Thread(ft).start();
try {
System.out.println(ft.get());
} catch (Exception e) {
}
}
5、线程挂起和唤醒
1.线程挂起:
线程的挂起操作实质上就是使线程进入“非可执行”状态下,在这个状态下CPU不会分给线程时间片,
进入这个状态可以用来暂停一个线程的运行。在线程挂起后,可以通过重新唤醒线程来使之恢复运行
2.什么时候适合使用挂起线程?
等待某些未就绪的资源,当资源就绪后唤醒被挂起的线程
wait() 暂停执行、放弃已经获得的锁、进入等待状态
notify() 随机唤醒一个在等待锁的线程
notifyAll() 唤醒所有在等待锁的线程,自行抢占cpu资源
注意:
李某人:线程等待wait(),释放锁,被重新唤起还需要拿到锁资源,然后从等待的位置继续向下执行。
廖大神:调用wait()方法后,线程进入等待状态,wait()方法不会返回,直到将来某个时刻,线程从等待状
态被其他线程唤醒后,wait()方法才会返回,然后,继续执行下一条语句
wait()方法必须在当前获取的锁对象上调用,比如获取的是this锁,因此调用this.wait()。
wait()方法调用时,会释放线程获得的锁。
public class WaitNotifyNotifyAll {
private static final Object o = new Object();
static int i = 0;
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
synchronized (o){
if(i < 10){
try {
System.out.println("线程被挂起,释放锁");
o.wait();
System.out.println("线程被唤醒---继续执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程正常执行:"+i);
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (o){
while (i <= 10){
System.out.println("线程2----》"+i);
if(i==10){
/*
随机唤醒单个等待线程
执行完当前线程,释放锁
之后,被唤醒的线程也需要
重新拿到锁,然后从
等待的位置,向下执行。
*/
o.notify();
}
i++;
}
}
}
}).start();
}
}
线程被挂起,释放锁
线程2----》0
线程2----》1
线程2----》2
线程2----》3
线程2----》4
线程2----》5
线程2----》6
线程2----》7
线程2----》8
线程2----》9
线程2----》10
线程被唤醒---继续执行
线程正常执行:11
6、线程中断
1.stop() 废弃方法,开发中不要使用。因为一调用,线程就立刻停止,此时有可能引发相应的线程安全性问题
2.Thread.interrupt方法
interrupt()方法仅仅向"某个"线程发出了“中断请求”,至于"某个"线程是否能立刻响应,要看具体代码。
3.自行定义一个标志,用来判断是否继续执行
1.stop()
public class MyThreadStop extends Thread{
private int i = 0;
private int j = 0;
@Override
public void run() {
i++;
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
j++;
}
public void printlnIJ(){
System.out.println("i-------->"+i);
System.out.println("j-------->"+j);
}
public static void main(String[] args) throws InterruptedException {
MyThreadStop myThreadStop = new MyThreadStop();
myThreadStop.start();
Thread.sleep(1000L);
myThreadStop.printlnIJ();
}
}
--------------------------------------
i-------->1
j-------->0
2.interrupt()
public class InterruptDemo implements Runnable{
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()){
System.out.println("线程运行"+Thread.currentThread().isInterrupted());
}
if(Thread.currentThread().isInterrupted()){
System.out.println("线程结束");
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new InterruptDemo());
thread.start();
Thread.sleep(1000L);
thread.interrupt();
}
}
3.自定义
public class CustomeInterruptDemo implements Runnable{
public static volatile boolean flag = false;
@Override
public void run() {
while (!flag) {
System.out.println("线程运行-----------");
}
if(flag){
System.out.println("线程结束");
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new a());
thread.start();
Thread.sleep(1000L);
flag = true;
}
}
*、volatile
线程间共享的变量用关键字volatile。
在Java虚拟机中,变量的值保存在主内存中,但是,当线程访问变量时,它会先获取一个副本,并保存在自己
的工作内存中。如果线程修改了变量的值,虚拟机会在某个时刻把修改后的值回写到主内存,但是,这个时间
是不确定的!
┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
Main Memory
│ │
┌───────┐┌───────┐┌───────┐
│ │ var A ││ var B ││ var C │ │
└───────┘└───────┘└───────┘
│ │ ▲ │ ▲ │
─ ─ ─│─│─ ─ ─ ─ ─ ─ ─ ─│─│─ ─ ─
│ │ │ │
┌ ─ ─ ┼ ┼ ─ ─ ┐ ┌ ─ ─ ┼ ┼ ─ ─ ┐
▼ │ ▼ │
│ ┌───────┐ │ │ ┌───────┐ │
│ var A │ │ var C │
│ └───────┘ │ │ └───────┘ │
Thread 1 Thread 2
└ ─ ─ ─ ─ ─ ─ ┘ └ ─ ─ ─ ─ ─ ─ ┘
这会导致如果一个线程更新了某个变量,另一个线程读取的值可能还是更新前的。例如,主内存的变量a =
true,线程1执行a = false时,它在此刻仅仅是把变量a的副本变成了false,主内存的变量a还是true,
在JVM把修改后的a回写到主内存之前,其他线程读取到的a的值仍然是true,这就造成了多线程之间共享的变量不一致。
因此,volatile关键字的目的是告诉虚拟机:
每次访问变量时,总是获取主内存的最新值;
每次修改变量后,立刻回写到主内存。
volatile关键字解决的是可见性问题:当一个线程修改了某个共享变量的值,其他线程能够立刻看到修改
后的值。
7、线程优先级
1.线程优先级
线程的优先级告诉程序该线程的重要程度有多大。如果有大量线程都被堵塞,都在等候运行,程序会尽可能地
先运行优先级高的那个线程。 但是,这并不表示优先级较低的线程不会运行。若线程的优先级较低,只不过表示
它被准许运行的机会小一些而已。
线程的优先级设置可以为1-10的任一数值,Thread类中定义了三个线程优先级,分别是:
MIN_PRIORITY(1)、NORM_PRIORITY(5)、MAX_PRIORITY(10),一般情况下推荐使用这几个常量,
不要自行设置数值。
public class ThreadPriority implements Runnable{
@Override
public void run() {
while (true){
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args) {
Thread thread1 = new Thread(new ThreadPriority(), "线程1");
Thread thread2 = new Thread(new ThreadPriority(), "线程2");
thread1.setPriority(Thread.MIN_PRIORITY);
thread2.setPriority(Thread.MAX_PRIORITY);
thread1.start();
thread2.start();
}
}
8、守护线程
1.线程分类
用户线程:用户自定义的线程
守护线程:任何一个守护线程都是整个程序中所有用户线程的守护者,只要有活着的用户线程,
守护线程就活着。当JVM实例中最后一个非守护线程结束时,守护线程也随JVM一起退出
守护线程的用处:jvm垃圾清理线程
注意: 尽量少使用守护线程,因其不可控
不要在守护线程里去进行读写操作、执行计算逻辑
public class MyThread4 implements Runnable{
@Override
public void run() {
while (true) {
System.out.println("我是守护线程");
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new MyThread4());
thread.setDaemon(true); //设置为守护线程
thread.start();
//2秒手main线程结束 守护线程也自动结束
Thread.sleep(2000L);
}
}
9、原子性
1.线程不安全
多线程并发访问时,得不到正确的结果
2.原子性
一个操作或多个操作,要么全部执行并且执行过程中不会被任何因素打断,要么就不执行。
解决多线程不安全,需确保操作为原子性即可。
3.如何把非原子性操作变成原子性
volatile关键字仅仅保证可见性,并不保证原子性
synchronized关键字,使得操作具有原子性
10、synchronized
1.内置锁
每个对象都可以作为实现同步的锁,这些锁就成为内置锁。线程进入同步代码块或同步方式时会自动
获取该锁,在退出的同步代码块或同步方式时会自动释放该锁。
2.互斥锁
内置锁就是一个互斥锁,也就是说在运行的时候只有一个线程能获取到该锁。当线程A去获取线程B持有的内置
锁时,线程A会处于一个阻塞或等待状态,直到线程B释放该锁。
2.synchronized修饰
修饰普通方法:锁住是这个类的实例(this 对象)
修饰静态方法:任何一个类都有一个由JVM自动创建的Class实例,因此,对static方法添加synchronized,锁住的是该类的Class实例
修饰代码块: 锁住一个对象(实例) synchronized (lock) 即synchronized后面括号里的内容
注意:
1.在使用synchronized的时候,不必担心抛出异常。因为无论是否有异常,都会在synchronized结束处正确
释放锁。
2.JVM只保证同一个锁在任意时刻只能被一个线程获取,但两个不同的锁在同一时刻可以被两个线程分别获取
1.synchronized修饰普通方式
public class SynchronizedDemo {
public synchronized void sync() throws InterruptedException {
System.out.println(Thread.currentThread().getName());
Thread.sleep(3000L);
}
public static void main(String[] args) {
/**
* synchronized 修饰普通方式 锁是类的实例(对象)
* 此刻new了2个实例 相当于有2把不同的锁
* 所以这两个线程互不影响
*/
final SynchronizedDemo demo1 = new SynchronizedDemo();
final SynchronizedDemo demo2 = new SynchronizedDemo();
new Thread(new Runnable() {
@Override
public void run() {
try {
demo1.sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
demo2.sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
--------------------
-------瞬间输出-----
Thread-0
Thread-1
2.synchronized修饰普通方式
public class SynchronizedDemo {
public synchronized void sync() throws InterruptedException {
System.out.println(Thread.currentThread().getName());
Thread.sleep(3000L);
}
public static void main(String[] args) {
/**
* synchronized 修饰普通方式 锁是类的实例(对象)
* new 了一个实例 2个线程公用一把锁(互斥锁)
*/
final SynchronizedDemo demo1 = new SynchronizedDemo();
new Thread(new Runnable() {
@Override
public void run() {
try {
demo1.sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
demo1.sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
-------------------------------------------
----先输出了Thread-0等待3秒后输出Thread-1----
Thread-0
Thread-1
3.synchronized 修饰静态方式
public class SynchronizedDemo {
public static synchronized void sync() throws InterruptedException {
System.out.println(Thread.currentThread().getName());
Thread.sleep(3000L);
}
public static void main(String[] args) {
/**
* synchronized 修饰静态方式 锁是类的本身
* new 了2个实例 2个线程公用一把锁(互斥锁)
*/
final SynchronizedDemo demo1 = new SynchronizedDemo();
final SynchronizedDemo demo2 = new SynchronizedDemo();
new Thread(new Runnable() {
@Override
public void run() {
try {
demo1.sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
demo2.sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
-------------------------------------------
----先输出了Thread-0等待3秒后输出Thread-1----
Thread-0
Thread-1
4.synchronized 修饰代码块
public class SynchronizedDemo {
public void sync() throws InterruptedException {
synchronized(this){
System.out.println(Thread.currentThread().getName());
Thread.sleep(3000L);
}
}
public static void main(String[] args) {
/**
* synchronized 修饰代码块 锁是对象(实例)
与synchronized修饰普通方式相同(区别在于对象是静态还是普通)
* new了1个实例 2个线程共用一把锁
*/
final SynchronizedDemo demo1 = new SynchronizedDemo();
new Thread(new Runnable() {
@Override
public void run() {
try {
demo1.sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
demo1.sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
-------------------------------------------
----先输出了Thread-0等待3秒后输出Thread-1----
Thread-0
Thread-1
11、单例
1.饿汉式
线程安全,在类加载的时候,就已经进行实例化,无论之后用不用到。如果该类比较占内存,之后又没用到,
就白白浪费了资源。
2.懒汉式
普通懒汉式线程不安全,在需要使用的时候才实例化
1.饿汉式
public class HungryDemo {
private HungryDemo(){}
private static HungryDemo hungryDemo = new HungryDemo();
public static HungryDemo getInstance(){
return hungryDemo;
}
}
2.饿汉式--线程不安全
public class LazyDemo {
private LazyDemo(){}
private static LazyDemo lazyDemo = null;
public static LazyDemo getInstance(){
if(null == lazyDemo){
lazyDemo = new LazyDemo();
}
return lazyDemo;
}
}
3.懒汉式--线程安全
public class LazyDemo {
private LazyDemo(){}
private static volatile LazyDemo lazyDemo = null;
public static LazyDemo getInstance(){
if(lazyDemo == null){
synchronized (LazyDemo.class){
if(null == lazyDemo){
lazyDemo = new LazyDemo();
}
}
}
return lazyDemo;
}
public static void main(String[] args) {
while (true){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(LazyDemo.getInstance());
}
}).start();
}
}
}
12、锁的分类
自旋锁: 线程状态及上下文切换消耗系统资源,当访问共享资源的时间短,频繁上下文切换不值得。
jvm实现,使线程在没获得锁的时候,不被挂起,转而执行空循环,循环几次之后,如果还没能获得锁,则被挂起
阻塞锁:阻塞锁改变了线程的运行状态,让线程进入阻塞状态进行等待,当获得相应的信号(唤醒或者时
间)时,才可以进入线程的准备就绪状态,转为就绪状态的所有线程,通过竞争,进入运行状态
重入锁: JVM允许同一个线程重复获取同一个锁,这种能被同一个线程反复获取的锁
读写锁: 两把锁,读锁跟写锁,写写互斥、读写互斥、读读共享
互斥锁: 上厕所,进门之后就把门关了,不让其他人进来
悲观锁: 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁
乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。
公平锁:大家都老老实实排队,对大家而言都很公平
非公平锁:一部分人排着队,但是新来的可能插队
偏向锁:偏向锁使用了一种等到竞争出现才释放锁的机制,所以当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁
独占锁:独占锁模式下,每次只能有一个线程能持有锁
共享锁:允许多个线程同时获取锁,并发访问共享资源
注意:
对同一个线程,能否在获取到锁以后继续获取同一个锁?
答案是肯定的。JVM允许同一个线程重复获取同一个锁,这种能被同一个线程反复获取的锁,就叫做可重入锁
13、Lock
1.lock与synchronized的区别
lock:
获取锁与释放锁的过程,都需要程序员手动的控制。lock采用的乐观锁机制。
synchronized:
托管给JVM,采用的是悲观锁机制。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockDemo {
private static int num = 0;
private static Lock lock = new ReentrantLock();
public static void numAdd(){
lock.lock();
num++;
lock.unlock();
}
public static void main(String[] args) throws InterruptedException {
for(int i = 0;i < 10; i++){
new Thread(new Runnable() {
@Override
public void run() {
for(int j = 0; j < 100; j++){
try {
Thread.sleep(5L);
} catch (InterruptedException e) {
e.printStackTrace();
}
LockDemo.numAdd();
}
}
}).start();
}
Thread.sleep(5000L);
System.out.println(num);
}
}
可以使用tryLock()尝试获取锁。如果程序获取不到锁,就可以做一些额外处理,而不是无限等待下去。
public class Zj {
private static final Lock lock = new ReentrantLock();
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
try{
System.out.println("开始执行》》》》》1");
Thread.sleep(5000L);
System.out.println("结束执行>>>>>>>>>2");
}catch (Exception e){
e.printStackTrace();
}
finally {
lock.unlock();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
//等待2秒后 没有获取到锁 将返回false 做一些额为处理 该线程就结束了
if(lock.tryLock(2, TimeUnit.SECONDS)){
try{
System.out.println("获取到了锁");
}finally {
lock.unlock();
}
}else{
System.out.println("等待2秒后,没获取锁先做一些额外处理");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
14、Condition
1、使用Condition对象来实现wait和notify的功能。
2、使用Condition时,引用的Condition对象必须从Lock实例的newCondition()返回,这样才能获得一个绑
定了Lock实例的Condition实例。
Condition提供的await()、signal()、signalAll()原理和synchronized锁对象的wait()、notify()、notifyAll()是一致的,并且其行为也是一样的:
await()会释放当前锁,进入等待状态;
signal()会唤醒某个等待线程;
signalAll()会唤醒所有等待线程;
唤醒线程从await()返回后需要重新获得锁。
public class Zj {
private static final Lock lock = new ReentrantLock();
private static final Condition condition = lock.newCondition();
private static int i = 0;
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
try{
System.out.println("线程1====》start");
condition.await();
System.out.println("线程1=====》end");
System.out.println("线程1=====》"+i);
}catch (Exception e){
e.printStackTrace();
}
finally {
lock.unlock();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
try {
while (i <10){
if(i == 6){
condition.signal();
}
i++;
System.out.println("线程2==>"+i);
}
}finally {
lock.unlock();
}
}
}).start();
}
}