张孝祥java高新视频-笔记

1 eclipse  的基本使用
 
alt+/   将代码快速补全 
windows 首选项 java editor templates 
ctrl+1  快速修复   


* 使用alt+/ 无反应 修改快捷键设置  window首选项 general --- keys 
* 设置代码 保存时进行格式化 windows首选项 java -editor - save actions


代码移动 alt+上下
重置透视图  windows-reset pers...


添加模版
window- preference -java -editor -templates 
其中常用两个变量
${line_selection} 选中的行
${cursor}  光标位置


2 eclipse 调试与运行
双击设置断点
F5跳入 F6跳过 F7跳出
观测变量 选中变量 右键 watch
高版本的java能运行低版本javac编译的程序
但高版本javac编译的程序不一定能在低版本java上运行


3 导入已有工程
File - import - general - existing projects
如果 使用的jdk不一样 ,则在工程右键-buildpath-configure buildpath 
在Libraries中将原有的jdk删除掉,再加入自己的add Library


JDK1.5 新特性:
 
4 导入静态
导入可以使写代码时少写前缀
import 导入某个类或者包下所有类
import static java.lang.Math.*; 导入某个类或者包中的所有静态变量与方法


package itcast.day01;
import static java.lang.Math.*;
public class HelloWorld
{


public static void main(String[] args)
{
int x = abs(1 - 2);
System.out.println(x);
System.out.println(max(1, 2));
}
}


5 可变参数


package itcast.day01;
public class Demo
{


public static void main(String[] args)
{
System.out.println(add(1, 2, 3, 4, 5, 6));
}


public static int add(int... args)
{
int sum = 0;
for (int a : args)
{
sum += a;
}
return sum;
}
}


两点注意的:
1 int... args 就相当于 int[] args ,写法不同用法相同
2 可变参数只能写在最后面 如 int fun(String str ,int... args);  str与args的顺序不能变




6 for 循环增强
for(类型 变量名:集合)
{

}
集合可以是数组 也可以是实现了Iterator接口的集合类


7 自动装箱 开箱


package itcast.day01;


public class Demo
{
public static void main(String[] args)
{
Integer i = 5; // 自动装箱
System.out.println(i + 10); // 自动拆箱


// 享元设计模式
Integer i1 = 123;
Integer i2 = 123;
//自动装箱时 相当于使用了 Integer i2 = Integer.valueOf(123);
System.out.println(i1 == i2); // true
System.out.println(i1.equals(i2));// true


Integer i3 = new Integer(123);
Integer i4 = new Integer(123);
System.out.println(i3 == i4); // false
System.out.println(i3.equals(i4));// true
}
}
享元设计模式: flyweight
小的整数的使用频率很高,不会为每个数都建立一个对象,
因为这样会造成资源浪费
所以自动装箱时 使用了 Integer.valueOf(int x); 来装箱
0-254 的整数自动装箱都使用一个固定的对象 
当然比较大小时 还是用equals方法 


如果有很多很小的对象,都使用一样的对象,那么就可以使用享元设计模式


8 枚举
enum 关键字
枚举值只能放在第一行,
public enum WeekDay
{
SUN, MON, TUE, WED, THI, FRI, SAT;  (其实相当于public static final WeekDay成员变量)
}


1 枚举成员必须写在第一行 
2 只能使用private 构造函数,并且在枚举中可以传入参数
3 使用抽象方法 ,让每个对象实现该方法
4 如果想写单例,可以用枚举只用一个类


package itcast.day01;


public class Demo
{


public static void main(String[] args)
{


WeekDay w = WeekDay.FRI;
// 1 枚举固有方法
System.out.println(w.name()); // 名字 FRI
System.out.println(w.ordinal()); // 序列号 5
System.out.println(WeekDay.valueOf("FRI").name()); // 还可以通过字符串转化成对象
System.out.println("----------------");
for (WeekDay wd : WeekDay.values()) // 获取枚举中所有元素
{
System.out.println(wd.name());
}
System.out.println("----------------");
System.out.println("length=" + WeekDay.values().length);


// 2带有抽象方法的枚举类
TrafficLamp green = TrafficLamp.GREEN;


System.out.println(green.next());
System.out.println("green time is " + green.getTime());


}


public enum WeekDay
{
SUN, MON, TUE, WED(1), THI, FRI, SAT; // 必须在第一行


private WeekDay()
{
System.out.println(this.ordinal() + " no arg");
}


private WeekDay(int a)
{
System.out.println(this.ordinal() + " arg");
}
}


public enum TrafficLamp
{
RED(30) // 该对象传入有参构造函数
{
public TrafficLamp next()
{
return YELLOW;
}
},
YELLOW(2)
{
public TrafficLamp next()
{
return GREEN;
}


},
GREEN(30)
{
public TrafficLamp next()
{
return RED;
}


};


private int time; // 交通灯的时间


private TrafficLamp(int time)
{
// TODO Auto-generated constructor stub
this.time = time;
}


public abstract TrafficLamp next(); // 抽象方法,下一个灯


public int getTime() // 获得交通灯时间
{
return time;
}
}


}


9  Class 类
class 是关键字 
Class 代表java类
它的对象是各个类在内存中的字节码
如何得到各个字节码对应的实例(Class 类型)
类名.class
对象.getClass()
Class.forName("类名")   如 Class.forName("java.util.Date"); 
每个类的字节码在内存中都只有一份,所以比较是否相等直接用==就可以了
 
package itcast.day01;
public class Demo
{


public static void main(String[] args) throws Exception
{
String str1 = "abc";
Class cl1 = String.class;
Class cl2 = str1.getClass();
Class cl3 = Class.forName("java.lang.String");


System.out.println(cl1 == cl2);
System.out.println(cl2 == cl3);
System.out.println(cl1.isPrimitive()); // 是否是基本数据类型 false
System.out.println(int.class.isPrimitive()); // true
System.out.println(int.class == Integer.class); // false
System.out.println(Integer.TYPE == int.class); // true
System.out.println(int[].class.isPrimitive()); // false
System.out.println(int[].class.isArray()); // true //是否是数组
}
}


10 反射  


反射就是把JAVA类中的各中成分映射成相应的类


10.1 Constructor 构造器反射
Constructor 代表类中的构造器
package itcast.day01;


import java.lang.reflect.Constructor;


public class Demo
{


public static void main(String[] args) throws Exception
{
//   构造函数
// 精确获取构造方法 参数中写想获取的构造方法的数据类型的Class对象
// String(StringBuffer buffer)
Constructor<String> strCon = String.class
.getConstructor(StringBuffer.class);
// Constructor<T> 中的 public T newInstance(Object... initargs) 创建实例
String s = strCon.newInstance(new StringBuffer("helloworld"));
System.out.println(s);
System.out.println(s.charAt(2));
}
}


10.2 Field  字段反射
Field 代表类中的字段
package itcast.day01;


public class ReflectPoint
{
private int x;
public int y;


public ReflectPoint(int x, int y)
{
super();
this.x = x;
this.y = y;
}


}


package itcast.day01;


import java.lang.reflect.Field;


public class Demo
{


public static void main(String[] args) throws Exception
{
ReflectPoint pt1 = new ReflectPoint(3, 5);
Field fieldy = pt1.getClass().getField("y"); // getField 只可以得到可见的
// filedy 不是对象身上的变量而是类上的 要用它去取某个对象对应的值
int y = (int) fieldy.get(pt1);
System.out.println(y); // 5


// 暴力反射 1 x是私有 getField拿不到 使用getDeclaredField 强行拿到
Field fieldx = pt1.getClass().getDeclaredField("x");
// 2 拿到这个Field 但是值无法访问 使用setAccessible 强行改变
fieldx.setAccessible(true);
int x = fieldx.getInt(pt1);
System.out.println(x); // 3


}
}


把一个对象中的所有字段中的b都替换成a


package itcast.day01;


public class ReflectPoint
{
public int x = 1;
private int y = 20;
public String str1 = "hellobjava";
public String str2 = "basketball";
public String str3 = "itcast";


public void show()
{
System.out.println("x=" + x + ",y=" + y + ",str1=" + str1 + ",str2="
+ str2 + ",str3=" + str3);
}
}




package itcast.day01;


import java.lang.reflect.Field;


public class Demo
{


public static void main(String[] args) throws Exception
{
ReflectPoint pt1 = new ReflectPoint();
Field[] fields = ReflectPoint.class.getDeclaredFields();
for (Field f : fields)
{
if (f.getType() == String.class)
{
if (!f.isAccessible())
{
f.setAccessible(true);
}


String str = (String) f.get(pt1);
str = str.replace('b', 'a');
f.set(pt1, str);
}
}
pt1.show();
}
}


10.3 Method 方法反射
Method 代表类中的方法


//1 调用类中的方法
package itcast.day01;


import java.lang.reflect.Method;


public class Demo
{


public static void main(String[] args) throws Exception
{
// Method getMethod(String name, Class<?>... parameterTypes)
// char charAt(int index)
Method charAt = Class.forName("java.lang.String").getMethod("charAt",
int.class); // 方法名称 和 参数类型对象的Class
String str = "abcdefg";
// public Object invoke(Object obj, Object... args) 调用方法
char ret = (char) (charAt.invoke(str, 1)); // 使用的对象 和参数
// 如果是静态方法对象的位置写null
System.out.println(ret);
}
}


 2 调用main方法
 两个注意点:
 1 main是静态方法  对象填null
 2 main的参数是String[] 
 invoke方法在 JDK1.5
public Object invoke(Object obj, Object... args)  
 invoke方法在 JDK1.4
public Object invoke(Object obj, Object[] args)  
在1.5中会兼容1.4方法,如果你传入一个数组,会被自动拆包成为Object...
因此就会传入多个参数,导致想接收一个String[]参数的main出现参数错误
所以将数组转化为Object类型,就不会出现拆成多个元素的情况了.
或者将数组作为元素放入Object[]数组中 
(Object)(new String[]{"123","abc"})
new Object[]{new String[]{"123","abc"}};




