Java学习(第十日)

锁对象  Lock

ReentrantLock(fair);  fair值默认为false,是非公平锁;fair值为true,是公平锁

加锁  lock.lock();

尝试加锁  lock.tryLock();成功加锁返回true,加锁失败返回false

解锁 lock.unlock

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class EasyThreadB {
    //锁对象 Lock
    //ReentrantLock可重入锁
    Lock lock = new ReentrantLock(true);//创建锁对象  true公平锁
    public void method(){
        //lock.lock();//加锁
        //lock.tryLock//尝试加锁  加锁成功  true  加锁失败  false
        if (lock.tryLock()) {
            System.out.println(Thread.currentThread().getName()+"进入方法");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+"结束方法");
            lock.unlock();//解锁
        } else{
            System.out.println("未成功加锁----去执行别的代码");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            method();
        }
    }
    public static void main(String[] args) {
        Runnable run=new EasyThreadB()::method;
        Thread a = new Thread(run);
        Thread b = new Thread(run);
        a.start();
        b.start();
    }
}

锁容器  ReentrantReadWriteLock读写锁

rrwl.readLock();  读锁允许多个线程同时读取共享资源

rrwl.writeLock();  写锁要求独占访问

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class EasyThreadC {
    //锁容器  ReentrantReadWriteLock
    public static ReentrantReadWriteLock rrwl = new ReentrantReadWriteLock();
    public static ReentrantLock rl = new ReentrantLock();
    public static void method(){
        System.out.println(Thread.currentThread().getName()+"进入方法");
        Lock lock = rrwl.readLock();
        lock.lock();
        System.out.println(Thread.currentThread().getName()+"加锁成功---读锁");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName()+"方法结束");
        lock.unlock();
    }
    public static void methodWrite(){
        System.out.println(Thread.currentThread().getName()+"进入方法");
        Lock lock = rrwl.writeLock();
        lock.lock();
        System.out.println(Thread.currentThread().getName()+"加锁成功---写锁");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName()+"方法结束");
        lock.unlock();

    }
    public static void main(String[] args) {
        Runnable run=EasyThreadC::method;
        Runnable runWrite=EasyThreadC::methodWrite;
        Thread thread=new Thread(run);
        thread.start();
        Thread thread1=new Thread(run);
        thread1.start();
        Thread thread2=new Thread(runWrite);
        thread2.start();
        Thread thread3=new Thread(runWrite);
        thread3.start();
        System.out.println("main线程结束-----");
    }
}

wait方法

OBJ.wait();  让执行到该代码的线程进入到等待状态(等待池)

OBJ.notify();  唤醒一条被该锁对象wait的线程                                                                        OBJ.notifyAll();  唤醒全部被锁对象wait的线程

wait和sleep的区别
wait是Object中定义的方法,可以有锁对象调用,让执行到该代码的线程进入到等待状态
sleep是Thread类中定义的静态方法,可以让执行到该行代码的线程进入等待状态
区别:1、sleep需要传入一个毫秒数,到达时间后自动唤醒
       wait不能自动唤醒,必须调用notify/notifyAll方法唤醒
           2、sleep方法保持锁状态进入等待状态
       wait方法会解除锁状态,其他线程可以进入运行

public class EasyTheadD {
    public static final Object OBJ = new Object();
    public static void method(){
        System.out.println(Thread.currentThread().getName()+"进入方法");
        synchronized (OBJ){
            OBJ.notify();//唤醒一条被该锁对象wait的线程
            OBJ.notifyAll();//唤醒全部被锁对象wait的线程
            System.out.println(Thread.currentThread().getName()+"进入同步代码块");
            try {
                try {
                    System.out.println(Thread.currentThread().getName()+"进入等待状态");
                    OBJ.wait();//让执行到该代码的线程进入到等待状态(等待池)
                    System.out.println(Thread.currentThread().getName()+"重新运行");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+"结束同步代码块");
            OBJ.notify();
        }
    }
    public static void main(String[] args) {
        Runnable run = EasyTheadD::method;
        Thread a = new Thread(run);
        a.start();
        Thread b = new Thread(run);
        b.start();
        Thread c = new Thread(run);
        c.start();
        Thread d = new Thread(run);
        d.start();
    }
}

死锁

概念

        Java中的死锁是指多个线程同时占用一些共享资源且彼此相互等待,从而导致所有的线程都被阻塞,不能继续执行程序的情况。这就好比在一个十字路口,没有交警也没有红绿灯指挥通行,所有的车辆都占据道路且互相等待对方让出路权,此时就很容易造成道路堵死,这其实就是道路的“死锁”。

死锁案例
public class DeadLock {
    //定义2两个锁定的对象
    private Object lock1 = new Object();
    private Object lock2 = new Object();
    public void method1(){
        //锁定对象1
        synchronized (lock1){
            System.out.println("Method 1:获取对象lock1的锁");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //锁定对象2
            synchronized (lock2){
                System.out.println("Method 1:获取对象lock2的锁");
            }
        }
    }
    public void method2(){
        //锁定对象1
        synchronized (lock2){
            System.out.println("Method 2:获取对象lock2的锁");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //锁定对象2
            synchronized (lock1){
                System.out.println("Method 2:获取对象lock1的锁");
            }
        }
    }

    public static void main(String[] args) {
        final DeadLock deadLock = new DeadLock();
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                deadLock.method1();
            }
        });
        Thread t2 = new Thread(new Runnable() {
            public void run() {
                deadLock.method2();
            }
        });
        //开启线程
        t1.start();
        t2.start();
    }
}

