java多线程笔记

1.线程的创建
1)继承thread类,重新run()方法
2)实现runnable接口
2.JVM的内存模型
3.线程的几种状态
创建New 可运行Runnable 被阻塞Blocked 等待Waiting(wait join方法 )
计时等待Timed waiting (Object.wait、Thread.join、Lock.tryLock和Condition.await等方法有超时参数,还有Thread.sleep方法、LockSupport.parkNanos方法和LockSupport.parkUntil方法,这些方法会导致线程进入计时等待状态,如果超时或者出现通知,都会切换回可运行状态;) 被终止Terminated
4.死锁
5.synchronizied
作用主要有:(1)确保线程互斥的访问同步代码(2)保证共享变量的修改能够及时可见。
synchronized总共有四种用法:
(1)修饰普通方法 (对于一个实例来说,只有一个线程能够在这个方法同步块中运行。如果有多个实例存在,每一个实例对应一个线程。 )
(2)修饰静态方法 (静态方法本质上是属于类的方法,而不是对象上的方法),所以即使test1和test2属于不同的对象,但是它们都属于SynchronizedTest类的实例,所以也只能挨个顺序执行线程,不能并发执行。)
(3)修饰普通方法中的代码块 (在同步构造器中用括号括起来的对象叫做监视器对象。在上例中,使用了this,即为调用该方法的实例本身。上述代码使用监视器对象同步,并使用调用方法本身的实例作为监视器对象。同一时间只有一个线程能够运行同一个监视器对象的同步块代码。)
(4)修饰静态方法中的代码块

这两条指令(monitorenter和monitorexit)的作用是什么?先看monitorenter :
每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:
(1)如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。
(2)如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1。
(3)如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。
再看monitorexit: 
执行monitorexit的线程必须是objectref所对应的monitor的所有者。
指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个monitor 的所有权。
通过上边两段描述,我们可以看出在同步代码块中synchronized的实现原理,是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException异常的原因。

从反编译的结果来看,方法的同步并没有通过指令monitorenter和monitorexit来完成(理论上其实也可以通过这两条指令来实现),不过其常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的,具体的:
当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。

Executor . ExecutorService AbstractExecutorService

6.线程间的协作
wait() ,notify(), notifyAll();
为了调用wait()或者notify(),线程必须先获得那个对象的锁。也就是说,线程必须在同步块里调用wait()或者notify()。
一个线程如果没有持有对象锁,将不能调用wait(),notify()或者notifyAll()。当你调用它们的时候,JVM首先要检查下当前线程是否是锁的拥有者,不是则抛出IllegalMonitorStateExcept异常。
注意一个问题:调用wait()和notify()都在同步块中,且有相同的监视器对象,那么对象调用一个方法时不会阻塞别的方法吗?答案是不会。一旦线程调用了wait()方法,它就释放了所持有的监视器对象上的锁,进入waiting状态直到其他线程调用这个对象上的notify()方法或者超时。
notify()和notifyAll()都是Object对象用于通知处在等待该对象的线程的方法。区别在于:
notify会随机唤醒一个wait状态线程,并使它获得该对象上的锁,而不惊动其它线程,此时对于其它线程来说,它们等待的是notify信号并一直等待下去。
notifyAll唤醒所有等待的线程,使它们的状态从等待notify变成等待锁,一旦该对象被解锁,他们就会去竞争锁,最终只有一个线程能获得对象锁并执行代码,其它的线程依次执行。

sleep()、yield()、join()

sleep()、yield()、join()三个方法作为线程通信的操作,它们是位于Thread类中。但是,同样实现线程通信的wait()、notify()、notifyAll()方法却不是位于Thread类中,而是位于Object类中?
因为这三个方法要操作锁,而锁是所有对象的一部分,这样Java的每一个类都有用于线程间通信的基本方法。我们只能在同步控制方法或同步控制块中调用wait()、notify()、notifyAll()方法,不用考虑这个类是继承了Thread类还是实现了Runnable接口。而sleep()方法可以在非同步控制方法中调用,因为它不用操作锁。
实际上,在非同步控制方法中调用wait()、notify()、notifyAll()方法是可以通过编译的,但运行时会报IllegalMonitorStateExcept异常。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值