java学习笔记:多线程2

1、lock锁的概述和使用
a、概述:虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK1.5以后提供了一个新的锁对象Lock
b、Lock和ReentrantLock

  • ReentrantLock:一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。
  • Lock:实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。

c、Lock锁的使用

public class MyThread implements Runnable { 	    	
	private static int tickets = 100 ;	// 定义票数  
    private static final Lock lock = new ReentrantLock() ;  // 创建Lock锁对象
    @Override
	public void run() {		
	while(true){			
		lock.lock() ;	// 添加锁		
		if(tickets > 0){				
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}				
			System.out.println(Thread.currentThread().getName() + "正在出售" + (tickets--) + "张票");				
		}			
		lock.unlock() ;	// 释放锁		
	}		
  }
}
public class MyThreadDemo {
  	public static void main(String[] args) {		
	   // 创建MyThread类的对象
	   MyThread mt = new MyThread() ;		
    	// 创建3个线程对象
	   Thread t1 = new Thread(mt , "窗口1") ;
	   Thread t2 = new Thread(mt , "窗口2") ;
	   Thread t3 = new Thread(mt , "窗口3") ;		
	   // 启动线程
	   t1.start() ;
	   t2.start() ;
	   t3.start() ;				
    }
}

2、死锁问题概述和使用
a、概述:如果出现了同步嵌套,就容易产生死锁问题,是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象。
死锁: 两个或者两个以上的线程,在抢占CPU的执行权的时候,都处于等待状态
b、案例演示

class ObjectUtils {
    //定义两把锁
    public static final Object objA=new Object();
    public static final Object objB = new Object();
}
class MyThread extends Thread {
boolean flag;
public MyThread(boolean flag) {
    this.flag = flag;
}
@Override
public void run() {
    if (flag) {
        synchronized (ObjectUtils.objA) {
            System.out.println("true 进来了 持有objA");
            try {
                sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //阻塞
            synchronized (ObjectUtils.objB) {
                System.out.println("true 进来了 持有objB");
            }
        }
    } else {
        synchronized (ObjectUtils.objB) {
            System.out.println("false 进来了 持有objB");
            //阻塞
            synchronized (ObjectUtils.objA) {
                System.out.println("false 进来了 持有objA");
            }
         }
     }
  }
}
public class MyDemo {
   public static void main(String[] args) throws InterruptedException {
      MyThread th1 = new MyThread(true);
      MyThread th2 = new MyThread(false);
      th1.start();
      th2.start();
  }
}
输出结果:true 进来了 持有objA
         false 进来了 持有objB
原因:在执行了第一条程序后,相互持有了对方的锁而未释放,导致程序发生阻塞不能往下执行

3、线程间的等待唤醒机制
一旦等待,就要立马释放锁,下次被换醒之后,从哪里等待从哪里醒来。

  • void wait ();//在其他线程调用此对象的 notify () 方法或 notifyAll () 方法前,导致当前线程等待。
  • void wait ( long timeout);//在其他线程调用此对象的 notify () 方法或 notifyAll () 方法,或者超过指定的时间量前,导致当前线程等待。
  • void notify ();//唤醒在此对象监视器上等待的单个线程。
  • void notifyAll ();//唤醒在此对象监视器上等待的所有线程。

sleep方法和wait方法的区别:都可以让线程处于阻塞状态
sleep()必须传入时间量;wait可传时间量也可不传时间量
sleep()线程休眠后,不释放锁;wait()方法 一旦等待 就要释放锁
4、线程的状态转换图及常见执行情况
新建:线程被创建出来
就绪:具有CPU的执行资格,但是不具有CPU的执行权
运行:具有CPU的执行资格,也具有CPU的执行权
阻塞:不具有CPU的执行资格,也不具有CPU的执行权
死亡:不具有CPU的执行资格,也不具有CPU的执行权
在这里插入图片描述
5、线程池的概述和使用
a、概述:是一个容器,里面预先装有线程对象,并可以帮我们高效的管理线程对象,我们自己手动创建线程,是比较耗费底层资源的,而且这个线程使用完之后,就死亡了,不能重复利用;JDK1.5之后已经给我们提供好了线程池,我们只需要,往线程池里面提交任务即可.

  • 获取线程池对象,Java给我们提供了一个工厂,用这个工厂类,来来获取线程池对象

  • ExecutorService executorService = Executors.newCachedThreadPool();

  • JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法

  • public static ExecutorService newCachedThreadPool ();根据任务的数量来创建线程对应的线程个数

  • public static ExecutorService newFixedThreadPool ( int nThreads);固定初始化几个线程

  • public static ExecutorService newSingleThreadExecutor ();初始化一个线程的线程池

b、 举例:

 public class MyDemo {
     public static void main(String[] args) {
     //获取线程池对象
        ExecutorService executorService = Executors.newCachedThreadPool();
        //往线程池中提交任务
        MyRuannable myRuannable = new MyRuannable();
        MyRuannable myRuannable2 = new MyRuannable();
        MyRuannable myRuannable3 = new MyRuannable();
        MyRuannable myRuannable4 = new MyRuannable();
        executorService.submit(myRuannable);
        executorService.submit(myRuannable2);
        executorService.submit(myRuannable3);
        executorService.submit(myRuannable4);
        //关掉线程池 
        executorService.shutdown();    
      }
  }
class MyRuannable implements Runnable{
    @Override
    public void run() {
       System.out.println(Thread.currentThread().getName()+"任务执行了");
    }
}

6、匿名内部类的方式实现多线程程序

public class MyDemo3 {
    public static void main(String[] args) {
        //匿名内部类来创建线程
        //方式1
        new Thread() {
            @Override
            public void run() {
                System.out.println("线程执行了");
            }
        }.start();
        //方式2:
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("线程执行了");
            }
        };
        new Thread(runnable) {
        }.start();
    }
}

7、定时器的概述和使用
a、概述:定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。在Java中,可以通过Timer和TimerTask类来实现定义调度的功能。
b、Timer和TimerTask

  • Timer:
    public Timer()//创建一个新计时器
    public void schedule(TimerTask task, long delay);//安排在指定延迟后执行指定的任务
    public void schedule(TimerTask task,long delay,long period);//安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。
    public void schedule(TimerTask task, Date time);//安排在指定的时间执行指定的任务。
    public void schedule(TimerTask task, Date firstTime, long period);//安排指定的任务在指定的时间开始进行重复的固定延迟执行。
  • TimerTask
    public abstract void run();//此计时器任务要执行的操作。
    public boolean cancel();//取消此计时器任务。

c、案例演示

定时任务的多次执行代码体现
定时删除指定的带内容目录
public class MyDemo {
    public static void main(String[] args) throws ParseException {
        Timer timer = new Timer();//创建定时器
        MyTimerTask task = new MyTimerTask(timer);
        //timer.schedule(task,2000); //参数1 定时任务,参数 等待多少毫秒后执行
       // timer.schedule(task,2000,1000);//连环爆炸
        task.cancel(); 取消定时任务
        //指定日期去执行任务
        String dateStr="2019-01-20 13:46:00";
        Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateStr);
       // timer.schedule(task,date,1000*60*60*24*30);
    }
}
class MyTimerTask extends TimerTask{
    Timer timer;
    public MyTimerTask(Timer timer) {
        this.timer=timer;
    }
    @Override
    public void run() {
       // System.out.println("砰~~~爆炸了");
       // timer.cancel(); //关闭定时器
    }
}
发布了24 篇原创文章 · 获赞 11 · 访问量 2049

猜你喜欢

转载自blog.csdn.net/weixin_43791069/article/details/86684478
今日推荐