package itcast.day01;
import java.lang.reflect.Method;
public class Demo
{
public static void main(String[] args) throws Exception
{
//使用参数传入调用的类名名字 在右键-Run as-Run Configure中设置itcast.day01.TestArguments
Class ta = Class.forName(args[0]);
Method ta_main = ta.getMethod("main", String[].class);
String[] ta_args = { "abc", "456" };
ta_main.invoke(null, (Object) ta_args); //调用main
}
}


class TestArguments
{
public static void main(String[] args)
{
for (String arg : args)
{
System.out.println(arg);
}
}
}


记录下我容易写错的地方
数组的三种写法
int[] a1 = new int[3];
int[] a2 = new int[] { 1, 2, 3 };
int[] a3 = { 1, 2, 3 };
错误的写法
int[] a4 = new int[3]{1,2,3}; 错误
写了new int[3] 就代表初始化为0了再到后面初始化就错了


10.4 反射数组
数组的类型相同维度相同 字节码是同一份


java.util.Arrays   操作数组的工具类 搜索和排序
java.reflect.Array  反射中操作数组的类 set 和 get




package itcast.day01;
package itcast.day01;


import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.*;


public class Demo
{


public static void main(String[] args) throws Exception
{
int[] a1 = new int[3];
int[] a2 = new int[4];
int[][] a3 = new int[3][2];
String[] a4 = new String[] { "abc", "123" };
System.out.println(a1.getClass().getName()); // [I
System.out.println(a2.getClass().getName()); // [I
System.out.println(a3.getClass().getName()); // [[I
System.out.println(a4.getClass().getName()); // [Ljava.lang.String;
System.out.println(a1.getClass() == a2.getClass()); // true
System.out.println(a1.getClass().getSuperclass().getName()); //Object 
System.out.println(a3.getClass().getSuperclass().getName()); //Object   
System.out.println(a4.getClass().getSuperclass().getName()); //Object 


Object o1 = a1;
Object o2 = a4;


// Object[] o3 = a1;
Object[] o4 = a4;


System.out.println(a1);
System.out.println(a4);


System.out.println(Arrays.asList(a1)); // [[I@1b15692]
System.out.println(Arrays.asList(a4)); // [abc, 123]


printObject(o1);
printObject(o4);
printObject("xcsds");


// 获得
getType(o1);
getType(o4);


}


// 利用反射 打印数组
private static void printObject(Object obj)
{
Class clasz = obj.getClass();
if (clasz.isArray())
{
int len = Array.getLength(obj);
for (int i = 0; i < len; i++)
{
System.out.println(Array.get(obj, i));


}


} else
{
System.out.println(obj);
}
}


//打印数组元素类型
private static void getType(Object obj)
{
Class clasz = obj.getClass();
if (clasz.isArray())
{
System.out.println(Array.get(obj, 0).getClass().getName());
} else
{
System.out.println(clasz.getName());
}
}


}




10.5 反射类的作用->实现框架
别人先写好程序,你在配置文件中写入你想加载的类,如此来




package itcast.day01;


import java.util.Collection;
import java.util.Properties;
import java.io.*;


public class Demo
{


public static void main(String[] args) throws Exception
{
// 三种加载配置文件的方法:


InputStream in = new FileInputStream("config.properties");


Properties prop = new Properties();
prop.load(in);
in.close();


// 使用反射来调用类
String className = prop.getProperty("className");


Collection collections = (Collection) Class.forName(className)
.getConstructor().newInstance();


ReflectPoint p1 = new ReflectPoint(1, 2);
ReflectPoint p2 = new ReflectPoint(3, 4);
ReflectPoint p3 = new ReflectPoint(1, 2);


collections.add(p1);
collections.add(p2);
collections.add(p3);


System.out.println(collections.size());


}
}


11 内省introspector-->对javaBean进行操作-->特殊的Java类


JavaBean是一种特殊的java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有字段
且方法名符合某种命名规则
如果两个模块之间要传递多个信息,则将这些值封装在javabean中


要将所有字段 写成get 和 set的形式


class Person
{
int x;
public int getAge()
{
return x;
}

public void setAge(int age)
{
x = age;
}
}


javabean的属性名 :根据方法而不是根据字段
 去掉get set -> 如果有第二个字母,其是小的,则首字母改小写->age属性
getAge-> age属性
getTIME ->TIME属性
getX -> X属性
 
使用右键->source->generate getter setter来产生源代码


用内省操作javabean
//将ReflectPoint写成javabean格式
package itcast.day01;


public class ReflectPoint
{
private int x;
private int y;


public ReflectPoint(int x, int y)
{
super();
this.x = x;
this.y = y;
}


public int getX()
{
return x;
}


public void setX(int x)
{
this.x = x;
}


public int getY()
{
return y;
}


public void setY(int y)
{
this.y = y;
}


public void show()
{
System.out.println("x=" + x + ",y=" + y);
}
}


//用内省进行操作


package itcast.day01;


import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;


