7月25日JavaSE学习笔记

今日重点:   

线程锁对象   wait  notify   notifyAll     wait和sleep区别

Lock   ReentrantLock    true 公平锁     方法lock   tryLock    unlock  ReentrantReadWriteLock       

线程池的定义   Callable    线程池对象执行Runnable 和Callable     Future    get                

线程池的七个参数    线程池的四种回绝策略    四种内置线程池(缓存线程池,定时运行线程池,边界线程池,单例线程池)  线程池的工作原理        


补充:继承Thread实现线程,每个线程数据是独立的;实现Runnable接口,传入同一个run,run是共享的,run里的数据也是共享的。


锁对象  Lock

创建锁对象

        对于所有锁对象,初始化时默认是非公平锁,传入true是公平锁

Lock lock=new ReentrantLock();

ReentrantReadWriteLock rrwl=new ReentrantReadWriteLock(true);
	
ReentrantLock rtl=new ReentrantLock(true);

       

使用Lock

        被lock(或tryLock)和unLock方法包裹的代码块相对于同步代码块

        相比之下tryLock方法更好一些,该方法具有返回值,能反馈是否加锁成功,便于设置处理加锁成功和失败的情况。

        lock.lock();//加锁   配合lock.unlock();//解锁,不解就会进入死锁

public void method() {
		
		    lock.lock();//加锁
		
			System.out.println(Thread.currentThread().getName()+"进入方法");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"进入方法");
			lock.unlock();//解锁,不解就会进入死锁
		
		}

       

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

     public void method() {
		
		//lock.lock();//加锁
		if(lock.tryLock()) {//尝试加锁,成功返回true失败返回false
			System.out.println(Thread.currentThread().getName()+"进入方法");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"进入方法");
			lock.unlock();//解锁,不解就会进入死锁
		}else {
			System.out.println("未成功加锁---执行其他任务等会回来执行");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			method();
		}

        

锁容器ReentrantReadWriteLock

        锁容器,里面有读锁和写锁  同一个锁容器的两个锁是互斥的,和定义在哪个方法或者代码块无关

        注意:

                1.没有线程加任何锁可以加写锁,加后没有线程可以再加任何锁

                2.读锁是共享锁,加读锁其他线程也可以加读锁

        加读锁演示:

package com.easy25;

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

public class EasyC {
	
	
	//lock与unlock之间的代码块是同步代码块
	public static ReentrantReadWriteLock rrwl=new ReentrantReadWriteLock();
	
	public static ReentrantLock rtl=new ReentrantLock(true);

	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) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"方法结束");
		lock.unlock();
		
	}
	
	

	public static void main(String[] args) {
		Runnable run=EasyC::method;
		Runnable runWrite=EasyC::methodWrite;
		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();
		
		System.out.println("main线程结束");
	}
}

        加写锁演示:

package com.easy25;

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

public class EasyC {
	
	//锁容器,里面有读锁和写锁  同一个锁容器的两个锁是互斥的,和定义在哪个方法或者代码块无关
	//没有线程加任何锁可以加写锁,加后没有线程可以再加任何锁
	//读锁是共享锁,加读锁其他线程也可以加读锁
	//传入boolean值,true是公平锁,false是非公平锁,默认非公平锁 
	
	//lock与unlock之间的代码块是同步代码块
	public static ReentrantReadWriteLock rrwl=new ReentrantReadWriteLock();
	
	public static ReentrantLock rtl=new ReentrantLock(true);

	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) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		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) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"方法结束");
		lock.unlock();
	
	}

	public static void main(String[] args) {
		Runnable run=EasyC::method;
		Runnable runWrite=EasyC::methodWrite;
		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();
		Thread e=new Thread(runWrite);
		e.start();
		Thread f=new Thread(runWrite);
		f.start();
		Thread h=new Thread(runWrite);
		h.start();
		System.out.println("main线程结束");
	}
}

锁对象的wait  notify   notifyAll方法

        只有是锁对象时才能调用wait  notify   notifyAll方法,在那个地方是锁对象才能在那个地方用

OBJ.wait();

        让执行到该行代码的线程进入等待状态,在锁对象的等待池等待

OBJ.notify();

        唤醒一条被该锁对象wait的线程,使其进入就绪状态,在其获得锁对象后从上次运行的位置开始运行

OBJ.notifyAll();

        唤醒所有被该锁对象wait的线程

        

        代码演示:


public class EasyD {

	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 {
				System.out.println(Thread.currentThread().getName()+"进入等待状态");
				OBJ.wait();//让执行到该行代码的线程进入等待状态,在锁对象的等待池等待
				System.out.println(Thread.currentThread().getName()+"重新运行");
				//只有某个线程执行notify方法才能唤醒 
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"进入同步代码块");
			OBJ.notify();//最后在唤醒,防止每次换出一个进去一个
		}
		
	}
	public static void main(String[] args) {
		Runnable run=EasyD::method;
		Thread a=new Thread(run);
		a.start();
		Thread b=new Thread(run);
		b.start();
		Thread c=new Thread(run);
		c.start();
	}

}

线程池

        池的目标就是重用     线程池的目的就是线程的重用

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

使用要求:线程池对象使用完毕要调用shutdown方法关闭

        

线程池创建的七个参数

BlockingQueue<Runnable> q=new ArrayBlockingQueue(12);
//核心线程数、最大线程数、存活时间、时间单位、工作队列、工厂模式、默认处理
ThreadPoolExecutor tpe=new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, q,
Executors.privilegedThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

线程池对象执行Runnable 和Callable

public class EasyE {


