11.线程
a.线程状态图
b 线程创建 继承 Thread 或者 实现Runnable接口
c 懒汉单例
public class Single { private static Single s = null; private Single(){ } public static Single getInstance(){ if(s == null){ synchronized (Single.class) { if(s == null){ s = new Single(); } } } return s; } }
d 线程死锁 同步中嵌套同步容易发生死锁
e 多线程运行安全问题
java解决方案:
同步代码块
同步的前提:
必须有两个或者两个以上的线程;必须是多个线程使用同一个锁
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,消耗资源
12.让线程锁一会儿吧
同步表现形式:
同步代码块;同步函数
同步代码块使用的锁是任意java对象;同步函数使用的锁是this;对应static的同步函数,使用的锁不是this。是类型.class该类的字节码文件。
当线程需要同时持有多个锁时,有可能产生死锁。考虑如下情形:
线程A当前持有互斥所锁lock1,线程B当前持有互斥锁lock2。接下来,当线程A仍然持有lock1时,它试图获取lock2,因为线程B正持有lock2,因此线程A会阻塞等待线程B对lock2的释放。如果此时线程B在持有lock2的时候,也在试图获取lock1,因为线程A正持有lock1,因此线程B会阻塞等待A对lock1的释放。二者都在等待对方所持有锁的释放,而二者却又都没释放自己所持有的锁,这时二者便会一直阻塞下去。这种情形称为死锁。
下面给出一个两个线程间产生死锁的示例,如下:
package com.itheima; public class DeadLock { private String objID; public DeadLock(String id){ objID = id; } public synchronized void checkOther(DeadLock other){ print("entering checkOther"); try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO: handle exception } print("in checkOther() - about to " + "invoke 'other.action()'"); other.action(); print("leaving checkOther()"); } public synchronized void action() { print("entering action()"); try { Thread.sleep(500); } catch ( InterruptedException x ) { } print("leaving action()"); } public void print(String msg) { threadPrint("objID=" + objID + " - " + msg); } public static void threadPrint(String msg) { String threadName = Thread.currentThread().getName(); System.out.println(threadName + ": " + msg); } public static void main(String[] args) { final DeadLock obj1 = new DeadLock("obj1"); final DeadLock obj2 = new DeadLock("obj2"); Runnable runA = new Runnable() { public void run() { obj1.checkOther(obj2); } }; Thread threadA = new Thread(runA, "threadA"); threadA.start(); try { Thread.sleep(200); } catch ( InterruptedException x ) { } Runnable runB = new Runnable() { public void run() { obj2.checkOther(obj1); } }; Thread threadB = new Thread(runB, "threadB"); threadB.start(); try { Thread.sleep(5000); } catch ( InterruptedException x ) { } threadPrint("finished sleeping"); threadPrint("about to interrupt() threadA"); threadA.interrupt(); try { Thread.sleep(1000); } catch ( InterruptedException x ) { } threadPrint("about to interrupt() threadB"); threadB.interrupt(); try { Thread.sleep(1000); } catch ( InterruptedException x ) { } threadPrint("did that break the deadlock?"); threadPrint("no,已经发生了死锁"); } }
如何在一个线程执行过程中加入另一个线程执行,并使当前线程等待。
join:当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。join可以用来临时加入线程执行。
调用线程run方法 和start方法的区别,看一段代码然后分析
class MyThread extends Thread{ public void run(){ try { Thread.currentThread().sleep(3000); } catch (InterruptedException e) { } System.out.println("MyThread running"); } } public class ThreadTest{ public static void main(String argv[]) { MyThread t = new MyThread(); t.run(); t.start(); System.out.println("Thread Test"); } }
/*
代码分析过程:
MyThread t = new MyThread();
创建了一个线程。
t.run();
调用MyThread对象的run方法。
这是只有一个线程在运行就是主线程。
当主线程执行到了run方法中的sleep(3000);时。
这是主线程处于冻结状态。程序并没有任何执行。
当3秒过后,主线程打印了 MyThread running。 run方法执行结束。
t.start();
开启了t线程。
有两种可能情况。
第一种,主线程在只执行了t.start()后,还具有执行权,继续往下执行,
打印了Thread Test。主线程结束。
t线程获取执行权,调用自己的run方法。然后执行的sleep(3000);冻结3秒。
3秒后,打印MyThread running t线程结束,整个程序结束。
第二种情况:
主线程执行到t.start();开启了t线程,t线程就直接获取到了执行权。
就调用自己的run方法。
指定到sleep(3000).t线程冻结3秒,这是t线程就是释放了执行权。
那么主线程开始执行打印了Thread Test,主线程结束。
等到3秒后,t线程打印MyThread running ,然后t线程结束。
程序结束。
如何停止线程?
只有一种,run方法结束。
开启多线程运行,运行代码通常是循环结构。
只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:
当线程处于了冻结状态。
就不会读取到标记。那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。
强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。
Thread类提供该方法 interrupt();
经典线程问题:生产者与消费者
对象分析:生产者-消费者-资源
资源涉及到同步
设计资源对象
class Resource { private String name; private int count = 1; private boolean flag = false; // t1 t2 public synchronized void set(String name) { while(flag) try{this.wait();}catch(Exception e){}//t1(放弃资格) t2(获取资格) this.name = name+"--"+count++; System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name); flag = true; this.notifyAll(); } // t3 t4 public synchronized void out() { while(!flag) try{wait();}catch(Exception e){}//t3(放弃资格) t4(放弃资格) System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name); flag = false; this.notifyAll(); } }
生产者对象
class Producer implements Runnable { private Resource res; Producer(Resource res) { this.res = res; } public void run() { while(true) { res.set("+商品+"); } } }
消费者对象
class Consumer implements Runnable { private Resource res; Consumer(Resource res) { this.res = res; } public void run() { while(true) { res.out(); } } }
测试用例
class ProducerConsumerDemo { public static void main(String[] args) { Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t1 = new Thread(pro); Thread t2 = new Thread(pro); Thread t3 = new Thread(con); Thread t4 = new Thread(con); t1.start(); t2.start(); t3.start(); t4.start(); } }
jdk1.5中提供的多线程升级解决方案:
Lock 和 Condition对象
import java.util.concurrent.locks.*; class ProducerConsumerDemo2 { public static void main(String[] args) { Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t1 = new Thread(pro); Thread t2 = new Thread(pro); Thread t3 = new Thread(con); Thread t4 = new Thread(con); t1.start(); t2.start(); t3.start(); t4.start(); } } /* JDK1.5 中提供了多线程升级解决方案。 将同步Synchronized替换成现实Lock操作。 将Object中的wait,notify notifyAll,替换了Condition对象。 该对象可以Lock锁 进行获取。 该示例中,实现了本方只唤醒对方操作。 Lock:替代了Synchronized lock unlock newCondition() Condition:替代了Object wait notify notifyAll await(); signal(); signalAll(); */ class Resource { private String name; private int count = 1; private boolean flag = false; // t1 t2 private Lock lock = new ReentrantLock(); private Condition condition_pro = lock.newCondition(); private Condition condition_con = lock.newCondition(); public void set(String name)throws InterruptedException { lock.lock(); try { while(flag) condition_pro.await();//t1,t2 this.name = name+"--"+count++; System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name); flag = true; condition_con.signal(); } finally { lock.unlock();//释放锁的动作一定要执行。 } } // t3 t4 public void out()throws InterruptedException { lock.lock(); try { while(!flag) condition_con.await(); System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name); flag = false; condition_pro.signal(); } finally { lock.unlock(); } } } class Producer implements Runnable { private Resource res; Producer(Resource res) { this.res = res; } public void run() { while(true) { try { res.set("+商品+"); } catch (InterruptedException e) { } } } } class Consumer implements Runnable { private Resource res; Consumer(Resource res) { this.res = res; } public void run() { while(true) { try { res.out(); } catch (InterruptedException e) { } } } }
线程总结:
wait和sleep区别
wait:释放cpu执行权,释放同步中锁
sleep:释放cpu执行权,不释放同步中锁
停止线程:
stop过时。
原理:run方法结束。run方法中通常定义循环,指定控制住循环线程即可结束。
1,定义结束标记。
2,当线程处于了冻结状态,没有执行标记,程序一样无法结束。
这时可以循环,正常退出冻结状态,或者强制结束冻结状态。
强制结束冻结状态:interrupt();目的是线程强制从冻结状态恢复到运行状态。
但是会发生InterruptedException异常。
线程中一些常见方法:
setDaemon(boolean):将线程标记为后台线程,后台线程和前台线程一样,开启,一样抢执行权运行,
只有在结束时,有区别,当前台线程都运行结束后,后台线程会自动结束。
join():什么意思?等待该线程结束。当A线程执行到了B的.join方法时,A就会处于冻结状态。
A什么时候运行呢?当B运行结束后,A就会具备运行资格,继续运行。
加入线程,可以完成对某个线程的临时加入执行。
yield:临时暂停,可以让线程是释放执行权。
13.数据类型
基本数据类型 ,基本数据类型包装类 ,自动装箱拆箱,特殊类型字符串
Integer m = 128;
Integer n = 128;
print("m==n:"+(m==n));false 不同的对象引用
Integer a = 127;
Integer b = 127;
print(a == b);true //当数值在byte范围内(0-127),对于新特性,如果该数值已经存在,则不会在开辟新的空间。
String s1 = "abc";//s1是一个类类型变量, "abc"是一个对象。
//字符串最大特点:一旦被初始化就不可以被改变。
String s2 = new String("abc");
//s1和s2有什么区别?
//s1在内存中有一个对象。
//s2在内存中有两个对象。
String a = "opq";
String b = "opq";
System.out.println("a==b:"+(a==b)); true
/*
获取两个字符串中最大相同子串。第一个动作:将短的那个串进行长度一次递减的子串打印。
"abcwerthelloyuiodef"
"cvhellobnm"
思路:
1,将短的那个子串按照长度递减的方式获取到。
2,将每获取到的子串去长串中判断是否包含,
如果包含,已经找到!。
*/
public static String maxSubStr(String s1, String s2){ String maxSub = ""; //得到短的串 //对短的串依次遍历长度依次递减获取字串 String max = s1.length() >= s2.length() ? s1 : s2; String min = max == s1 ? s2 : s1; for (int i = 0; i < max.length(); i++) { for(int j=0,k=min.length()-i ; k != min.length() +1 ; j++,k++){ maxSub = min.substring(j, k); if(max.contains(maxSub)){ System.out.println("同时出现的最大字串:"+maxSub); return maxSub; } } } return maxSub; }
StringBuffer 字符串缓冲区
JDK1.5版本之后出现了StringBuilder
StringBuffer线程同步
StringBuilder线程不同步
开发建议使用StringBuilder
1.提高效率
2.简化书写
3.提高安全性
14.集合
/*
Collection
|--List:元素是有序的,元素可以重复。因为该集合体系有索引。
|--ArrayList:底层的数据结构使用的是数组结构。特点:查询速度很快。但是增删稍慢。线程不同步。
|--LinkedList:底层使用的链表数据结构。特点:增删速度很快,查询稍慢。线程不同步。
|--Vector:底层是数组数据结构。线程同步。被ArrayList替代了。因为效率低。
|--Set:元素是无序,元素不可以重复。、
List:
特有方法。凡是可以操作角标的方法都是该体系特有的方法。
增
add(index,element);
addAll(index,Collection);
删
remove(index);
改
set(index,element);
查
get(index):
subList(from,to);
listIterator();
int indexOf(obj):获取指定元素的位置。
ListIterator listIterator();
List集合特有的迭代器。ListIterator是Iterator的子接口。
在迭代时,不可以通过集合对象的方法操作集合中的元素。
因为会发生ConcurrentModificationException异常。
所以,在迭代器时,只能用迭代器的放过操作元素,可是Iterator方法是有限的,
只能对元素进行判断,取出,删除的操作,
如果想要其他的操作如添加,修改等,就需要使用其子接口,ListIterator。
该接口只能通过List集合的listIterator方法获取。
*/
1,add方法的参数类型是Object。以便于接收任意类型对象。
2,集合中存储的都是对象的引用(地址)
|--Set:元素是无序(存入和取出的顺序不一定一致),元素不可以重复。、
|--HashSet:底层数据结构是哈希表。是线程不安全的。不同步。
HashSet是如何保证元素唯一性的呢?
是通过元素的两个方法,hashCode和equals来完成。
如果元素的HashCode值相同,才会判断equals是否为true。
如果元素的hashcode值不同,不会调用equals。
注意,对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法。
|--TreeSet:
Set集合的功能和Collection是一致的。
15.泛型
泛型:JDK1.5版本以后出现新特性。用于解决安全问题,是一个类型安全机制。
好处
1.将运行时期出现问题ClassCastException,转移到了编译时期。,
方便于程序员解决问题。让运行时问题减少,安全。,
2,避免了强制转换麻烦。
泛型格式:通过<>来定义要操作的引用数据类型。
在使用java提供的对象时,什么时候写泛型呢?
通常在集合框架中很常见,
只要见到<>就要定义泛型。
其实<> 就是用来接收类型的。
当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。
16.Map集合
|--Hashtable:底层是哈希表数据结构,不可以存入null键null值。该集合是线程同步的。jdk1.0.效率低。
|--HashMap:底层是哈希表数据结构,允许使用 null 值和 null 键,该集合是不同步的。将hashtable替代,jdk1.2.效率高。
|--TreeMap:底层是二叉树数据结构。线程不同步。可以用于给map集合中的键进行排序。
"sdfgzxcvasdfxcvdf"获取该字符串中的字母出现的次数。
希望打印结果:a(1)c(2).....
通过结果发现,每一个字母都有对应的次数。
说明字母和次数之间都有映射关系。
注意了,当发现有映射关系时,可以选择map集合。
因为map集合中存放就是映射关系。
因为打印结果的字母有顺序 核心就是说出使用treemap集合
17.数组操作工具类、集合操作工具类、静态导入
高级for循环
格式:
for(数据类型 变量名 : 被遍历的集合(Collection)或者数组)
{
}
对集合进行遍历。
只能获取集合元素。但是不能对集合进行操作。
迭代器除了遍历,还可以进行remove集合中元素的动作。
如果是用ListIterator,还可以在遍历过程中对集合进行增删改查的动作。
传统for和高级for有什么区别呢?
高级for有一个局限性。必须有被遍历的目标。
建议在遍历数组的时候,还是希望是用传统for。因为传统for可以定义脚标。
/*
JDK1.5版本出现的新特性。
方法的可变参数。
在使用时注意:可变参数一定要定义在参数列表最后面。
*/
18.日期类和文件操作
19. 文件包装类
装饰设计模式:
当想要对已有的对象进行功能增强时,
可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。
那么自定义的该类称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象。
并基于被装饰的对象的功能,提供更强的功能。
20. 递归 ini文件 properties
SequenceInputStream