public class Demo
{


public static void main(String[] args) throws Exception
{
ReflectPoint p1 = new ReflectPoint(1, 2);
String propertyName = "X";
PropertyDescriptor pd = getProperty(p1, propertyName);
setProperty(p1, pd, 10);
}


private static PropertyDescriptor getProperty(Object p1, String propertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException
{
PropertyDescriptor pd = new PropertyDescriptor(propertyName,
p1.getClass()); // 属性名字 和 所属Class
Method methodGetX = pd.getReadMethod();
Object retVal = methodGetX.invoke(p1); // 调用方法 : 对象 和 参数
System.out.println((int) retVal);
return pd;
}

private static void setProperty(Object p1, PropertyDescriptor pd, int data)
throws IllegalAccessException, InvocationTargetException
{
Method methodSetX = pd.getWriteMethod();
methodSetX.invoke(p1, data);
System.out.println(((ReflectPoint) p1).getX()); // 打印10
}



}


//另一种获取PropertyDescriptor的方法: 比new PropertyDescriptor要麻烦些
private static PropertyDescriptor getProperty(Object p1, String propertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException
{
BeanInfo beanInfo = Introspector.getBeanInfo(p1.getClass());
// 通过BeanInfo 只能获得所有的属性描述符 所以要用循环
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds)
{
if (pd.getName().equals(propertyName))
{
Method methodGetX = pd.getReadMethod();
int x = (int) (methodGetX.invoke(p1));
System.out.println(x);
return pd;
}
}
return null;
}

12 Beanutils工具包
如何导入Beanutils工具包?
1 在commons-beanutils-1.8.3-bin.zip下载commons-beanutils-1.8.3-bin.zip 
2 解压
3 在工程下新建文件夹 lib(非必须)
4 拷贝commons-beanutils-1.8.3.jar 于lib处右键 粘贴
5 选中commons-beanutils-1.8.3.jar 右键 addto buildpath 出现奶瓶成功


但是使用时还需要一个日志包 否则会报异常
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
需要commons-logging 包
1 去http://commons.apache.org/proper/commons-logging/download_logging.cgi下载
2 解压
3 拷贝commons-logging-1.1.3.jar 到lib下
4 选中commons-logging-1.1.3.jar 右键 addto buildpath 出现奶瓶成功


使用 Beanutils 操作属性


package itcast.day01;


import java.util.Date;


public class ReflectPoint
{


private int x;
private int y;
private Date brithday;


public ReflectPoint(int x, int y)
{
super();
this.x = x;
this.y = y;
brithday = new Date();
}


public int getX()
{
return x;
}


public void setX(int x)
{
this.x = x;
}


public int getY()
{
return y;
}


public void setY(int y)
{
this.y = y;
}


public Date getBrithday()
{
return brithday;
}


public void setBrithday(Date brithday)
{
this.brithday = brithday;
}


public void show()
{
System.out.println("x=" + x + ",y=" + y);
}
}






package itcast.day01;


import java.util.Date;
import java.util.HashMap;
import java.util.Map;


import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;


public class Demo
{


public static void main(String[] args) throws Exception
{


ReflectPoint p1 = new ReflectPoint(1, 2);
String propertyName = "x";


// 使用beanUtils 更简单的内省方法 beanUtils 所有的值都是String
// static String getProperty(Object bean, String name)
System.out.println(BeanUtils.getProperty(p1, propertyName)); // 1
// static void setProperty(Object bean, String name, Object value)
BeanUtils.setProperty(p1, propertyName, "10");
System.out.println(BeanUtils.getProperty(p1, propertyName)); // 10
System.out.println(BeanUtils.getProperty(p1, "class")); // 默认有个class属性


// 还可以使用级联设置 因为Date中也有time属性
BeanUtils.setProperty(p1, "brithday.time", "111");
System.out.println(BeanUtils.getProperty(p1, "brithday.time"));


// javabean 转 map
Map<String, String> map = BeanUtils.describe(p1);
for (Map.Entry<String, String> entry : map.entrySet())
{
System.out.println(entry.getKey() + " " + entry.getValue());
}


// 将一个map转bean
ReflectPoint p2 = new ReflectPoint(0, 0);
Map<String, String> map2 = new HashMap<String, String>();
map2.put("x", "1000");
map2.put("y", "2000");
map2.put("birthday.time", "10000000");
BeanUtils.populate(p2, map2);
System.out.println(p2.getX());


// 还可以利用beanutil操作map
BeanUtils.setProperty(map2, "x", "2000");
System.out.println("before copyProperties " + map2.get("x"));


// 拷贝 将p1中的值拷贝到p2中
BeanUtils.copyProperties(p2, p1);
System.out.println("after copyProperties " + p2.getX());
// 当然 这是浅拷贝 ,使用引用数据类型的属性 ,可得注意了
p1.getBrithday().setTime(111111111);
System.out.println(p2.getBrithday().getTime()); // 111111111
// p1与 p2的birthday使用的是同一个对象,改变p1中的date对象 p2中的也改变了


// PropertyUtils 会保留原有的类型 不会自动将数据转换为String
// 这样子速度变快 但由于各属性数据类型不一致 就没有和Map的相互转换了
PropertyUtils.setProperty(p1, "x", 1); // 写字符串报错
int x = (int) PropertyUtils.getProperty(p1, "x");
System.out.println(x);


}
}


13  基本注解
1 标记某个方法过时
@Deprecated
public static void sayHello()
{
System.out.println("hi,传智");
}
调用过时方法时编译器会有警告

2 标记某个方法覆盖父类方法

如果没覆盖 会报错 ,可以用来检查是否覆盖成功


3  @SuppressWarnings("deprecation")
使用过时方法时不警告


注解相当于一种标记,加了注解就等于为程序打上了某种标记




14 注解的定义与应用
先说两个元注解,它们用来修饰注解类


@Retention(RetentionPolicy.RUNTIME)
表明注解的声明周期
RetentionPolicy.SOURCE 注解保留到源程序阶段
RetentionPolicy.CLASS 注解保留到class文件
RetentionPolicy.RUNTIME 注解保留到运行时 


@Target( ElementType.TYPE ) 
 ElementType.METHOD 修饰方法
 ElementType.TYPE  修饰类
 或者想多写几个 用ElementType[] 数组来表示
@Target({ ElementType.METHOD, ElementType.TYPE }) 
所有的字节有
CONSTRUCTOR 
          构造方法声明 
FIELD 
          字段声明(包括枚举常量) 
LOCAL_VARIABLE 
          局部变量声明 
METHOD 
          方法声明 
PACKAGE 
          包声明 
PARAMETER 
          参数声明 
TYPE 
          类、接口(包括注释类型)或枚举声明 
 
14.1 定义注解 使用注解 通过反射调用注解\




package itcast.day02;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


import javax.lang.model.element.Element;


//定义一个注解类
//元注解   声明注解声明周期  一直保持到运行期间
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface itcastAnnocation
{
String color(); // 属性
String value();
}


package itcast.day02;


//加了@注解 就相当于加入了一个对象 // 添加注解 且为属性设值
@itcastAnnocation(color = "red", value = "123") 
public class AnnocationTest // 使用注解的类
{
// main方法 静态的 不在这个类中
public static void main(String[] args)
{
// 反射调用注解
if (AnnocationTest.class.isAnnotationPresent(itcastAnnocation.class))
{
itcastAnnocation annocation = AnnocationTest.class
.getAnnotation(itcastAnnocation.class);
System.out.println(annocation);
// 获取注解中的属性 还是像方法一样调用
System.out.println(annocation.color());
}
}
}


14.2 默认值与value属性的特殊之处
设置默认值
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface itcastAnnocation
{
String color() default "red"; 
String value();
}


如果只有value属性需要设置值时 (只有value一个属性,或者其他属性有默认值)
package itcast.day02;
@itcastAnnocation("123")   //可以不写value 直接赋值  ,就如@Retention(RetentionPolicy.RUNTIME)
public class AnnocationTest 
{
public static void main(String[] args)
{
if (AnnocationTest.class.isAnnotationPresent(itcastAnnocation.class))
{
itcastAnnocation annocation = AnnocationTest.class
.getAnnotation(itcastAnnocation.class);
System.out.println(annocation);
System.out.println(annocation.value());
}
}
}


14.3 数组属性
定义:int[] arr();
传值:
@itcastAnnocation(color = "red", value = "123", arr = { 1, 2, 3, 4 }) //如果数组只有一个值 可以省略大括号,就如Target里面一样 它本身是 @Target(ElementType[]) 
取值:
int[] arr = annocation.arr();


注解类型和枚举类型不想写了 太麻烦


15 泛型
1 集合实际上本身可以放不同的数据类型
ArrayList c3 = new ArrayList();
c3.add(123);
c3.add("abc");
System.out.println(c3);
这样实际也是正确的
 
2 泛型的好处 集合使用了泛型后,编译时会严格检查,有效的避免了运行期的ClassCastException,提高了程序的安全性和健壮性。 
此外,泛型消除了绝大多数的类型转换
ArrayList<Integer> c1 = new ArrayList<Integer>();


3 注意泛型是给编译器看的,使用反射可以绕过泛型


package itcast.day01;


import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;


public class Demo
{


// @SuppressWarnings("deprecation")
public static void main(String[] args) throws Exception
{
ArrayList<Integer> c1 = new ArrayList<Integer>();
ArrayList<String> c2 = new ArrayList<String>();


// 发现c1 和 c2的字节码类型是一样的
System.out.println(c1.getClass());// class java.util.ArrayList
System.out.println(c2.getClass());// class java.util.ArrayList
System.out.println(c1.getClass() == c2.getClass());


// c1.add("abc"); //因为使用了泛型,编译器报错

//使用反射操作集合,反射是运行期间的事情
Method add = c1.getClass().getMethod("add", Object.class);
add.invoke(c1, "abc");
add.invoke(c1, 12);
System.out.println(c1.get(0));
System.out.println(c1.get(1));


// 对c1的反射操作和不使用泛型是一样了
ArrayList c3 = new ArrayList();
c3.add(123);
c3.add("abc");
System.out.println(c3);
}
}


这个实验中同时得出了一个小知识点
反射带泛型的方法时,参数写Object.class
 boolean add(E e) 
 
 术语:
 原始类型 (原来的)
 参数化类型 (泛型)
 
4 参数化类型不考虑继承关系 
Vector<String> v = new Vector<Object>(); //错误
Vector<Object> v = new Vector<Integer>(); //(经常)错误


5 创建数组实例时,数组元素不能使用参数化类型
Vector<Interger> vs[]  = new Vector<Integer>[10]; //报错


6 这是可行的
Vector v = new Vector<Object>(); //考虑到版本前后兼容性,只有警告


7 ?通配符
当我要创建一个打印所有变量的
private static void printCollection(Collection<Object> cols)
{
//在printCollection中可以使用和类型有关的方法
/但cols = new Collection<String>(); 是错误的
}
当我使用 printCollection(new ArrayList<Integer>)时会报错
因为参照第4中,参数化类型是不考虑继承关系的


下面才是正确的,?通配符可以匹配任意类型
private static void printCollection(Collection<?> cols)
{
}
但使用?通配符也有限制:
即不能使用和参数化有关的方法
比如 cols.add("abc") 是不可以的  
但使用 cols.size() 是可以的
cols = new Collection<String>(); 是可以的


8 ?通配符的扩展
Vector<? extends Number> x = new Vector<Integer>(); //Number以及Number子类
Vector<? super Integer> x = new Vector<Number>();


限定通配符总是包括自己
现在就能使用参数化有关的方法了






9 通过反射获得泛型的实际类型参数
首先我们知道,泛型在执行时是去掉了实际类型参数的,
即Vector<Date> v 和 Vector<Integer> v1 的字节码对象是同一个
所以不能通过泛型对象来获得实际参数类型
只能通过反射方法来获得参数类型,通过这点转化为泛型类型再获得实际类型参数




