java学习笔记4

来源:互联网 发布:伊斯兰知识软件下载 编辑:程序博客网 时间:2024/05/09 01:03
CoreJava第十天     20125月16

 

一、复习内部类

 1、成员内部类

    可以访问外部类的私有成员,外部类类名.this.属性

    构造成员内部类对象,必须先构造一个外部类对象,外部类对象.new 构造内部类对象

 2、静态内部类

    只能访问外部类的静态成员

    构造静态内部类对象时,不再需要构造外部类对象

 3、局部内部类

    在外部类方法内定义的内部类

    不仅能访问外部类的私有成员,而且还能访问外部类的局部变量,但是要求局部变量是final

 4、匿名内部类

    局部内部类,用于实现一个借口或者继承一个类,只会构造一次

 

 内部类的作用:

    访问外部类的私有成员,不破坏封装。可以给编程带来一些方便

    我们可以把接口公开,把接口的实现类以内部类的形式隐藏起来。强制用户通过接口来实现弱耦合

    接口+内部类实现多继承

 

二、List接口的实现类

1ArrayList

   底层使用数组实现

2Vector

  ArrayList  轻量级  线程不安全

  Vector     重量级  线程安全的

3LinkedList

  底层使用双向循环链表实现

  ArrayList   数组 查询快 增删操作慢

  LinkedList  链表 查询慢 增删操作快  

  使用组合复用实现栈

 

三、Set接口

1HashSet

Set的实现类的集合对象中不能够有重复元素,HashSet也一样是使用了一种标识来确定元素的不重复,是元素内容不重复

HashSet用一种算法来保证集合中的元素是不重复的,HashSet的底层实现还是数组。

Object类中的hashCode()的方法是所有子类都会继承这个方法,这个方法会用Hash算法算出一个Hash(哈希)码值返回,HashSet会用Hash码值去和数组长度取模,

模(这个模就是对象要存放在数组中的位置)相同时才会判断数组中的元素和要加入的对象的内容是否相同,如果不同才会添加进去。

 

Hash算法是一种散列算法。

 

注意:所有要存入HashSet的集合对象中的自定义类必须覆盖hashCode(),equals()两个方法,才能保证集合中元素容不重复。

      在覆盖hashCode()方法时,要使相同对象的hashCode()方法返回相同值,覆盖equals()方法再判断其内容。为了保证效率,所以在覆盖hashCode()方法时,也要尽量使不同对象尽量返回不同的Hash码值。

 

如果数组中的元素和要加入的对象的hashCode()返回了相同的Hash值(相同对象返回相同整数),才会用equals()方法来判断两个对象的内容是否相同(不同对象返回不同整数)。

 

练习:

把若干Employee对象放在Set中并遍历,要求没有重复元素

 

2SortedSet接口是Set的子接口。

TreeSetSortedSet接口的实现类,他可以对集合中的元素进行排序。

要存放在TreeSet中自定义类的对象,这个类要么是已经实现了Comparable接口,要么是能给出Comparator比较器,

TreeSet可以自动过滤掉重复元素所以不用重载hashCode()方法,TreeSet会根据比较规则判断元素内容是否相同,TreeSet会在元素存入时就进行了排序。

判断对象重复的依据:compareTo()方法的返回值为,就是重复元素

(在TreeSet给出排序规则时,一定要注意对象内容相等的条件,一定要注意在主观的认为两个对象内容相同时,才可以使用比较少的条件来进行判断)

 

在要排序时才使用TreeSet类(存储效率比较低),HashSet的存储效率比较高,在需要为HashSet的对象排序时,就可以把HashSet中的元素放入TreeSet

 

四、Map

Map中只可以存放键值对(Keyvalue),其中Key是不可以重复的。Keyvalue是一一对应的。

 

HashMap,是Map接口的实现类,Key时无序存放的,其中Key是不可以重复的,它也是通过Hash码值来保证Key不重复的,Keyvalue是一一对应的。

如果要加入的键值对和HashMap中键值对的Key是相同的就会将这个集合中的Key所队应的value值进行覆盖,在使用自定义类型作为Key时,那就是要覆盖hashCode(),equals()方法,也就是和HashSet中要放入自定义类型是的处理方法相同。

这个类的对象是线程不安全的。

 