在上面的案例中,定义了两个方法method1和method2,这两个方法分别占用了lock1和lock2两个锁,并且在执行过程中会互相等待对方的锁,从而形成了死锁。如果我们运行该程序,就会看到两个线程互相等待对方释放自己占用的锁,这最终会导致所有的线程都被阻塞。

产生条件

互斥条件:多个线程需同时访问一个共享资源,但每次只能有一个线程访问该资源;

请求和保持条件:一个线程在持有一个资源的同时,还想请求另一个资源;

不可剥夺条件:已经分配的资源不能被其他线程剥夺;

循环(环路)等待条件:多个线程形成了一个循环等待资源的链路,例如线程A等待线程B释放自己所占用的资源,线程B等待线程C释放自己所占用的资源,而线程C又等待线程A释放自己所占用的资源。

解决办法

避免使用多个锁;

尽可能减少同步代码块的长度;

尝试改变锁的获取顺序,避免线程之间形成循环等待;

使用定时锁,当等待时间超过一定的时间值后就自动释放锁。

案例优化
public class DeadLock {
    //定义2两个锁定的对象
    private Object lock1 = new Object();
    private Object lock2 = new Object();
    public void method1(){
        //锁定对象1
        synchronized (lock1){
            System.out.println("Method 1:获取对象lock1的锁");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //锁定对象2
            synchronized (lock2){
                System.out.println("Method 1:获取对象lock2的锁");
            }
        }
    }
    public void method2(){
        //锁定对象1
        synchronized (lock1){
            System.out.println("Method 2:获取对象lock2的锁");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //锁定对象2
            synchronized (lock2){
                System.out.println("Method 2:获取对象lock1的锁");
            }
        }
    }

    public static void main(String[] args) {
        final DeadLock deadLock = new DeadLock();
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                deadLock.method1();
            }
        });
        Thread t2 = new Thread(new Runnable() {
            public void run() {
                deadLock.method2();
            }
        });
        //开启线程
        t1.start();
        t2.start();
    }
}

         上面的这个案例与之前的案例代码几乎一样,但与之不同的是,本案例中的方法method1和method2,都是先占用lock1锁,再占用lock2锁,这样就避免了死锁的发生,因为这两个方法占用锁的顺序是一致的。所以我们在编写多线程代码时,需要特别注意线程死锁的问题,避免影响程序的正常执行。

线程池  为了让线程达到重用的效果

线程池完成线程的创建、管理和销毁工作

线程池七个参数

ThreadPoolExecutor(核心线程数,最大线程数 ,空闲线程存活时间 ,空闲线程存活时间单位 , 工作队列(阻塞队列),线程工厂 , 回绝策略)

四种回绝策略

AbortPolicy(默认)    放弃该任务并会抛出一个异常RejectedExecutionException
CallerRunsPolicy         调用者执行,让传递任务的线程执行
DiscardOldestPolicy    放弃队列中时间最长的任务,不会抛出异常
DiscardPolicy              直接放弃新的任务,不会抛出异常

线程池的工作原理

任务放置在工作队列中
1>判断池中是否有空闲的线程,如果有让该线程执行任务
2>如果池中没有空闲的线程,判断线程数量是否达到核心线程数
3>如果没有达到,创建新的线程执行任务,直到填满核心线程数,如果已经达到,优先在队列中储存,直到队列填满
4>工作队列填满后再添加新的任务,判断是否达到最大线程数;如果没有,创建新的线程执行任务,直到填满最大线程数
5>已经填满最大线程数,队列也已经填满。没有空闲的线程,就执行回绝策略
如果线程池中的线程达到(超过)核心线程数,超出的数量会根据存活时间,进行销毁,直到数量达到核心线程数。如果线程的数量少于核心线程数,不会销毁

java中内置的线程池对象

Executors.newCachedThreadPool();可以根据工作任务来创建线程,如果没有空闲的线程就创建新的线程  线程存活时间60秒

Executors.newFixedThreadPool(10);设定最大线程数量的线程池

Executors.newScheduledThreadPool(10);提供定时运行的处理方案

Executors.newSingleThreadExecutor();创建一个具有单个线程的线程池  保证任务队列完全按照顺序执行

线程任务RunnableCallable

Runnable可以使用tpe.execute(run);和tpe.execute(run);调用

Callable只能使用tpe.submit(call);调用

f.cancel(true);中断线程
tpe.shutdown();关闭线程池对象
import java.util.concurrent.*;

public class EasyExecutors {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        BlockingQueue qu=new ArrayBlockingQueue(12);
        ThreadPoolExecutor tpe = new ThreadPoolExecutor(5,10,10,
                TimeUnit.SECONDS,qu,Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        //线程任务  Runnable  Callable
        Runnable run = EasyExecutors::method;
        tpe.execute(run);
        Callable<String> call= EasyExecutors::methodCall;
        Future<String> f=tpe.submit(call);
        //f.cancel(true);中断线程方式
        //tpe.submit(run);
        System.out.println(f.get());//会等待线程执行完毕
        //关闭线程池对象
        tpe.shutdown();
    }
    public static void method(){
        System.out.println(Thread.currentThread().getName()+"执行代码");
    }
    public static String methodCall() throws InterruptedException {
        System.out.println(Thread.currentThread().getName()+"执行代码call");
        Thread.sleep(2000);
        return "callResult";
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值