 package itcast.day01;


import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;


import java.util.*;


public class Demo
{


// @SuppressWarnings("deprecation")
public static void main(String[] args) throws Exception
{
Method applyMethod = Demo.class.getMethod("applyVector", Vector.class);
// Type 是通用类型
Type[] types = applyMethod.getGenericParameterTypes(); // 得到参数类型
System.out.println(types[0]);
// 实际上我知道返回值是个泛型类型,所以做个类型转换 .///Parameterized 参数化
ParameterizedType pType = (ParameterizedType) (types[0]); //转化为参数化类型
System.out.println(pType.getRawType()); // 获取参数化类型的原始类型
System.out.println(pType.getActualTypeArguments()[0]); // 获取参数化类型的实际参数类型
// 返回时也是一个数组 
//因为如Map等有2个参数


}


public static void applyVector(Vector<Date> v)
{
}
 






16 类加载器
BootStrap 加载JRE/lib/rt.jar java的基本类 同时也是加载所有类加载器的类
ExtClassLoader  加载JRE/lib/ext/*.jar 自定义的公用类
AppClassLoader 加载CLASSPATH下的类或者目录  
它们三个是继承关系 从上往下是父到子


注意如果 getClassLoader()返回值是null 说明类加载器是BootStrap
package itcast.day01;
public class Demo
{
public static void main(String[] args) throws Exception
{
System.out.println(Demo.class.getClassLoader().getClass().getName());
// sun.misc.Launcher$AppClassLoader
// 原来它是AppClassLoader加载的


System.out.println(System.class.getClassLoader()); // null
// 对象的加载器为null 说明它的加载器是BootStrap  
}
}
当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
首先当前线程的类加载器去加载线程中的第一个类。
如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。 
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。


每个类加载器加载类时,又先委托给其上级类加载器。
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?




17 写自己的类加载器
先给自己写的类加密 然后自己写个类加载器解密并加载这个类


首先说下加载原理
1 使用自己的加载器继承LoadClass
其对象调用.loadClass(String name)来加载类
2 loadClass之后的第一步是层层由父类来加载这个类,
三个父类会依次在自己指定的范围内查找这个类,如果找到就加载
如果父类找不到,才调用子类方法中重写的findClass来查找类
3 子类查找到了要加载的类之后调用 defineClass来加载,并得到Class对象返回

package itcast.day03;


import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;


public class MyClassLoader extends ClassLoader
{
public static void main(String[] args) throws IOException,
ClassNotFoundException, InstantiationException,
IllegalAccessException
{
if (args.length != 2)
{
System.out.println("参数数目不正确");
return;
}


// 将args[0]中的类 加密 并放到 args[1]文件夹中去
// sypherClass(args);


// 把加密的文件放进 bin下覆盖原来的,发现出现异常 这说明使用原有的加载器加载不了
// Exception in thread "main" java.lang.ClassFormatError: Incompatible
// magic value....
// System.out.println(new ClassLoaderAttachment().toString());


Class clasz = new MyClassLoader(
"E:\\eclipse-workspace\\javaenhance\\itcastlib")
.loadClass("ClassLoaderAttachment");


Date d1 = (Date) (clasz.newInstance());


System.out.println(d1.toString());


}


private static void sypherClass(String[] args)
throws FileNotFoundException, IOException
{
String srcPath = args[0];
String dstDir = args[1];
String fileName = srcPath.substring(srcPath.lastIndexOf("\\") + 1);
String dstPath = dstDir + File.separator + fileName;


FileInputStream fis = new FileInputStream(srcPath);
FileOutputStream fos = new FileOutputStream(dstPath);


// 给类加密
cypher(fis, fos);
fis.close();
fos.close();
}


@SuppressWarnings("deprecation")
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
{


String fileName = classDir + File.separator + name + ".class";
try
// 由于覆盖父类方法时 子类不能抛出更多的异常 所以只能写 try catch
{
FileInputStream fis = new FileInputStream(fileName);
System.out.println(fileName);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cypher(fis, bos);
byte[] bytes = bos.toByteArray();
fis.close();
return defineClass(bytes, 0, bytes.length);


} catch (IOException e)
{
e.printStackTrace();
}


return super.findClass(name);
}


private String classDir;


public MyClassLoader()
{
}


public MyClassLoader(String classDir)
{
this.classDir = classDir;
}


/* 简单的加密函数 */
public static void cypher(InputStream ips, OutputStream ops)
throws IOException
{
int b = -1;
while ((b = ips.read()) != -1) // read返回值 0 to 255.
{
ops.write(b ^ 0xff);
}
/*
* 再说下read 和 wirte 虽说它们返回值和参数都是整形. 但实际它们读取和写入的都是8位 , read读取
* 到是一个0~255,只有8位 .write 只写入参数的低8位,高位忽略
*/
}
}


18 代理与面向方面的编程
程序中的代理
要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事务管理、等等,你准备如何做?
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。  
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。


系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
                      安全       事务         日志
StudentService  ------|----------|------------|-------------
CourseService   ------|----------|------------|-------------
MiscService     ------|----------|------------|-------------
用具体的程序代码描述交叉业务:
method1         method2          method3
{                      {                       { 
------------------------------------------------------切面
....            ....              ......
------------------------------------------------------切面
}                       }                       }
交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:
------------------------------------------------------切面
func1         func2            func3
{             {                { 
....            ....              ......
}             }                }
------------------------------------------------------切面
使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。




动态代理技术
要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,是不是太累!
JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。


package itcast.day03;


import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Collection;


public class ProxyTest
{
public static void main(String[] args) throws NoSuchMethodException,
SecurityException, InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
/*
* java.lang.reflect.Proxy

* static Class<?> getProxyClass(ClassLoader loader, Class<?>...
* interfaces) 返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。
*/
Class clazzProxy1 = Proxy.getProxyClass(
Collection.class.getClassLoader(), Collection.class);
// 第二个参数是要代理的类的字节码,第一个参数是加载器,通常用这个类常用的加载器 返回值是一个新的代理类的字节码
System.out.println("代理类的名字:--------------------------");
System.out.println(clazzProxy1.getName()); // com.sun.proxy.$Proxy0


// 得到字节码后通过反射获得其所有的方法
// 1 打印构造方法
System.out.println("代理类的构造方法:--------------------------");
Constructor[] cons = clazzProxy1.getConstructors();
for (Constructor con : cons)
{
String name = con.getName();
StringBuilder sb = new StringBuilder(name);
sb.append('(');
Class[] clazzParams = con.getParameterTypes();
for (Class param : clazzParams)
{
sb.append(param.getName() + ",");
}
if (sb != null && sb.charAt(sb.length() - 1) == ',')
{
sb.deleteCharAt(sb.length() - 1);
}
sb.append(')');
System.out.println(sb.toString());
}


System.out.println("代理类的方法:--------------------------");
Method[] methods = clazzProxy1.getMethods();
for (Method method : methods)
{
String name = method.getName();
StringBuilder sb = new StringBuilder(name);
sb.append('(');
Class[] clazzParams = method.getParameterTypes();
for (Class param : clazzParams)
{
sb.append(param.getName() + ",");
}
if (sb != null && sb.charAt(sb.length() - 1) == ',')
{
sb.deleteCharAt(sb.length() - 1);
}
sb.append(')');
System.out.println(sb.toString());
}
System.out.println("方法结束");


/*
* 创建实例对象 首先通过前面知道只有一个构造方法:
* com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler) 需要一个
* InvocationHandler接口 java.lang.reflect 接口 InvocationHandler
* InvocationHandler 是代理实例的调用处理程序 实现的接口。 每个代理实例都具有一个关联的调用处理程序。
* 对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。
* clazzProxy1.newInstance()只能调用无参方法 所以需要使用构造函数
*/
Constructor constructor = clazzProxy1
.getConstructor(InvocationHandler.class);
class MyCollectionInvocationHandler implements InvocationHandler
{


@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
// TODO Auto-generated method stub
return null;
}
}


Collection proxy1 = (Collection) constructor
.newInstance(new MyCollectionInvocationHandler());
System.out.println(proxy1);
proxy1.clear();

}
}


再难点的不想看了 


19 多线程的两种写法