遍历:(1vlaues() 返回所有值(value)的集合,是一个Collection

      2keySet() 返回所有键对象的集合,是一个Set

          过遍历这个Set,用get()方法来获得Key所对应的value,也就遍历了Map

 

Hashtable,也是Map接口的实现类,他和HashMap比较相似,只不过这个类对象是重量级的,也是线程安全的。他不允许Keyvaluenull

 

Properties,这个类是Hashtable的子类,他的Keyvalue只能是字符串。

 

SortedMapMap的子接口

TreeMap,是SortedMap的实现类,他会按照Key进行排序。和TreeSet类一样,在使用自定义类作Key时,要用自定义类实现Comparable接口。

 

 

 

练习:

达内希望在学生毕业的时候统计出学生在校期间考试成绩的排名,写一个Student,其中用集合来管理每个学生的各个科目的考试成绩,

将多个Student对象放在集合中,打印出学生的总分以及排名

 

(集合)

改写Bank,采用集合的方式来管理多个Account对象

Bank类添加一个方法

打印所有用户的总资产排名

说明:一个用户可能会有多个账号,以身份证号为准.总资产指多个账户余额的总和,不需要考虑贷款账户的贷款额

 

考试系统

Exam   考试类

属性: 若干学生  一张考卷

提示:学生采用HashSet存放

 

Paper   考卷类 

属性:若干试题

提示:试题采用HashMap存放,keyString,表示题号,value为试题对象

 

Student     学生类

属性:姓名   一张答卷   一张考卷

 

Question    试题类

属性:题号 题目描述    若干选项    正确答案(多选)

提示:若干选项用ArrayList

 

AnswerSheet    答卷类

属性:每道题的答案   

提示:答卷中每道题的答案用HashMap存放,keyString,表示题号,value为学生的答案

 

问题:为Exam类添加一个方法,用来为所有学生判卷,并打印成绩排名(名次、姓名)

 

 

                         CoreJava第十一天      2012517

一、复习

集合:用一个对象储存管理多个对象

 

Collection:元素都是对象

遍历:迭代遍历

 

List:元素有顺序,可以重复

      遍历:还可以用for循环(下标)

      排序:Collections.sortlist

实现类:

ArrayList:底层数组实现,查询快,而增删慢;轻量级,线程不安全

Vector:底层数组实现,重量级,线程安全

LinkedList:底层链表实现,查询慢,增删快

Java中用LinkedList实现一个栈,不用数组,因为栈的主要功能就是增删,数组慢;不用Vector,因为效率低

 

Set:元素无序,元素内容不重复

SortedSet:按照各种排序规则给Set排序

 

实现类:

HashSet:采用哈希算法保证元素不重复,覆盖hashCode()保证哈希码相同,equals()保证true

TreeSet:元素一定要实现了Comparable接口的

         根据排序规则,compareTo()返回,说明是重复元素

 

Map:元素是键值对

     key  无序,不重复       value 可以重复

SortedMap:按照key排序

遍历:values() 遍历所有的值对象

      keySet() 遍历所有的键对象

实现类:

HashMap:线程不安全  允许null作为keyvalue

Hashtable:线程安全,不允许

TreeSetSortedSet的实现类

 

二、java中的图形界面

 

GUI,图形化的用户接口,为了人机交互使用的。

 

BSCS的联系与区别。

C/SClient/Server的缩写。服务器通常采用高性能的PC、工作站或小型机,并采用大型数据库系统,如OracleSybaseInformix SQL Server。客户端需要安装专用的客户端软件。

B/S是Brower/Server的缩写,客户机上只要安装一个浏览器(Browser),如Netscape NavigatorInternet Explorer,服务器安装OracleSybaseInformix SQL Server等数据库。

在这种结构下,用户界面完全通过WWW浏览器实现,一部分事务逻辑在前端实现,但是主要事务逻辑在服务器端实现。浏览器通过Web Server 同数据库进行数据交互。

C/S  B/S 区别:

1.硬件环境不同:

C/S 一般建立在专用的网络上小范围里的网络环境局域网之间再通过专门服务器提供连接和数据交换服务.

B/S 建立在广域网之上的不必是专门的网络硬件环境,例与电话上网租用设备信息自己管理有比C/S更强的适应范围一般只要有操作系统和浏览器就行

2.对安全要求不同

C/S 一般面向相对固定的用户群对信息安全的控制能力很强一般高度机密的信息系统采用C/S 结构适宜可以通过B/S发布部分可公开信息.

B/S 建立在广域网之上对安全的控制能力相对弱可能面向不可知的用户。

3.对程序架构不同

C/S 程序可以更加注重流程可以对权限多层次校验对系统运行速度可以较少考虑.

B/S 对安全以及访问速度的多重的考虑建立在需要更加优化的基础之上C/S有更高的要求 B/S结构的程序架构是发展的趋势MS.Net系列的BizTalk 2000 Exchange 2000,

     全面支持网络的构件搭建的系统. SUN IBM推的JavaBean 构件技术等,使 B/S更加成熟.

4.软件重用不同

C/S 程序可以不可避免的整体性考虑构件的重用性不如在B/S要求下的构件的重用性好.

B/S 对的多重结构,要求构件相对独立的功能能够相对较好的重用.就入买来的餐桌可以再利用,而不是做在墙上的石头桌子

5.系统维护不同?

C/S 程序由于整体性必须整体考察处理出现的问题以及系统升级升级难可能是再做一个全新的系统

B/S 构件组成,方面构件个别的更换,实现系统的无缝升级系统维护开销减到最小.用户从网上自己下载安装就可以实现升级.

6.处理问题不同

C/S 程序可以处理用户面固定并且在相同区域安全要求高需求与操作系统相关应该都是相同的系统

B/S 建立在广域网上面向不同的用户群分散地域这是C/S无法作到的与操作系统平台关系最小.

7.用户接口不同

C/S 多是建立的Window平台上,表现方法有限,对程序员普遍要求较高

B/S 建立在浏览器上有更加丰富和生动的表现方式与用户交流并且大部分难度减低,减低开发成本.

8.信息流不同

C/S 程序一般是典型的中央集权的机械式处理交互性相对低

B/S 信息流向可变化, B-B B-C B-G等信息、流向的变化更像交易中心。

 

构造图形界面的步骤

1,选择一个容器

2,设置容器的布局管理器

3,向容器添加组件

4,事件的监听

 

容器(Container)用于管理其他的组件的对象。组件必须放到容器里。

JFrame,这是一个最顶层的窗体容器,所有其他的组件必须放在顶层容器里。

   JFrame frame = new JFrame("Hello Swing");  //创建窗体,字符串为窗体的标题

   frame.setSize(500,300);  //设置窗口大小,500像素长,300像素高

   frame.setVisible(true);  //设置可见性

JPanel,他不是顶层容器,必须放在顶层容器中,任何一个容器都有add()方法,Panel面板是透明的(默认)。他也是一个组件。

JDialog 对话框容器,他要依附于其父组件,他不是一个顶层容器。

 

 

布局管理:对于任何一个容器类中都有setLayout()方法,用容器对象调用这个方法,来设置容器的布局管理器(LayoutManager这是一个接口,所有布局管理器都实现了这个接口)。

 

可用的布局管理器:

所有的布局管理器实现了一个接口java.awt.LayoutManager

FlowLayout,流式布局管。尝试在一行中按增加顺序摆放组件,窗体大小改变时,组件位置会相应发生改变

    Panel的默认布局管理就是FlowLayout

    FlowLayout flow = new FlowLayout();

     frame.setLayout(flow);

BorderLayout,按方位进行布局管理,(NorthSouthEastWestMiddle)不明确指定,就会默认加载在中间(Middle),每个部分只能放一个组件

    frame.add(Component comp,String place);这个方法是在指定的位置添加组件。

    JFrame的默认布局管理器

GridLayout,网格布局,通过行列,间距,来用网格分割,把组件放入如网格中,先行后列摆放组件。可以保证每个组件的大小都是一样的

    frame.setLayout(new GirdLayout(3,2));  //把容器平均的分为32列,先左后右,先上到下的顺序排列

CardLayout,卡片布局,组件重叠放置。

GridBagLayout,组件可以跨行跨列的网格布局。

 

*** 注意:一定要在图形界面都其他功能都设置好之后才能设置可见性。

 

JButton :按钮

JTextField:单行文本域

JTextArea:多行文本区

JPasswordField:密码输入框

JScrollPane:滚动窗体  使用一个多行文本域作为参数创建滚动窗体

JComboBox:下拉选择框

 

JRadioButton:单选按钮

JCheckBox:多选按钮

JList:多行列表

JLabel:标签

JEditorPane:显示结构化文档

Border:边框

 

JMenuBar:菜单条

JMenu:菜单

JMenuItem:菜单项

JPopupMenu:弹出式菜单

 

JSlider:滑动条

JProgressBar:进度条

JTabbedPane:分层面板

JSplitPane:分隔面板

JToolBar:工具条

 

JFileChooser:文件选择器

JColorChooser:颜色选择器

 

显示对话框

JoptionPane 里面有很多静态方法可以弹出对话框

 

注意:具体的方法可以去参看Java2 SEAPI文档。

 

 

三、awt事件模型(观察者模式)(重点)

 

事件模型中,包括事件源对象,事件处理者(事件监听者对象),事件对象。

 

事件源和事件处理者之间建立了授权注册关系,也就是在事件源类中有一个事件处理者的对象作为属性,也可能是一个事件处理者的集合。

      

          事件对象

事件源————————〉事件处理者

 

这就是事件模型的机制,也就是由事件源对象发送一个消息(事件对象),然后事件处理者调用相应的方法处理事件。

 

在事件监听器接口中定义的方法,都要以事件对象为参数。

 

***   一个事件源可以注册多个同类型的监听器,也可以注册多种多个事件监听器,

      一个事件监听器也可以为多个事件源服务。

 

事件对象继承自EventObject类,并可以通过getSource()方法获得事件源对象,当然需要在构造事件对象时将事件源对象传入,来区分是哪个事件源发出的事件,所以要用事件对象作为参数。

 

事件源,事件对象,监听接口,在java.awt包中提供了很多已经定义好的,只需要实现监听接口就好了。

 

什么是发消息:

ABC三个类,分别作为事件源,事件处理者,事件对象。

A类中有一个B类的属性或者是一个内容为B类对象的集合,也就是事件源和事件处理者之间的建立了授权关系,

B类需要实现一个自定义的接口,这个自定义的接口继承了EventListenerEventListener接口中没有定义任何方法,这只是一个标记接口。

实现在自定义接口中定义好的用于事件处理的方法,C类要继承EventObject类。

这些方法是以事件对象为参数的b(C c),而后在Aa(C c)方法中使用B类的对象调用B类中的b(C c)方法,

并把事件对象作为参数,并在main方法中用A类的对象调用了a(c)方法,这也就叫做A类对象给B类发送了消息。

 

也就是说事件源对象间接调用了事件监听器的方法,并以事件对象为实参传到事件监听器的方法中,要就叫事件源给事件监听器的方法发了一个消息(事件对象)。

 

 

例子如下:

import java.util.*;

//事件源类

class A{

         private String test;

         private List li=new ArrayList();

         public A(String test){

                   this.test=test;

         }

         public String getTest(){return this.test;}

         public void addB(B b){

                   this.li.add(b);

         }

         public void removeB(B b){

                   this.li.remove(b);

         }

       /*

       * 所谓的事件源给事件监听器,发送事件对象。

       * 其实就是事件源用事件为参数,调用时间监听器的相应方法

       */

         public void fire(){

                   C c=new C(this);

                   Iterator it=li.iterator();

                   while(it.hasNext()){

                            B b=(B)it.next();

                            b.b(c);    

                   }

         }     

}

//事件监听器的接口,要继承EventListener标记接口

//监听接口中的每一个方法,都应该以对应的时间对象作为参数

interface Blistener extends EventListener{

         void b(C c);

}

//事件监听器,实现接口

class B implements Blistener{

         public void b(C c){

                   A a=(A)c.getSource();

                   System.out.println(a.getTest()+" "+c.getMessage());

         }

}

/*

事件对象类

事件对象类中要封装事件源对象

*/

class C extends EventObject{

         private String message;

         public C(Object src){

                   super(src);

         }

         public void setMessage(String message){

                   this.message=message;

         }

         public String getMessage(){return this.message;}     

}

public class Test{

         public static void main(String[] args){

                   A a1=new A("Event");

                   B b1=new B();

                   c1.setMessage("Test");

                   a1.addB(b1);//注册监听器

                   a1.fire();//发送事件

         }

}

以上代码只是事例,在引入包之后可以运行。

 

Java的图形编程中,所有动作(事件)都已经提供了相应的事件对象和事件监听接口,例如:实现窗口的关闭按钮,点击关闭按钮会发出相应的事件对象,相应的调用监听器中实现好的方法。

相应的方法清参阅Java2 SE API帮助文档。

 

缺省适配模式,通过一个抽象类实现接口,抽象类中的接口方法实现,都是一个无意义的空实现,可以继承这个抽象类,只覆盖向覆盖的方法就可以了。

 

java.awt.event包中,会有一些适配类,也就是把相应的XXXListener,换成XXXAdapter就是适配类。

java.awt.event包中的ActionEvent类,在以下操作中会发送这个事件,

1JButton组件,按钮被点击

2JTextField组件,在单行文本域中按Enter键。

3JCheckBox组件,选中了复选框。

4JRadioButton组件,选中了单选按钮。

5JMenu组件,选中菜单项。

 

 

作业:

写一个股市类作为事件源,事件源会随机产生波动,写两个监听类,一个会在股市跌的时候卖出,涨的时候买入,另一个投资逻辑刚好相反

                                 CoreJava第十二天    2012-5-18

 

一、复习AWT事件模型(Observer模式)

   1、事件源

   2、事件对象

   3、事件监听器

 

事件源和监听器事先进行授权注册,当事件条件满足时,事件源会给注册的监听器发送一个事件对象,由事件监听器作出相应的处理。

 

一个事件源可以是多种事件的事件源

一个事件源就同一类事件可以注册多个监听器

一个监听器可以同时注册在多个事件源当中

 

事件源和监听器是独立,弱耦合的,是各司其职的

 

事件对象中会封装事件源对象

事件监听接口中的每一个方法都要以事件对象为参数

事件源中要保存和它有监听关系的监听器

事件源给事件监听器发送事件对象:事件源以事件对象作为参数,调用监听器接口的相应方法,通过回调,调用的是不同监听实现类的方法

 

二、

Java的图形编程中,所有动作(事件)都已经提供了相应的事件对象和事件监听接口,

例如:实现窗口的关闭按钮,点击关闭按钮会发出相应的事件对象,相应的调用监听器中实现好的方法。

相应的方法清参阅Java2 SE API帮助文档。

 

缺省适配模式,通过一个抽象类实现接口,抽象类中的接口方法实现,都是一个无意义的空实现,可以继承这个抽象类,只覆盖向覆盖的方法就可以了。

 

java.awt.event包中,会有一些适配类,也就是把相应的XXXListener,换成XXXAdapter就是适配类。

适配类是抽象类,其中对接口XXXListener中的方法进行了空实现,实现这个类,覆盖对自己有用的方法

 

java.awt.event包中的ActionEvent类,在以下操作中会发送这个事件,

1JButton组件,按钮被点击

2JTextField组件,在单行文本域中按Enter键。

3JCheckBox组件,选中了复选框。

4JRadioButton组件,选中了单选按钮。

5JMenu组件,选中菜单项。

 

 

添加事件监听:

1、实现监听接口

2、将监听器对象注册在组件(事件源)

 

ActionEvent 

事件源 --- 组件 JButton 按钮   点击触发ActionEvent 

               JTextField 单行文本域  输入内容以后回车触发ActionEvent 

jtf.getText();  //得到文本域中的内容

 

练习:

1、写一个图形界面,采用BorderLayout布局,中间的部分放置一个可以滚动不可编辑的JTextArea,南面放置一个可以编辑的JTextField,

但在TextField中输入文字并按下回车的时候,文字会添加到TextArea

 

2、为BAM添加用户界面

需要以下几个类:

 

BAMClient 其中会包含一个Frame,这是用户主界面

MainPanel:主界面,用户可以选择开户或者登录

RegisterPanel:用户开户具体用到的界面

LoginPanel:用户登录需要的界面

BusinessPanel:界面上会显示账户的功能 至少包括存款和取款,对于可透支的用户,还允许用户修改透支额度,对于贷款用户,还允许用户贷款和还贷款

 

:本练习的界面布局不做要求,请阅读现有代码,添加事件处理代码

提示:在开户或者登录之后都会跳到BusinessPanel,而用户点击了交易之后,界面停留在BusinessPanel

要随时注意在BusinessPanel上根据数据的变化更新显示信息

 

三、多线程

C++的多进程是OS系统并发的一个任务

Java中没有多进程,一个JVM就是一个进程

 

线程是在进程中并发的一个顺序的执行流程

 

多进程:划分时间片,宏观上并行,微观上串行

多线程:cpu在进程内部再划分时间片

 

CPU ,代码 ,数据

进程:进程间数据独立

线程:数据空间共享,堆空间的共享(堆空间中存放的是对象),栈空间是独立的

所以线程间切换容易,称为轻量级进程

 

一个线程对象代表了一个线程,并非就是一个线程

线程是操作系统中负责维护的资源

java.lang.Thread类的一个对象就代表一个线程

线程是底层OS维护的资源,JVM跑在OS上,在JVM中创建一个Thread对象,调用其start()方法,底层OS会申请一个线程资源,线程对象可到底层管理一个线程

创建好线程之后,把要让线程执行的代码封装到线程对象中(覆盖run()方法)

 

实现线程代码的方式:

1、继承Thread 类,覆盖run()方法

   去底层申请线程并运行,对线程对象调start()方法,main方法是一个主线程

   宏观并行,微观串行

2、实现Runnable接口

  使用多态获得Runnable对象,成为目标对象

  再利用目标对象构造线程对象  Thread t = new Thread(target);

 

 

四、多线程的状态转换图(7状态图)

  见另一文件,名为Thread.pdf

 

 

作业:

用两种方式实现两个线程,一个线程负责打印1-2600,另一个线程打印A-Z,反复打印100

 

CoreJava Day13

 

13.1

   阻塞状态

 

     初始状态                ▲阻塞状态          ▲终止状态

        \                  /           ^ 1           ^

         \                /             \ 2sleep    /

          \start        /                \ 3join   /stop

           \           /                  \       /

            V        V                     \     /

          可运行状态 _ _ _ _ OS选中 _ _ _ _\ ▲运行状态

          (只缺CPU)   \  CPU到期或调用yield

          

    下面为线程中的7中非常重要的状态:(有的书上也只有认为前五种状态:而将“锁池”和“等待池”都看成是“阻塞”状态的特殊情况:这种认识也是正确的,但是将“锁池”和“等待池”单独分离出来有利于对程序的理解)

    1,初始状态,线程创建,线程对象调用start()方法。

    2,可运行状态,也就是等待Cpu资源,等待运行的状态。

    3,运行状态,获得了cpu资源,正在运行状态。

    4,阻塞状态,也就是让出cpu资源,进入一种等待状态,而且不是可运行状态,有三种情况会进入阻塞状态。

      1)如等待数据输入(输入设备进行处理,而CPU不处理),则放入阻塞,直到输入完毕,阻塞结束后会进入可运行状态。

      2)线程休眠,线程对象调用sleep()方法,阻塞结束后会进入可运行状态。

                           public static void sleep(long millis)

                                           throws InterruptedException

                                括号中以毫秒为单位使线程停止一段时间,间隔期满后,线程不一定立即恢复执行。

                                main()运行完毕,即使在结束时时间片还没有用完,CPU也放弃此时间片,继续运行其他程序。

                                       try{

                                                 Thread.sleep(1000);

                                   }catch(InterruptedException e){

                                            e.printStackTrace(e);

                                       }

                                       线程中有异常,只能trycatch,子类中不能抛出比父类更多的异常,父类run方法没有抛出异常。

      3)线程对象2调用线程对象1join()方法,那么线程对象2进入阻塞状态,直到线程对象1中止。

                         public final void join() throws InterruptedException

                                表示其他运行线程放弃执行权,进入阻塞状态,直到调用线程结束。

                                实际上是把并发的线程变为串行运行。

                                               t1 num

                                               t2 char  if(c=='m') -> t1.join()

                                               //t2t1调用joint2进入了阻塞状态

                                               //当条件成立时,t1加入打印数字,一直到打印完,此时t2继续运行

 

    5,中止状态,也就是执行结束。

    6,锁池状态

    7,等待队列

 