	public static void main(String[] args) throws InterruptedException, ExecutionException {
		
		BlockingQueue<Runnable> q=new ArrayBlockingQueue(12);
		//核心线程数、最大线程数、存活时间、时间单位、工作队列、工厂模式、默认处理
		ThreadPoolExecutor tpe=new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, q,
			Executors.privilegedThreadFactory(),newThreadPoolExecutor.AbortPolicy());
		
		//线程的任务   Runnable  Callable
		Runnable run=EasyE::method;
		//线程池接收处理任务
		tpe.execute(run);
		//可以加<String>
		Callable call=EasyE::methodCall;
		//Future接收      定义类型都要一致
		Future<String> f=tpe.submit(call);
		//tpe.submit(run);
		
		
		//get方法会等待线程执行完毕       延迟效果
		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";
	}
}
Callable与Future

        线程池不仅可以处理Runnable任务,还可以处理Callable任务

        实现Callable要重写call方法,call方法具有泛型返回值,并且可以抛出异常

        call方法返回值要用Future实例化对象的get方法获取

class p implements Callable{
	@Override
	public Object call() throws Exception {
		// TODO Auto-generated method stub
		return null;
	}
}

完整演示:Future成员方法get方法会等待线程执行完毕再获取返回值     具有延迟效果


		//可以加<String>
		Callable<String> call=EasyE::methodCall;

		//Future接收      定义类型都要一致
		Future<String> f=tpe.submit(call);
		//tpe.submit(run);
		
		//get方法会等待线程执行完毕       延迟效果
		System.out.println(f.get());
		
		//关闭线程池对象
		tpe.shutdown();
	
	

	public static String methodCall() throws InterruptedException{
		System.out.println(Thread.currentThread().getName()+"执行代码call");
		Thread.sleep(2000);
		return "callResult";
	}

线程池重点

        

七个参数

        核心线程数、最大线程数、存活时间、时间单位、工作队列、工厂模式、回绝策略

工作原理

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

四种回绝策略

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

java中内置的四种线程池对象

缓存线程池

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

边界线程池

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

定时运行线程池

        和fixed类型,并且提供定时运行的处理方案
        Executors.newScheduledThreadPool(10);
        

单例线程池

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


扩展

ArrayList与Vector性能比较,如何优化Vector 

        两者底层都是数组实现,由于 ArrayList 不是线程安全的,它在单线程环境下通常比 Vector 有更好的性能,尤其是在迭代和随机访问元素时;Vector 的同步机制增加了额外的开销,这在高并发环境下可能会影响性能。

        优化方法:将synchronized关键字换成读锁和写锁,读操作时效率会大大提升



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


public class EasyList{
	
	
	public static void main(String[] args) {
        EasyList list=new EasyList();
        Runnable runSize=()->{list.size();};
        Runnable runGet=()->{list.get(0);};
        Runnable runAdd=()->{list.add(12);};
        list.add(12);
        Thread a=new Thread(runSize);
        Thread b=new Thread(runGet);
        Thread c=new Thread(runAdd);
        a.start();b.start();c.start();
    }
	
	
	
    private int[] values=new int[20];
    private int size=0;

    ReentrantReadWriteLock rwLock=new ReentrantReadWriteLock();

    public int size(){
        Lock readLock=rwLock.readLock();
        readLock.lock();
        System.out.println(Thread.currentThread().getName()+"Size开始");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName()+"Size结束");
        readLock.unlock();
        return size;
    }

    public int get(int index){
        Lock readLock=rwLock.readLock();
        readLock.lock();
        System.out.println(Thread.currentThread().getName()+"Get开始");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if(index>=size){
            throw new IndexOutOfBoundsException("index is "+index);
        }
        System.out.println(Thread.currentThread().getName()+"Get结束");
        readLock.unlock();
        return values[index];
    }

    public  boolean add(int item){
        Lock writeLock=rwLock.writeLock();
        writeLock.lock();
        System.out.println(Thread.currentThread().getName()+"Add开始");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if(size>=values.length){
            return false;
        }
        values[size++]=item;
        System.out.println(Thread.currentThread().getName()+"Add结束");
        writeLock.unlock();
        return true;
    }
}

什么是死锁,什么情况下会发生死锁现象

        死锁是指两个或多个进程(或线程)在执行过程中,因争夺资源而造成的一种僵局。

        1. 互斥条件: 一个资源每次只能被一个进程使用

        2. 占有且等待:一个进程因请求资源而阻塞时,对已获得的资源保持不放

        3. 不可强行占有: 进程已获得的资源,在末使用完之前,不能强行剥夺。

        4. 循环等待条件: 若干进程之间形成一种头尾相接的循环等待资源关系

要避免死锁,可以采取以下一些策略:

        避免占有和等待:要求进程在开始执行前一次性请求所有需要的资源。
        资源有序分配:按照某种全局顺序来请求资源,确保所有进程都按照这个顺序来请求。
        超时:允许进程请求资源时设置超时,如果超时则释放所有资源并重试。
        资源剥夺:当检测到死锁时,可以强制剥夺某个进程的资源并分配给其他进程。
        死锁检测:定期检测资源分配图是否存在循环等待,如果检测到死锁,采取措施解决。

        在编程中,特别是在多线程环境中,可以通过合理设计同步机制和资源访问策略来避免死锁的发生。

wait和sleep区别

    wait是Object内定义的方法,由锁对象调用,只能在其负责的同步方法或同步块中调用,让执行到该行代码的线程进入等待状态
    sleep是Thread类中定义的静态方法,可以在任何地方调用,也可以让执行到该行的线程进入等待状态
    区别:
    1.功能上:sleep需要传入一个毫秒数,到达时间后会自动唤醒;wait不能自动唤醒,必须要其锁对象的 notify或 notifyAll方法唤醒;
    2.sleep方法不会释放任何锁,wait方法会释放当前对象的锁,允许其他线程进入同步方法或同步块。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值