Thread 传入 Runnable对象 就使用Runnable对象的run方法
如果没传入 就使用Thread本身的run方法
 
 
package itcast.day04;
import java.util.Date;
public class ThreadDemo
{
public static void main(String[] args)
{
// 方法一 继承Thread 覆盖run方法
class MyThread extends Thread
{
public void run()
{
while (true)
{
System.out.println(Thread.currentThread().getName()
+ new Date());


try
{
Thread.sleep(1000);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}


}


Thread thread0 = new MyThread();
thread0.start();


// 方法二: 实现Runnable接口 将Runnable对象传入Thread构造方法中
class MyRunnable implements Runnable
{


@Override
public void run()
{
while (true)
{
System.out.println(Thread.currentThread().getName()
+ new Date());
try
{
Thread.sleep(1000);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}


Thread thread1 = new Thread(new MyRunnable());
thread1.start();
}
}




//使用匿名类的写法
 


package itcast.day04;


import java.util.Date;


public class ThreadDemo
{
public static void main(String[] args)
{
// 方法一  
new Thread()   //Thread的匿名子类 对象
{
public void run()
{
while (true)
{
System.out.println(Thread.currentThread().getName()
+ new Date());


try
{
Thread.sleep(1000);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}


}.start();


// 方法二:
new Thread(new Runnable() //Runnable的匿名子类对象
{


@Override
public void run()
{
while (true)
{
System.out.println(Thread.currentThread().getName()
+ new Date());
try
{
Thread.sleep(1000);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();


}
}


20 定时器
Timer 类
每个 Timer 对象相对应的是单个后台线程
默认情况下,任务执行线程并不作为守护线程来运行,所以它能够阻止应用程序终止
通过schedule方法来进行任务调度 可以进行延迟执行或者定义具体时间执行和重复
void schedule(TimerTask task, long delay) 
          安排在指定延迟后执行指定的任务。 
void schedule(TimerTask task, long delay, long period) 
          安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。
通过cancel结束
 
TimerTask接口
由 Timer 安排为一次执行或重复执行的任务。 
实现方法
abstract  void run() 
          此计时器任务要执行的操作。 
通过cancel结束


正在执行的任务需要本次结束后才能被cancel
在run中cancel可以精确地确保本次是最后一次


 10毫秒延迟后开始 每3秒重复一次
package itcast.day04;


import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;


public class TraditionalTimerTest
{


public static void main(String[] args)
{


// 10秒后开始新线程
// Timer.schedule(TimerTask task, long delay)
new Timer().schedule(new TimerTask()
{
int cnt = 0;


@Override
public void run()
{
cnt++;
// TODO Auto-generated method stub
if (cnt == 5)
this.cancel(); // 执行5次后结束任务
System.out.println("task run");
}
}, 10000, 3000); // 10毫秒延迟后开始 每3秒重复一次


while (true)
{
try
{
Thread.sleep(1000);
System.out.println(Calendar.getInstance().get(Calendar.SECOND));
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}


// 如果希望5秒后执行 依次以1秒 3秒的速度重复 如何写呢?
//自定义一个TimerTask子类 在run中嵌套一个定时器


package itcast.day04;


import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;


public class TraditionalTimerTest
{


public static void main(String[] args)
{
new Timer().schedule(new MyTimerTask(), 5000);  


while (true)
{
try
{
Thread.sleep(1000);
System.out.println(Calendar.getInstance().get(Calendar.SECOND));
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}


}
}
//注意 内部类里面不能定义静态变量 所以只能定义在外面
class MyTimerTask extends TimerTask
{
private static int cnt = 0;


@Override
public void run()
{
int delay = 0;
cnt++;
if (cnt % 2 == 0)
{
delay = 1000;
} else
{
delay = 3000;
}
System.out.println("boom");
new Timer().schedule(new MyTimerTask(), delay); //再开始一个定时器
}
}


定时器的开源工具: quarlz


21 线程互斥和同步通信
同步需要使用同一把锁
保护代码块有两种方式:
1 同步代码块 -- 使用指定的对象做为锁 也可以用this
2 同步函数 -- 使用this作为锁
3 同步静态函数 -- 使用对象.class文件做为锁




package itcast.day04;


public class TraditionalThreadSynchronized
{


public static void main(String[] args)
{
final Outputer output = new Outputer();


new Thread(new Runnable()
{


@Override
public void run()
{
while (true)
{
try
{
Thread.sleep(10);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}


output.outputString("abcdefghi");
}


}


}).start();


new Thread(new Runnable()
{


@Override
public void run()
{
while (true)
{
try
{
Thread.sleep(10);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
output.outputString("123456789");
}
}
}).start();


}


}


class Outputer
{
public void outputString(String name)
{
synchronized(this)
{
int len = name.length();
for (int i = 0; i < len; i++)
{
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}


// -------------
静态函数 与 普通函数同步
package itcast.day04;


public class TraditionalThreadSynchronized
{


public static void main(String[] args)
{
final Outputer output = new Outputer();


new Thread(new Runnable()
{


@Override
public void run()
{
while (true)
{
try
{
Thread.sleep(10);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}


output.outputString("abcdefghi");
}


}


}).start();


new Thread(new Runnable()
{


@Override
public void run()
{
while (true)
{
try
{
Thread.sleep(10);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
output.outputString2("mnbvcxz");
}
}
}).start();
}


}


class Outputer
{
String xx = new String();


public void outputString(String name)
{
synchronized (Outputer.class)
{
int len = name.length();
for (int i = 0; i < len; i++)
{
System.out.print(name.charAt(i));
}
System.out.println();
}
}


 
public static void outputString2(String name)
{
int len = name.length();
for (int i = 0; i < len; i++)
{
System.out.print(name.charAt(i));
}
System.out.println();
}


}


22 线程同步与通信
都是Object的方法
wait()
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。换句话说,此方法的行为就好像它仅执行 wait(0) 调用一样。 


public final void notify()唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。


public final void notifyAll()唤醒在此对象监视器上等待的所有线程。线程通过调用其中一个 wait 方法,在对象的监视器上等待。
注意 wait和notify使用在同一个对象上,一般是资源对象this.而不是使用在线程中.


子线程循环10次 主线程循环10次 两者交替执行50次
经验: 1要用到共同数据(包括同步锁)或者共同算法的若干方法应该用到同一个类上.这种设计正好体现了高类聚和程序的健壮性.
2锁是上在代表要操作的资源的类的内部方法中,而不是线程代码中.
3 以上这种题目需要用到条件和锁来实现同步通信.


package itcast.day04;


public class TraditionalThreadCommunication
{
public static void main(String[] args)
{
final Bussiness bus = new Bussiness();
Thread thread = new Thread(new Runnable()
{
@Override
public void run()
{
for (int x = 0; x < 10; x++)
{
bus.sub(10);
}
}
});


thread.start();


for (int x = 0; x < 10; x++)
{
bus.main(10);
}


}
}


class Bussiness //将这个类写成同步的
{
private boolean flag = false;


public synchronized void sub(int cnt) // false时运行
{
if (flag)
{
try
{
this.wait(); //wait时会释放锁  让其他线程进到同一把锁标识的区域中去


} catch (InterruptedException e)
{
e.printStackTrace();
}
}


for (int i = 0; i < cnt; i++)
{
System.out.println("sub " + i);
}
flag = true;
this.notify();


}


public synchronized void main(int cnt)
{
if (!flag)
{
try
{
this.wait();
} catch (InterruptedException e)
{
e.printStackTrace();
}


}


for (int i = 0; i < cnt; i++)
{
System.out.println("main " + i);
}


flag = false;
this.notify();


}


}


23 线程范围内共享变量
ThreadLocal 该类提供了线程局部 (thread-local) 变量
 T get() 
          返回此线程局部变量的当前线程副本中的值。 
protected  T initialValue() 
          返回此线程局部变量的当前线程的“初始值”。 
 void remove() 
          移除此线程局部变量当前线程的值。 
 void set(T value) 
          将此线程局部变量的当前线程副本中的值设置为指定值 
 ThreadLocal 一般使用 Static 修饰 供不同的类或者线程调用
ThreadLocal的实质是一个Map对象,将线程名和值一起存入.
线程结束时,如果没有与当前线程有关变量的引用,就自动清除相关变量.
 
实验案例:定义一个全局共享的ThreadLocal变量,然后启动多个线程向该ThreadLocal变量中存储一个随机值,接着各个线程调用另外其他多个类的方法,这多个类的方法中读取这个ThreadLocal变量的值,就可以看到多个类在同一个线程中共享同一份数据。


package itcast.day04;
import java.util.Random;


public class ThreadLocalTest
{


static ThreadLocal<Integer> x = new ThreadLocal<Integer>();


public static void main(String[] args)
{


for (int i = 0; i < 2; i++)
{
new Thread(new Runnable()
{
@Override
public void run()
{
int data = new Random().nextInt();
System.out.println(Thread.currentThread().getName()
+ " set " + data);
x.set(data);
new A().get();
new B().get();
}
}).start();


}


}


static class A
{
public void get()
{
int data = x.get();
System.out.println(Thread.currentThread().getName() + " get "
+ data);
}


}


static class B
{
public void get()
{
int data = x.get();
System.out.println(Thread.currentThread().getName() + " get "
+ data);
}
}
}


Thread-1 set -604698073
Thread-0 set -1029155777
Thread-1 get -604698073
Thread-0 get -1029155777
Thread-0 get -1029155777
Thread-1 get -604698073






 实现对ThreadLocal变量的封装,让外界不要直接操作ThreadLocal变量。
对基本类型的数据的封装,这种应用相对很少见。
对对象类型的数据的封装,比较常见,即让某个类针对不同线程分别创建一个独立的实例对象。


将ThreadLocal封装在类的内部
package itcast.day04;


import java.util.Random;


public class ThreadLocalTest
{
public static void main(String[] args)
{


for (int i = 0; i < 2; i++)
{
new Thread(new Runnable()
{
@Override
public void run()
{


ThreadScopeData t = ThreadScopeData.getThreadInstance();
t.setAge(new Random().nextInt(100));
t.setName("unkown");


new A().get();
new B().get();


}
}).start();


}


}


static class A
{
public void get()
{
System.out.println(Thread.currentThread().getName() + " get "
+ ThreadScopeData.getThreadInstance().toString());
}
}


static class B
{
public void get()
{
System.out.println(Thread.currentThread().getName() + " get "
+ ThreadScopeData.getThreadInstance().toString());
}
}
}


class ThreadScopeData
{
private String name;
private int age;
private static ThreadLocal<ThreadScopeData> map = new ThreadLocal<ThreadScopeData>();


private ThreadScopeData()
{
}


//注意这个封装方法
public static ThreadScopeData getThreadInstance()
{
ThreadScopeData instance = map.get();
if (instance == null)
{
instance = new ThreadScopeData();
map.set(instance);
}


return instance;
}


public String getName()
{
return name;
}


public void setName(String name)
{
this.name = name;
}


public int getAge()
{
return age;
}


public void setAge(int age)
{
this.age = age;
}


@Override
public String toString()
{
return "ThreadScopeData [name=" + name + ", age=" + age + "]";
}


}


24 线程间共享数据
如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做。
//卖票
package itcast.day04;


import java.util.Random;


public class MutilThreadShareData
{


/**
* @param args
*/
public static void main(String[] args)
{
SharedData sd = new SharedData();
new Thread(sd).start();
new Thread(sd).start();
}


}


class SharedData implements Runnable
{
private int ticketNum = 100;
private boolean sellOut = false;


/*
* public synchronized void increase() { j++; }
*/
public synchronized void sellTicket()
{
if (ticketNum == 0)
{
sellOut = true;
return;
}
System.out.println(Thread.currentThread().getName() + " sell "
+ ticketNum--);
}


@Override
public void run()
{
while (!sellOut)
{
long sleeptime = new Random().nextInt(500);
try
{
Thread.sleep(sleeptime);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
sellTicket();
}


}
}




如果每个线程执行的代码不同,这时候需要用不同的Runnable对象,有如下两种方式来实现这些Runnable对象之间的数据共享:
将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。
 
package itcast.day04;


import java.util.Random;


public class MutilThreadShareData2
{


public static void main(String[] args)
{
final SharedDatas sharedata = new SharedDatas();

// 将共享数据作为参数传入构造函数中来操作
class myRunnableIncrease implements Runnable
{
private SharedDatas sharedata;
private Random random = new Random();


public myRunnableIncrease(SharedDatas sharedata)
{
this.sharedata = sharedata;
}


@Override
public void run()
{
while (true)
{
try
{
Thread.sleep(random.nextInt(2000));
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
sharedata.increase();
}


}


}


class myRunnableDecrease implements Runnable
{
private SharedDatas sharedata;
private Random random = new Random();


public myRunnableDecrease(SharedDatas sharedata)
{
this.sharedata = sharedata;
}


@Override
public void run()
{
while (true)
{
try
{
Thread.sleep(random.nextInt(2000));
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
sharedata.decrease();
}


}


}


new Thread(new myRunnableIncrease(sharedata)).start();
new Thread(new myRunnableIncrease(sharedata)).start();
new Thread(new myRunnableDecrease(sharedata)).start();
new Thread(new myRunnableDecrease(sharedata)).start();


}
}


class SharedDatas
{
private int j = 0;


public synchronized void increase()
{
j++;
System.out.println("increase " + j);
}


public synchronized void decrease()
{
if (j > 0)
{
j--;
System.out.println("decrease " + j);
}


}
}






将这些Runnable对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个Runnable对象调用外部类的这些方法。
上面两种方式的组合:将共享数据封装在另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象身上去完成,对象作为这个外部类中的成员变量或方法中的局部变量,每个线程的Runnable对象作为外部类中的成员内部类或局部内部类。
总之,要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥和通信。
极端且简单的方式,即在任意一个类中定义一个static的变量,这将被所有线程共享。


package itcast.day04;


import java.util.Random;


public class MutilThreadShareData2
{


public static void main(String[] args)
{

SharedDatas sharedata = new SharedDatas();
SharedDatas.Inc in1 = sharedata.new Inc();
SharedDatas.Dec dec1 = sharedata.new Dec();


new Thread(in1).start();
new Thread(in1).start();
new Thread(dec1).start();
new Thread(dec1).start();


}
}


class SharedDatas
{
private int j = 0;
private Random random = new Random();


private synchronized void increase()
{
j++;
System.out.println(Thread.currentThread().getName() + " increase " + j);
}


private synchronized void decrease()
{
if (j > 0)
{
j--;
System.out.println(Thread.currentThread().getName() + " decrease "
+ j);
}
}


public class Inc implements Runnable
{


public void run()
{
while (true)
{
try
{
Thread.sleep(random.nextInt(2000));
} catch (InterruptedException e)
{
e.printStackTrace();
}
increase();
}
}
}


public class Dec implements Runnable
{


public void run()
{
while (true)
{
try
{
Thread.sleep(random.nextInt(2000));
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
decrease();
}
}
}


// 也可以调用这两个函数
public void IncThread()
{
new Thread(new Inc()).start();
}


public void DecThread()
{
new Thread(new Dec()).start();
}


}






25 java5 原子性操作
java.util.concurrent.atomic 
类 AtomicInteger
可以用原子方式更新的 int 值
另外还有其他类型的原子操作 
功能是对多线程的支持


26 线程并发库的应用
1 三种线程池  两个多线程 一个单线程
newFixedThreadPool
newCachedThreadPool
newSingleThreadExecutor


2 shutdown 与 shutdownNow 的区别


package itcast.day04;


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ThreadPoolTest
{


public static void main(String[] args)
{
// 线程池
// 生成3个线程 这个是固定线程池
// ExecutorService threadPool = Executors.newFixedThreadPool(3);
// 将10个任务放入线程池 若池中有空闲线程则执行任务 没有则任务等待有空闲线程再执行


// 动态线程池 当前有几个任务则生成几条线程 该方式自动终止60秒未用的线程
// ExecutorService threadPool = Executors.newCachedThreadPool();


// 单线程 //执行期间线程死了怎么办?注意,如果因为在关闭前的执行期间出现失败而终止了此单个线程,那么如果需要,一个新线程将代替它执行后续的任务
ExecutorService threadPool = Executors.newSingleThreadExecutor();


for (int x = 0; x < 10; x++)
{
final int task = x;
//execute 将任务交给线程池
threadPool.execute(new Runnable()
{
@Override
public void run()
{
for (int i = 0; i < 10; i++)
{
try
{
Thread.sleep(100);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ " loop of " + i + " in " + task);
}


}
});
}


// 当任务运行完了 三个线程仍然会保留 程序不会结束 需要你手动结束线程
// 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。如果已经关闭,则调用没有其他作用
threadPool.shutdown();
// 也可以不管任务完成没有 立即结束
// threadPool.shutdownNow();


}


}


3 定时器启动线程池
Executors 中
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 
          创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 
 
ScheduledExecutorService 中两种调度的方式
 ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) 
          创建并执行在给定延迟后启用的一次性操作。 
 ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) 
          创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;也就是将在 initialDelay 后开始执行,然后在 initialDelay+period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推。 


package itcast.day04;


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;


public class ThreadPoolTest
{


public static void main(String[] args)
{
ScheduledExecutorService scheduleThreadPool = Executors
.newScheduledThreadPool(3);

// 延迟执行
scheduleThreadPool.schedule(new Runnable()
{
@Override
public void run()
{
System.out.println("boom!");
}
}, 3, TimeUnit.SECONDS);



// 延迟 并 周期性执行
scheduleThreadPool.scheduleAtFixedRate(new Runnable()
{


@Override
public void run()
{
System.out.println("boom..");


}
}, 5, 3, TimeUnit.SECONDS);


}
}
 
27 Callable 和 Future
可以拿到返回值的任务.
Future取得的结果类型和Callable返回的结果类型必须一致,这是通过泛型来实现的。
Callable要采用ExecutorSevice的submit方法提交,通过get返回的future对象可以得到结果.


Future
 V get() 
          如有必要,等待计算完成,然后获取其结果。
 
package itcast.day04;


import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;


public class CallableAndFuture
{


public static void main(String[] args)
{
ExecutorService threadPool = Executors.newSingleThreadExecutor();
Future<String> future = threadPool.submit(new Callable<String>()
{
@Override
public String call() throws Exception
{
Thread.sleep(2000);
return "hello";
}
});


System.out.println("等待结果");
try
{
System.out.println(future.get());
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}


}


多个任务一起执行
CompletionService用于提交一组Callable任务,
其take方法返回已完成的一个Callable任务对应的Future对象。


好比我同时种了几块地的麦子,然后就等待收割。收割时,则是那块先成熟了,则先去收割哪块麦子。


package itcast.day04;


import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;


public class CallableAndFuture
{


public static void main(String[] args)
{
ExecutorService threadPool = Executors.newFixedThreadPool(10);


CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(
threadPool);
for (int i = 0; i < 10; i++)
{
final int cnt = i; // 内部类使用局部变量时只能使用final变量
completionService.submit(new Callable<Integer>()
{
@Override
public Integer call() throws Exception
{
Thread.sleep(new Random().nextInt(1000));
return cnt;
}
});
}


for (int i = 0; i < 10; i++)
{
try
{
// Future<V> take()
// 获取并移除表示下一个已完成任务的 Future,如果目前不存在这样的任务,则等待。
System.out.println(completionService.take().get());
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}


}
}




28 锁
java.util.concurrent.locks.Lock;




package itcast.day05;


import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class LockTest
{


public static void main(String[] args)
{
final Output output = new Output();
new Thread()
{
@Override
public void run()
{
try
{
Thread.sleep(10);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
output.out("zhangxiaoxiang");


};


}.start();


new Thread()
{
@Override
public void run()
{
try
{
Thread.sleep(10);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
output.out("lihouming");


};


}.start();


}


}


class Output
{
Lock lock = new ReentrantLock();


public void out(String str)
{
lock.lock(); //锁住之后 
try
{
for (int i = 0; i < str.length(); i++)
{
System.out.print(str.charAt(i));
}
System.out.println();
} finally
{
lock.unlock(); // 解锁一定要放在finally中 以防万一出现异常 导致不能解锁
}
}


}




29 读写锁
读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁


下面的一个程序中有3个线程写 3个线程读  


读取完成之前也会有其他线程写入,导致读取的不是预期的数据
在写好之前会有其他线程写入,还会有其他线程读取,导致混乱.
所以读的时候不能有写,写的时候不能有其他的线程读写.


package itcast.day05;


import java.util.Random;


public class ReadWriteLockTest
{


public static void main(String[] args)
{
final Queue3 queue = new Queue3();
for (int i = 0; i < 3; i++)
{
new Thread()
{
@Override
public void run()
{
while (true)
{
queue.get();
}
}


}.start();


for (int j = 0; j < 3; j++)
{
new Thread()
{
@Override
public void run()
{
while (true)
{


queue.put((new Random().nextInt(1000)));
}
}


}.start();


}
}
}
}


class Queue3
{
private Object data = null;


public void get()
{
System.out.println(Thread.currentThread().getName() + "ready to read:");
try
{
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "get data:"
+ data);
}


public void put(Object obj)
{
System.out
.println(Thread.currentThread().getName() + "ready to write:");
data = obj;
try
{
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}


System.out.println(Thread.currentThread().getName() + "write data:"
+ data);
}
}


hread-0ready to read:
Thread-1ready to write:
Thread-3ready to write:
Thread-2ready to write:
Thread-5ready to write:
Thread-4ready to read:
Thread-7ready to write:
Thread-8ready to read:
Thread-10ready to write:
Thread-6ready to write:
Thread-9ready to write:
Thread-11ready to write:
Thread-10write data:28
Thread-10ready to write:
Thread-11write data:221
Thread-11ready to write:
Thread-10write data:167
...


如果直接上同步锁或者Lock,会导致读的时候不能有其他线程读了.
所以现在用读写锁.
 ReadWriteLock  是个接口 用其子类ReentrantReadWriteLock 来实现


 package itcast.day05;


import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;


public class ReadWriteLockTest
{


public static void main(String[] args)
{
final Queue3 queue = new Queue3();


for (int i = 0; i < 3; i++)
{
new Thread()
{
@Override
public void run()
{
while (true)
{
queue.get();
}
}


}.start();


for (int j = 0; j < 3; j++)
{
new Thread()
{
@Override
public void run()
{
while (true)
{


queue.put((new Random().nextInt(1000)));
}
}


}.start();


}
}
}
}


class Queue3
{
private Object data = null;
ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); //创建读写锁对象


public void get()
{
readWriteLock.readLock().lock(); //读锁上锁: 有读锁的可以同时执行,有写锁的不能同时执行
try
{
System.out.println(Thread.currentThread().getName()
+ "ready to read:");
Thread.sleep(new Random().nextInt(500));
System.out.println(Thread.currentThread().getName() + "get data:"
+ data);
} catch (InterruptedException e)
{
e.printStackTrace();
} finally
{
readWriteLock.readLock().unlock();
}


}


public void put(Object obj)
{
readWriteLock.writeLock().lock(); //上写锁,有读写锁的都不能同时执行.
try
{
System.out.println(Thread.currentThread().getName()
+ "ready to write:");
data = obj;
Thread.sleep(new Random().nextInt(1000));
System.out.println(Thread.currentThread().getName() + "write data:"
+ data);
} catch (InterruptedException e)
{
e.printStackTrace();
} finally
{
readWriteLock.writeLock().unlock();
}


}
}


 
 
Thread-0ready to read:
Thread-8ready to read:
Thread-8get data:null
Thread-0get data:null
Thread-6ready to write:
Thread-6write data:752
Thread-6ready to write:
Thread-6write data:495
,,,




什么时候选择用 ReentrantLock 代替 synchronized
既然如此,我们什么时候才应该使用 ReentrantLock 呢?答案非常简单 —— 在确实需要一些 synchronized 所没有的特性的时候,比如时间锁等候、可中断锁等候、无块结构锁、多个条件变量或者锁投票。 ReentrantLock 还具有可伸缩性的好处,应当在高度争用的情况下使用它,但是请记住,大多数 synchronized 块几乎从来没有出现过争用,所以可以把高度争用放在一边。我建议用 synchronized 开发,直到确实证明 synchronized 不合适,而不要仅仅是假设如果使用 ReentrantLock “性能会更好”。请记住,这些是供高级用户使用的高级工具。(而且,真正的高级用户喜欢选择能够找到的最简单工具,直到他们认为简单的工具不适用为止。)。一如既往,首先要把事情做好,然后再考虑是不是有必要做得更快。




30 Condition 
Condition  要和 Lock 配合使用,可以使程序休眠 也可以在合适的条件下唤醒
这和Synchronized 和 wait notify差不多




//希望主线程和子线程交替运行
package itcast.day05;


import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


//主线程和子线程交替进行
public class ConditionCommunication
{
public static void main(String[] args)
{
final Bussiness bus = new Bussiness();
// 生成子线程
Thread thread = new Thread(new Runnable()
{
@Override
public void run()
{
for (int x = 0; x < 10; x++)
{
bus.sub(10); // 子线程代码
}
}
});


thread.start();


for (int x = 0; x < 10; x++)
{
// 主线程代码
bus.main(10);
}


}
}


class Bussiness // 将这个类写成同步的
{
private boolean flag = false;
private Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();


public void sub(int cnt) // false时运行
{
System.out.println("sub ready to lock");
lock.lock(); //上锁 防止2段程序交叉执行
System.out.println("sub lock");
try
{
while (!flag) // 防止虚假唤醒,其实一般只运行一次
{
try
{
System.out.println(" sub wait ");
condition.await(); // Causes the current thread to wait
// until it is signalled or interrupted.
// 开始休眠 休眠时 同时释放掉了锁
//唤醒后 再执行一次while 确认flag已改变 不再进入循环


} catch (Exception e)
{
e.printStackTrace();
}
}


for (int i = 0; i < cnt; i++)
{
System.out.println("sub " + i);
}
flag = false;
condition.signal(); // 唤醒一个休眠中的线程
} finally
{
System.out.println("sub  unlock");
lock.unlock();


}


}


public void main(int cnt)
{
System.out.println("main ready to lock");
lock.lock();
System.out.println("main  lock");
try
{
while (flag)
{
try
{
System.out.println("main wait ");
condition.await();


} catch (Exception e)
{
e.printStackTrace();
}
}


for (int i = 0; i < cnt; i++)
{
System.out.println("main " + i);
}
flag = true;
condition.signal();
} finally
{
System.out.println("main  unlock");
lock.unlock();
}


}
}


下面是conditon.await() 和  wait() 不同的地方
就是一个锁下可以有多个condition ,于是就可以精确地唤醒某个执行对应代码的线程.


wait与notify只支持一个线程读 一个线程写 ,
若有多个线程在读和写,读线程结束时的signal可能继续唤醒其他读线程.
但是若采用两个condition ,就可以写成这样
 
class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition(); //2个condition
   final Condition notEmpty = lock.newCondition(); 


   final Object[] items = new Object[100];
   int putptr, takeptr, count;


   public void put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length) 
         notFull.await();
       items[putptr] = x; 
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();
     } finally {
       lock.unlock();
     }
   }


   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0) 
         notEmpty.await();
       Object x = items[takeptr]; 
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();
       return x;
     } finally {
       lock.unlock();
     }
   } 
 }
 (ArrayBlockingQueue 类提供了这项功能,因此没有理由去实现这个示例类。) 
 
 
 


//三个线程交替进行
 
package itcast.day05;


import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


//主线程和子线程交替进行
public class ConditionCommunication
{
public static void main(String[] args)
{
final Bussiness bus = new Bussiness();
// 生成子线程


new Thread(new Runnable()
{
@Override
public void run()
{
for (int x = 0; x < 10; x++)
{
bus.first(10); // 假设这是读
}
}
}).start();


new Thread(new Runnable()
{
@Override
public void run()
{
for (int x = 0; x < 10; x++)
{
bus.second(10); // 假设这是写
}
}
}).start();


new Thread(new Runnable()
{
@Override
public void run()
{
for (int x = 0; x < 10; x++)
{
bus.third(10);
}
}
}).start();


}
}


class Bussiness // 将这个类写成同步的
{
private int flag = 1;
private Lock lock = new ReentrantLock();
Condition first_condition = lock.newCondition();
Condition second_conditon = lock.newCondition();
Condition third_conditon = lock.newCondition();


public void first(int cnt) // false时运行
{


lock.lock();


try
{
while (flag != 1) // 防止虚假唤醒,其实一般只运行一次
{
try
{
first_condition.await(); // Causes the current thread to
// wait
// until it is signalled or interrupted.
// 开始休眠 休眠时 同时释放掉了锁


} catch (Exception e)
{
e.printStackTrace();
}
}


for (int i = 0; i < cnt; i++)
{
System.out.println("first " + i);
}
flag = 2;
second_conditon.signal(); // 唤醒一个休眠中的线程
} finally
{
lock.unlock();


}


}


public void second(int cnt)
{


lock.lock();


try
{
while (flag != 2)
{
try
{


second_conditon.await();


} catch (Exception e)
{
e.printStackTrace();
}
}


for (int i = 0; i < cnt; i++)
{
System.out.println("second " + i);
}
flag = 3;
third_conditon.signal();
} finally
{


lock.unlock();
}


}


public void third(int cnt)
{
lock.lock();
try
{
while (flag != 3)
{
try
{
third_conditon.await();


} catch (Exception e)
{
e.printStackTrace();
}
}


for (int i = 0; i < cnt; i++)
{
System.out.println("thrid " + i);
}
flag = 1;
first_condition.signal();
} finally
{


lock.unlock();
}


}
}


// 上面的程序也可以用notifyAll来做
package itcast.day05;


import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


//三个线程交替进行
public class ConditionCommunication
{
public static void main(String[] args)
{
final Bussiness bus = new Bussiness();
// 生成子线程


new Thread(new Runnable()
{
@Override
public void run()
{
for (int x = 0; x < 10; x++)
{
bus.first(10); // 假设这是读
}
}
}).start();


new Thread(new Runnable()
{
@Override
public void run()
{
for (int x = 0; x < 10; x++)
{
bus.second(10); // 假设这是写
}
}
}).start();


new Thread(new Runnable()
{
@Override
public void run()
{
for (int x = 0; x < 10; x++)
{
bus.third(10);
}
}
}).start();


}
}


class Bussiness // 将这个类写成同步的
{
private int flag = 1;
private Lock lock = new ReentrantLock();
Condition first_condition = lock.newCondition();
Condition second_conditon = lock.newCondition();
Condition third_conditon = lock.newCondition();


public synchronized void first(int cnt) // false时运行
{


while (flag != 1)
{
try
{


this.wait();


} catch (Exception e)
{
e.printStackTrace();
}
}


for (int i = 0; i < cnt; i++)
{
System.out.println("first " + i);
}
flag = 2;


this.notifyAll();
}


public synchronized void second(int cnt)
{


while (flag != 2)
{
try
{
wait();


} catch (Exception e)
{
e.printStackTrace();
}
}


for (int i = 0; i < cnt; i++)
{
System.out.println("second " + i);
}
flag = 3;


this.notifyAll();


}


public synchronized void third(int cnt)
{


while (flag != 3)
{
try
{


this.wait();


} catch (Exception e)
{
e.printStackTrace();
}
}


for (int i = 0; i < cnt; i++)
{
System.out.println("thrid " + i);
}
flag = 1;


this.notifyAll();
}


}


31 Semaphore 信号灯
限制文件被同时访问的线程数量


Semaphore可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。
Semaphore实现的功能就类似厕所有5个坑,假如有十个人要上厕所,那么同时能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中的任何一个人让开后,其中在等待的另外5个人中又有一个可以占用了。
另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。
单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。






Semaphore(int permits) 
          创建具有给定的许可数和非公平的公平设置的 Semaphore。
 void acquire()   从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。  
 void release() 
          释放一个许可,将其返回给信号量。 
 
package itcast.day05;


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;


public class SemaphoreTest
{
public static void main(String[] args)
{
ExecutorService threadPool = Executors.newCachedThreadPool();
final Semaphore sp = new Semaphore(3); // 信号量 设定值为3个
for (int i = 0; i < 10; i++)
{
Runnable r = new Runnable()
{


@Override
public void run()
{
try
{
sp.acquire(); // 向信号量请求1个资源
System.out.println("线程"
+ Thread.currentThread().getName() + "已进入,现有"
+ (3 - sp.availablePermits()) + "个线程");
Thread.sleep((long) ((Math.random() * 1000)));
System.out.println("线程"
+ Thread.currentThread().getName() + "即将离开");
sp.release(); // 释放资源
System.out.println("线程"
+ Thread.currentThread().getName() + "已离开,现有"
+ (3 - sp.availablePermits()) + "个线程");


} catch (Exception e)
{
e.printStackTrace();
}


}
};


threadPool.execute(r); // 在线程池中执行任务


}
}
}


32 其他同步工具类
32.1 CyclicBarrier  
一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。 


package itcast.day05;


import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;


public class CyclicBarrierTest
{


public static void main(String[] args)
{
ExecutorService service = Executors.newCachedThreadPool();
// 需要3个线程到达,才会一起被唤醒
final CyclicBarrier cb = new CyclicBarrier(3);


// 这中构造函数 最后一个到达的线程会执行第二个参数的内容
// final CyclicBarrier cb = new CyclicBarrier(3, new Runnable()
// {


// @Override
// public void run()
// {
// System.out.println(Thread.currentThread().getName()
// + "说都到达屏障,大家可以继续了....");
// }
// });


for (int i = 0; i < 3; i++)
{
Runnable runnable = new Runnable()
{
public void run()
{
try
{
Thread.sleep((long) (Math.random() * 10000));
System.out.println("线程"
+ Thread.currentThread().getName()
+ "即将到达集合地点1,当前已有" + cb.getNumberWaiting()
+ "个已经到达,正在等候");
cb.await();


Thread.sleep((long) (Math.random() * 10000));
System.out.println("线程"
+ Thread.currentThread().getName()
+ "即将到达集合地点2,当前已有" + cb.getNumberWaiting()
+ "个已经到达,正在等候");
cb.await(); //可以循环使用
Thread.sleep((long) (Math.random() * 10000));
System.out.println("线程"
+ Thread.currentThread().getName()
+ "即将到达集合地点3,当前已有" + cb.getNumberWaiting()
+ "个已经到达,正在等候");
cb.await();
} catch (Exception e)
{
e.printStackTrace();
}
}
};
service.execute(runnable);
}
service.shutdown();
}
}


32.2 CountDownLatch。
由于调用了一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。 
用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。如果需要重置计数,请考虑使用 CyclicBarrier。 


package itcast.day05;


import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class CountdownLatchTest
{


public static void main(String[] args)
{
ExecutorService service = Executors.newCachedThreadPool();
final CountDownLatch cdOrder = new CountDownLatch(1);
final CountDownLatch cdAnswer = new CountDownLatch(3);
for (int i = 0; i < 3; i++)
{
Runnable runnable = new Runnable()
{
public void run()
{
try
{
System.out.println("线程"
+ Thread.currentThread().getName() + "正准备接受命令");
cdOrder.await();
System.out.println("线程"
+ Thread.currentThread().getName() + "已接受命令");
Thread.sleep((long) (Math.random() * 10000));
System.out
.println("线程"
+ Thread.currentThread().getName()
+ "回应命令处理结果");
cdAnswer.countDown();
} catch (Exception e)
{
e.printStackTrace();
}
}
};
service.execute(runnable);
}
try
{
Thread.sleep((long) (Math.random() * 10000));


System.out.println("线程" + Thread.currentThread().getName()
+ "即将发布命令");
cdOrder.countDown();
System.out.println("线程" + Thread.currentThread().getName()
+ "已发送命令,正在等待结果");
cdAnswer.await();
System.out.println("线程" + Thread.currentThread().getName()
+ "已收到所有响应结果");
} catch (Exception e)
{
e.printStackTrace();
}
service.shutdown();


}
}


32.3 Exchanger
用于实现两个人之间的数据交换,每个人在完成一定的事务后想与对方交换数据,第一个先拿出数据的人将一直等待第二个人拿着数据到来时,才能彼此交换数据。


Exchanger() 
          创建一个新的 Exchanger。
V exchange(V x) 
等待另一个线程到达此交换点(除非当前线程被中断),然后将给定的对象传送给该线程,并接收该线程的对象 
package itcast.day05;


import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ExchangerTest
{


public static void main(String[] args)
{
ExecutorService service = Executors.newCachedThreadPool();
final Exchanger exchanger = new Exchanger();
service.execute(new Runnable()
{
public void run()
{
try
{
Thread.sleep((long) (Math.random() * 10000));
String data1 = "zxx";
System.out.println("线程" + Thread.currentThread().getName()
+ "正在把数据" + data1 + "换出去");
String data2 = (String) exchanger.exchange(data1); //等待把数据交换出去
System.out.println("线程" + Thread.currentThread().getName()
+ "换回的数据为" + data2);
} catch (Exception e)
{


}
}
});
service.execute(new Runnable()
{
public void run()
{
try
{
Thread.sleep((long) (Math.random() * 10000));
String data1 = "lhm";
System.out.println("线程" + Thread.currentThread().getName()
+ "正在把数据" + data1 + "换出去");
String data2 = (String) exchanger.exchange(data1); //等待把数据交换出去
System.out.println("线程" + Thread.currentThread().getName()
+ "换回的数据为" + data2);
} catch (Exception e)
{


}
}
});
}
}
线程pool-1-thread-2正在把数据lhm换出去
线程pool-1-thread-1正在把数据zxx换出去
线程pool-1-thread-1换回的数据为lhm
线程pool-1-thread-2换回的数据为zxx








33 ArrayBlockingQueue<E> 阻塞队列
什么是可阻塞队列,阻塞队列的作用与实际应用,阻塞队列的实现原理。
阻塞队列与Semaphore有些相似,但也不同,阻塞队列是一方存放数据,另一方释放数据,Semaphore通常则是由同一方设置和释放信号量。
 
抛出异常 特殊值 阻塞 超时 
插入 add(e) offer(e) put(e) offer(e, time, unit) 
移除 remove() poll() take() poll(time, unit) 
检查 element() peek() 不可用 不可用 


只有put方法和take方法才具有阻塞功能






//用3个空间的队列来演示阻塞队列的功能和效果。 
package itcast.day05;


import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ArrayBlockingQueueTest
{


public static void main(String[] args)
{
ExecutorService threadPool = Executors.newCachedThreadPool();
final ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(
3);
for (int i = 0; i < 3; i++)
{
Runnable readRun = new Runnable()
{
@Override
public void run()
{
try
{
while (true)
{
Thread.sleep((long) (Math.random() * 1000));
System.out.println(Thread.currentThread().getName()
+ "准备读取队列.");
System.out.println(Thread.currentThread().getName()
+ "取出了" + queue.take()); // 从头部取
if (queue.isEmpty())
{
System.out.println("队列被取空");
}
}
} catch (Exception e)
{
e.printStackTrace();
}


}


};
threadPool.execute(readRun);
}


for (int i = 0; i < 3; i++)
{
Runnable writeRun = new Runnable()
{
@Override
public void run()
{


try
{
while (true)
{
Thread.sleep((long) (Math.random() * 1000));
System.out.println(Thread.currentThread().getName()
+ "准备写入队列.");
int data = new Random().nextInt(100);
queue.put(data); // 插入尾部
System.out.println(Thread.currentThread().getName()
+ "写入" + data);
if (queue.size() == 3)
{
System.out.println("队列已满");
}
}
} catch (Exception e)
{
e.printStackTrace();
}


}


};
threadPool.execute(writeRun);
}


}
}
pool-1-thread-5准备写入队列.
pool-1-thread-5写入23
pool-1-thread-1准备读取队列.
pool-1-thread-1取出了23
队列被取空
pool-1-thread-3准备读取队列.
pool-1-thread-6准备写入队列.
pool-1-thread-6写入19
pool-1-thread-3取出了19
队列被取空
pool-1-thread-4准备写入队列.
pool-1-thread-4写入6
pool-1-thread-5准备写入队列.
pool-1-thread-5写入70
pool-1-thread-2准备读取队列.
pool-1-thread-2取出了6
pool-1-thread-5准备写入队列.
pool-1-thread-5写入52
pool-1-thread-3准备读取队列.
pool-1-thread-3取出了70
pool-1-thread-6准备写入队列.
 ...
 
 //用两个具有1个空间的队列来实现同步通知的功能 
package itcast.day05;


import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ArrayBlockingQueueSynchrnoize
{


public static void main(String[] args)
{
ExecutorService threadPool = Executors.newSingleThreadExecutor();
final ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(
1);
threadPool.execute(new Runnable()
{
@Override
public void run()
{
while (true)
{
try
{
System.out.println("获取到主线程产生的数据" + queue.take());
Thread.sleep((long) (Math.random() * 1000));
int data = (int) (Math.random() * 100);
queue.put(data);
System.out.println("子线程给主线程传输数据" + data);
} catch (Exception e)
{
e.printStackTrace();
}
}


}
});


while (true)
{
try
{
Thread.sleep((long) (Math.random() * 3000));
int data = (int) (Math.random() * 100);
queue.put(data);
System.out.println(" 主线程给子线程传输数据" + data);
System.out.println("获取到子线程产生的数据" + queue.take());


} catch (Exception e)
{
e.printStackTrace();
}
}


}
}
 主线程给子线程传输数据12
获取到主线程产生的数据12
子线程给主线程传输数据94
获取到子线程产生的数据94
 主线程给子线程传输数据63
获取到主线程产生的数据63
子线程给主线程传输数据8
获取到主线程产生的数据8
子线程给主线程传输数据53
获取到子线程产生的数据53
 主线程给子线程传输数据47
获取到主线程产生的数据47
子线程给主线程传输数据60
获取到子线程产生的数据60




34 同步集合
普通的集合是不支持并发访问的,并发读写会发生问题.
 
以前的方法----Collections工具类中的静态方法: 转化为同步集合
 static <T> Collection<T> 
 synchronizedCollection(Collection<T> c) 
          返回指定 collection 支持的同步(线程安全的)collection。 
static <T> List<T> 
 synchronizedList(List<T> list) 
          返回指定列表支持的同步(线程安全的)列表。 
static <K,V> Map<K,V> 
 synchronizedMap(Map<K,V> m) 
          返回由指定映射支持的同步(线程安全的)映射。 
static <T> Set<T> 
 synchronizedSet(Set<T> s) 
          返回指定 set 支持的同步(线程安全的)set。 
static <K,V> SortedMap<K,V> 
 synchronizedSortedMap(SortedMap<K,V> m) 
          返回指定有序映射支持的同步(线程安全的)有序映射。 
static <T> SortedSet<T> 
 synchronizedSortedSet(SortedSet<T> s) 
          返回指定有序 set 支持的同步(线程安全的)有序 set。 


 java5 提供的并发集合
 
 ConcurrentHashMap、 //HashMap的并发集合
 ConcurrentSkipListMap、 //TreeeMap
 ConcurrentSkipListSet、  //类似 TreeSet
 CopyOnWriteArrayList //ArrayList
 CopyOnWriteArraySet //ArraySet  


 
 在用迭代器iterator迭代元素的时候,是不能够直接添加删除元素的,就是说不能进行修改,否则可能会产生异常。移除元素可以使用it.remove()这个方法是标记移除,等遍历完才真正删除。
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值