13.2 共享数据的并发处理

13.2.1

   数据的错误发生

  多线程并发访问同一个对象(临界资源)

  破坏了原子操作,就会发生数据不一致的情况

 

13.2.2 共享数据的并发处理

   多线程同时并发访问的资源叫做临界资源。

    多个线程同时访问对象并要求操作相同资源时分割了原子操作就会出现问题。(原子操作,不可再分的操作)会出现数据的不一致或数据不完整,为避免这种现象采用对访问的线程做限制的方法。

 

   互斥锁机制,利用每个对象都有一个monitor(锁标记),当线程拥有这个锁标记时才能访问这个资源,没有锁标记便进入锁池。任何一个对象系统都会为其创建一个互斥锁,这个琐是为了分配给线程的,防止打断原子操作。每个对象的锁只能分配给一个线程。

 

   Synchronized用法

    1Synchronized修饰代码块(同步代码块),

              public void push(char c){

                  synchronized(this)//只有持有当前对象锁标记的线程才能访问这个代码块

                          {

                               ...

                  }

              }

 

      对括号内的对象加锁,只有拿到锁标记的对象才能执行该代码块

    2Synchronized修饰方法

                public synchronized void push(char c) {

                     ...

                }

       在整个方法里,对当前对象的加锁,只有拿到锁标记的对象才能执行该方法。

 

  

     初始状态                ▲阻塞状态          ▲终止状态

        \                  /           1           

         \                /             \ 2sleep    /

          \start        /                \ 3join   /stop

           \           /                  \       /

                                       \     /

          可运行状态  _ _ _ OS选中 _ _ _\ ▲运行状态

          (只缺CPU)    \  CPU到期或调用yield

                                            /

                      \                      /

                       \        Synchronized/

                        \                  /

                         \               

                               锁池状态

          

 

    锁池:一个空间,每个对象都有一个,用来存放等待锁标记的线程

    当一个对象中有了锁标记,不会释放其它对象的锁标记。

 

    t1线程正在访问对象O的同步方法时,别的线程t2不能访问O的任何同步方法,但还是可以访问其它的非同步方法

 

   ArrayList Vector

              list

               

            /     \

           /       \

          /         \

      ArrayList    Vector(所有方法都做成了同步)

       

   构造方法   ×对象没有完全构造好了,没有当前对象概念

    抽象方法   ×抽象方法没有代码块,没用

    静态方法   √是对类对象的加锁

         

    ☆注意:构造方法不能Synchronized修饰

    静态方法可以用Synchronized修饰(是对类对象加锁,类对象会在反射时讲到)

    抽象方法不能用Synchronized修饰,不影响子类覆盖,子类在覆盖这个方法是可以加Synchronized,也可以不加Synchronized,所以根据Java不允许写废代码的特点是不能写在一起。

 

 

   练习:

    1、用数组实现个栈;一个线程负责入栈一个线程负责出栈;长度为6

    char[6]

    T1, Push A~Z

    T2, Pop

    长度为6,并且不允许扩充

 

   

