JUC编程-什么是JUC

1.什么是JUC

JUC:java原生并发包还有一些常用工具类

 

2.线程基础知识

并发、并行

并发:多个线程操作同一个资源,交替执行的过程!

并行:多个线程同时执行!只有在多核CPU下才能完成!

线程六种状态

新建(new) ,运行(RUNNABLE),阻塞(BLOCKED),等待(WAITING),延迟等待(TIMED_WAITING),终止(TERMINATED)

wait/Sleep 区别

1.类不同

        s.wait(1000);//object 类
​
        TimeUnit.SECONDS.sleep(1);//Thread 类

 

 

2.释放资源不同

sleep:抱着锁睡得,不会释放锁!wait 会释放锁!

3.使用范围不同

wait 和 notify 是一组,一般在线程通信的时候使用!

sleep 就是一个单独的方法,在那里都可以用

4.关于异常

sleep 需要捕获异常!

说明

两者都可以暂停线程的执行。

对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。

Wait 通常被用于线程间交互/通信,sleep 通常被用于暂停执行。

sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。

在调用sleep()方法的过程中,线程不会释放对象锁。

而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备,获取对象锁进入运行状态。线程不会自动苏醒

3、线程锁

synchronized 传统方式

java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。

java内置锁是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,知道线程B释放这个锁,如果B线程不释放这个锁,那么A线程将永远等待下去。

java的对象锁和类锁:java的对象锁和类锁在锁的概念上基本上和内置锁是一致的,但是,两个锁实际是有很大的区别的,对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。我们知道,类的对象实例可以有很多个,但是每个类只有一个class对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的(后面会在8锁现象里具体说明)

先看传统的synchronized写法:

/*
 *线程操作资源类,资源类是单独的
 */
public class Demo1 {
​
    public static void main(String[] args) {
          Ticket ticket = new Ticket();
​
        //线程操控资源类
        new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i <30 ; i++) {
                    ticket.saleTicket();
                }
            }
        },"A").start();
​
        new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i <30 ; i++) {
                    ticket.saleTicket();
                }
            }
        },"B").start();
​
​
        new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i <30 ; i++) {
                    ticket.saleTicket();
                }
            }
        },"C").start();
​
    }
​
}
 //资源类 假设我们在卖票
 class Ticket{
​
    private int num = 100;
​
    public synchronized void saleTicket(){
        if (num>0){
            System.out.println(Thread.currentThread().getName()
                    +"卖出第"+(num--)+"票,还剩"+num);
        }
    }
​
​
}

Lock锁

锁是用于通过多个线程控制对共享资源的访问的工具,通常锁提供对共享资源的独占访问,一次只能有一个线程可以获取锁,并且对共享资源的所有访问都要求首先获取锁。 但是,一些锁可能允许并发访问共享资源,如ReadWriteLock的读写锁。在Lock接口出现之前,Java程序是靠synchronized关键字实现锁功能的。JDK1.5之后并发包中新增了Lock接口以及相关实现类来实现锁功能。synchronized方法和语句的范围机制使得使用监视器锁更容易编程,并且有助于避免涉及锁的许多常见编程错误,但是有时您需要以更灵活的方式处理锁。

如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况: 1.获取锁的线程执行完了该代码块,然后线程释放对锁的占有 2.线程执行发生异常,此时JVM会让线程自动释放锁

那么如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能地等待,这多么影响程序执行效率。因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock就可以办到

写法:

public class Demo2 {
​
    public static void main(String[] args) {
        Ticket2 ticket = new Ticket2();
​
        new Thread(()->{
            for (int i = 0; i < 30; i++)ticket.saleTicket(); },"A").start();
​
        new Thread(()-> {
            for (int i = 0; i <30 ; i++) ticket.saleTicket(); },"B").start();
​
        new Thread(()->{
            for (int i = 0; i < 30; i++) ticket.saleTicket(); },"C").start();
    }
​
}
​
//资源类 假设我们在卖票
class Ticket2{
    // 使用Lock,它是一个对象
    // ReentrantLock 可重入锁:回家:大门 (卧室门,厕所门...)
    // ReentrantLock 默认是非公平锁!
    // 非公平锁: 不公平 (插队,后面的线程可以插队)
    // 公平锁: 公平(只能排队,后面的线程无法插队)
    // ReentrantLock(轻量级锁)也可以叫对象锁,可重入锁,互斥锁
    private Lock lock = new ReentrantLock();
​
   private int num = 100;
​
   public void saleTicket(){
       lock.lock();
​
       try {
           if (num>0){
               System.out.println(Thread.currentThread().getName()
                       +"卖出第"+(num--)+"票,还剩"+num);
           }
       } catch (Exception e) {
           e.printStackTrace();
       } finally {
           lock.unlock();
       }
   }
​
​
}

注意lock.lock()不能放在try里面

说明:因为在 try-finally 外加锁的话,如果因为发生异常导致加锁失败,try-finally 块中的代码不会执行。相反,如果在 try{ } 代码块中加锁失败,finally 中的代码无论如何都会执行,但是由于当前线程加锁失败并没有持有 lock 对象锁,所以程序会抛出异常。

Synchronized 和 Lock 区别

1、Synchronized 是一个关键字、Lock 是一个对象

2、Synchronized 无法尝试获取锁,Lock 可以尝试获取锁,判断;

3、Synchronized 会自动释放锁(a线程执行完毕,b如果异常了,也会释放锁),lock锁是手动释放锁!如果你不释放就会死锁。

4、Synchronized (线程A(获得锁,如果阻塞),线程B(等待,一直等待);)lock,可以尝试获取锁,失败了之后就放弃

5、Synchronized 一定是非公平的,但是 Lock 锁可以是公平的,通过参数设置;

6、代码量特别大的时候,我们一般使用Lock实现精准控制,Synchronized 适合代码量比较小的同步问题;

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值