Hz零基础学习多线程

物理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();
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值