暑假开始学习java,今天开始每天都在这笔记做笔录做总结,希望能在目标的8月七号前完成第一遍的学习!加油!
1.使用关键字synchronized修饰的方式是不是表示让一个线程执行呢?事实并非如此,在Java语言中,每个对象都有一把“锁”,而关键字synchronized的意思是获取对象的锁。而这样的锁一次只能被一个线程获取,当多个线程执行syschronized操作的时候,其中一个线程抢到了“锁”,其他没有抢到的就等机会。当获取了锁的线程执行完毕,并释放“锁”之后,其他线程才能获取锁。
2.快捷键 整行删除 ctrl+d
3.
/*
* 将售票的例子改成Runnable接口的方式
*
* 创建线程方式2:
* 1,定义一个类实现Runnable接口
* 2,覆盖runnable接口中的run方法
* 将线程要运行的代码存储到run方法中
* 3,创建该接口的子类对象。
* 4,通过Thread类进行线程的创建,并将Runnable接口的子类对象作为Thread类的构造函数的实参进行传递。
* 为什么要传递?
* 让线程对象创建时候,就要明确要运行哪个run方法,而这个run方法是需要被对象调用的。
* 所以将run方法所属的对象传递给Thread类构造函数
* 5,调用thread类中的start方式开启线程
*
* Runnable接口的由来其实就是将线程的任务进行对象的封装
* 将线程任务封装成对象后,通过Runnable接口可以降低和Thread对象的耦合性
*
* 如果是集成Thread类,覆盖run方法这种情况
* Thread的子类即封装了线程的任务,而且自身还是一个线程对象
* 这就是任务和对象耦合性过高。
* class Demo extends Thread{
* public void run(){
* //线程任务
* }
* }
* Demo d = new Demo();//这是一个带有具备任务的线程对象
* d.start();
*
*
* 如果是实现Runnable接口
* class Demo implements runnable{
* public void run(){
* //线程的任务
* }
* }
* Demo d = new Demo();//这是一个任务对象
* Thread t1 = new Thread(d);//创建线程对象去运行指定的任务。
*
* class Thread{
* Thread(Runnable r){
*
* }
* }
*
* Runable接口的出现,线程对象在使用该接口
* 我们只要实现该接口即可,讲实现的子类对象,也就是线程任务传递给线程对象就ok了
*
* 以后创建线程就用Runnable接口的方式
* 避免了单继承的局限性
*/
4.
/*
* 通过两个线程来验证同步函数的锁是什么
*
* 都是在买票一个在同步代码块中买票
* 一个在同步函数中卖票。如果两个线程用的锁是同一个,就会同步,不会出现错票的情况
*
* 同步函数使用的锁是什么呢?
* 函数肯定被对象调用。代表调用函数对象的引用就是this。
*
* 也就是说同步函数使用的锁是this
*
*
*
* 同步函数与同步代码块的区别?
* 1,同步函数函数比同步代码块写法简单
* 2,同步函数使用的锁是this,同步代码块使用的锁是任意指定的对象
* 建议开发时,使用同步代码块。尤其是需要不同锁时。
*
*
* 静态同步函数使用的锁是什么?
*
* 静态随着类的加载而加载的,这时内存中存储的对象至少有一个
* 这就是该类字节码文件对象
* 这个对象的表示方法 类名.class 他就表示字节码文件对象
*
*
*
* 同步的另一个弊端:死锁。
*
* 最常见的思索状况:同步嵌套
* 同步中还有同步,两个同步用的不是一个锁
* 记住尽量避免同步嵌套的情况
*
*/
5.suspend()方法和stop()方法容易导致死锁,不推荐
6.不要对死亡状态的线程调用start()方法,程序只能对新建状态的线程调用start()方法,对新建状态的线程两次调用start()方法也是错误的,这会引发IllegalThreadStateException异常
7.后台线程(aka守卫线程 aka精灵线程)特征:如果所有的前台线程都死亡,后台线程会自动死亡
调用Thread对象的setDaemon(true)方法可将制定线程设置为后台线程。可以看到 当所有前台线程死亡时,后台线程随之死亡。当整个学你急中只剩下后台线程时,程序就没有继续进行的必要了,所以虚拟机也就退出了
Thread类还提了isDaemon()方法,用于判断制定线程是否为后台线程
前台线程死亡后,JVM会通知后台线程死亡,但从他啊接受指令到做出相应,需要一定时间,而且要将某个线程设置为后台线程,必须在该线程启动之前设置。也就是说,setDaemon(true)必须在start()方法之前调用,否则会引发IllegalThreadStateException异常
8.
Lock
实现提供了比使用 synchronized
方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition
对象。
锁是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问。一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁。不过,某些锁可能允许对共享资源并发访问,如 ReadWriteLock
的读取锁。
synchronized
方法或语句的使用提供了对与每个对象相关的隐式监视器锁的访问,但却强制所有锁获取和释放均要出现在一个块结构中:当获取了多个锁时,它们必须以相反的顺序释放,且必须在与所有锁被获取时相同的词法范围内释放所有锁。
虽然 synchronized
方法和语句的范围机制使得使用监视器锁编程方便了很多,而且还帮助避免了很多涉及到锁的常见编程错误,但有时也需要以更为灵活的方式使用锁。例如,某些遍历并发访问的数据结果的算法要求使用 "hand-over-hand" 或 "chain locking":获取节点 A 的锁,然后再获取节点 B 的锁,然后释放 A 并获取 C,然后释放 B 并获取 D,依此类推。Lock
接口的实现允许锁在不同的作用范围内获取和释放,并允许以任何顺序获取和释放多个锁,从而支持使用这种技术。
随着灵活性的增加,也带来了更多的责任。不使用块结构锁就失去了使用 synchronized
方法和语句时会出现的锁自动释放功能。在大多数情况下,应该使用以下语句:
Lock l = ...; l.lock(); try { // access the resource protected by this lock } finally { l.unlock(); }锁定和取消锁定出现在不同作用范围中时,必须谨慎地确保保持锁定时所执行的所有代码用 try-finally 或 try-catch 加以保护,以确保在必要时释放锁。
9.ReentrantLock锁具有可重入性,也就是说,一个线程可以对已被加锁的ReentrantLock锁在此加锁,ReentrantLock对象会维持一个计算器来追踪lock()方法的嵌套调用,线程在每次调用lock()加锁后,必须显示调用unlock来释放锁,所以一段被锁保护的代码可以调用另一个被相同锁保护的方法。
网上找到一个对锁机制的一个很好的解释 点击打开链接