黑马程序员——JAVA基础之Day24 多线程 ,死锁,线程间通信 ,线程组,线程池,定时器。
-
Lock()实现提供了比使用synchronized方法和语句可获得更广泛的锁定操作。
-
private Lock lock =new ReentrantLock();
-
被锁的代码要用 lock.lock() lock.unlock() 包括。其中用try 。。。finally包围
-
同步:效率低,如果出现同步嵌套,会出现死锁。 但是安全。
-
死锁问题:两个或者两个以上的线程在争夺资源的过程中,发生的一种相互等待的现象
-
线程间通信,不同种类的线程针对同一资源的操作。
-
多线程还可能出现同一个数据出现多次,或者输出不匹配问题。都属于线程安全问题。
-
等待 唤醒机制
-
Object类中wait()等待 notify()唤醒单个线程 notifyAll()唤醒所有线程
-
这些方法调用必须通过锁对象调用,二我们刚才使用的锁对象是任意锁对象。
wait()要用try catch包围 用唤醒notify()来解决死锁即一个run()里判断一个条件,如果成立就锁调用wait(),如果不成立,锁的判断条件改变,然后调用notify()唤醒。
-
运行状态图:
-
线程组:默认情况下是main线程组
getThreadGroup()获得线程组
Thread.currentThread().getThreadGroup().getName();返回线程组的名字。 -
改变线程组
首先创建一个线程组:ThreadGroup tg=new ThreadGroup("这是一个新的线程组");然后Thread t1=new Thread(tg,Myrunnale,"线程名");
-
通过线程组名称设置后台线程,tg.setDaemon() 设置守护线程。等等
线程组主要是对线程统一管理。
-
同步还可以在类里,把相应的方法设置成synchronized 注意方法里面的锁的对象是this
-
线程池:程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互,二使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存周期很短的线程时,更应该考虑使用线程池。
-
线程池每个线程结束后不会销毁,而是回到线程池变成空闲状态。
-
static ExecutorService
newCachedThreadPool()
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。 -
static ExecutorService
newFixedThreadPool(int nThreads)
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。 -
static ExecutorService
newSingleThreadExecutor()
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。 -
A:创建一个线程池对象,控制要创建几个线程对象
ExecutorServoce newFixedThreadPool(int nThread)
B:这种线程池的线程可以执行:
可以执行Runnable对象或者CallAble对象代表的线程
做一个实现Runnable接口
C:调用如下方法即可:
Future<?> submit(Runnable task)
<T> Future<T> submit(Callable<T> task) -
newFixedThreadPool静态函数,直接调用
-
1 ExecutorService pool=Executors.newFixedThreadPool(2); 2 pool.submit(new MyRunnable);
- 结束线程池:pool.shutdown();
- 创建多线程方式3:
Callable是个接口 这个线程有返回接口,但是runnable没有返回结果。 跟 Runnable用法相似
他是通过线程池来创建线程。 这个不常见 - Future 表示submit异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行。 麻烦,一般不用。
- 匿名内部类使用多线程“
1 //集成Thread类来实现多线程 2 new Thread(){ 3 public void run() { 4 for (int i = 0; i < 100; i++) { 5 System.out.println(Thread.currentThread().getName()+":"+i); 6 } 7 }; 8 }.start(); 9 //用Runnable接口实现 10 new Thread(new Runnable() { 11 12 @Override 13 public void run() { 14 // TODO Auto-generated method stub 15 for (int i = 0; i < 100; i++) { 16 System.out.println(Thread.currentThread().getName()+":"+i); 17 } 18 } 19 }){}.start();
- 但是对于,走的是子类的,即会输出world,不会输出hello
1 new Thread(new Runnable() { 2 3 @Override 4 public void run() { 5 // TODO Auto-generated method stub 6 for (int i = 0; i < 100; i++) { 7 System.out.println("hello"+":"+i); 8 } 9 } 10 }){ 11 public void run() { 12 for (int i = 0; i < 100; i++) { 13 System.out.println("world"+":"+i); 14 } 15 }; 16 }.start();
-
定时器:在指定时间做某件事或者重复某件事
Timer 一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。
- 依赖Timer和TimerTask两个类
Timer 定时
TimerTask :任务 -
构造方法摘要 Timer()
创建一个新计时器。 -
timer方法
cancel()
终止此计时器,丢弃所有当前已安排的任务。int
purge()
从此计时器的任务队列中移除所有已取消的任务。void
schedule(TimerTask task, Date time)
安排在指定的时间执行指定的任务。void
schedule(TimerTask task, Date firstTime, long period)
安排指定的任务在指定的时间开始进行重复的固定延迟执行。void
schedule(TimerTask task, long delay)
安排在指定延迟后执行指定的任务。void
schedule(TimerTask task, long delay, long period)
安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。void
scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
安排指定的任务在指定的时间开始进行重复的固定速率执行。void
scheduleAtFixedRate(TimerTask task, long delay, long period)
安排指定的任务在指定的延迟后开始进行重复的固定速率执行。 - 面试题
1:多线程有几种实现方案,分别是哪几种? 两种。 继承Thread类 实现Runnable接口 扩展一种:实现Callable接口。这个得和线程池结合。 2:同步有几种方式,分别是什么? 两种。 同步代码块 同步方法 3:启动一个线程是run()还是start()?它们的区别? start(); run():封装了被线程执行的代码,直接调用仅仅是普通方法的调用 start():启动线程,并由JVM自动调用run()方法 4:sleep()和wait()方法的区别 sleep():必须指时间;不释放锁。 wait():可以不指定时间,也可以指定时间;释放锁。 5:为什么wait(),notify(),notifyAll()等方法都定义在Object类中 因为这些方法的调用是依赖于锁对象的,而同步代码块的锁对象是任意锁。 而Object代码任意的对象,所以,定义在这里面。 6:线程的生命周期图 新建 -- 就绪 -- 运行 -- 死亡 新建 -- 就绪 -- 运行 -- 阻塞 -- 就绪 -- 运行 -- 死亡 建议:画图解释。