-----------------

注意:对当前对象加锁,一个代码块或者方法是同步的(Synchronized),当前对象的锁标记没有分配出去时,有一个线程来访问这个代码块时,就会的到这个对象的锁标记,直到这个线程结束才会释放着个锁标记,其他想访问这个代码块或者是方法线程就会进入这个对象锁池,如果没有得到当前对象的锁标记,就不能访问这个代码块或者是方法。当一个线程想要获得某个对象锁标记而进入锁池,这个线程又持有其他对象的锁标记,那么这个线程也不会释放持有的锁标记。

 

注:方法的Synchronized特性本身不会被继承,只能覆盖。

线程因为未拿到锁标记而发生阻塞进入锁池(lock pool)。每个对象都有自己的一个锁池的空间,用于放置等待运行的线程。由系统决定哪个线程拿到锁标记并运行。

 

使用互斥锁的注意事项

 

举例:男孩和女孩例子,每个女孩是一个对象,每个男孩是个线程。每个女孩都有自己的锁池。每个男孩可能在锁池里等待。

Class Girl{

         Public void hand(){

 

         }

         Public syncronized void kiss(){

 

         }

}

Class Boy extends Thread{

         Public void run(){

                  

         }

}

 

注意:只读不用加同步,只写也不用加同步,只有读写操作兼而有之时才加同步。

 

注意:在java.io包中Vector  HashTable 之所以是线程安全的,是因为每个方法都有synchronized修饰。Static 方法可以加synchronized , 锁的是类对象。但是Vector  jdk 1.0   ArrayList  jdk1.2 所以实际应用还是使用ArrayList

 

注意:内同步,外同步,内同步,即,类内的方法加同步(synchronized)修饰,外同步即,在需要控制只能由一个线程进行访问时,把需要控制的方法写在同步代码块里。

原创粉丝点击