一、继承Thread的方式
1.提供一个继承于Thread类的子类
2.重写Thread类的run():将创建的线程要执行的操作,声明在run()中。
3.实例化Thread子类
4.调用子类对象的start()
Thread类的常用方法的测试
1.run():Thread的子类一定要重写的方法。将此分线程要执行的操作,声明在run()中
2.start():要想启动一个分线程,就需要调用start():①启动线程②调用线程的run()
3.currentThread():静态方法,获取当前的线程
4.getName():获取当前线程的名字
5.setName(String name):设置当前线程的名字
6.yield():当前线程调用此方法,释放CPU的执行权
7.join():在线程a中调用线程b的join()方法:只用当线程b执行结束以后,线程a结束阻塞状态,继续执行。
8.sleep(long millitimes):让当前的线程睡眠millitimes毫秒
9.isAlive():判断当前线程是否存活
10.线程的优先级:
MAX_PRIORITY:10
NORM_PRIORITY:5 —默认优先级
MIN_PRIORITY:1
设置优先级:setPriority(int priority);
获取优先级:getPriority();
设置优先级以后,对高优先级,使用优先调度的抢占式策略,抢占低优先级的执行。但是并不意味着高优 先级的线程一定先于低优先级的线程执行,而是从概率上来讲,概率更大而已。
线程通信:wait() / notify() / notifyAll() —->java.lang.Object类中定义的方法
一:继承Thread的方式
1.提供一个继承Thread类的子类
2.重写Thread类run():将创建的线程要执行的操作,声明在run()中
3.实例化Thread子类
4.调用子类对象的start()方法
创建多线程的方式二:
1.创建一个实现Runnable接口的类
2.实现Runnable的run()
3.创建当前实现类的对象
4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
5.通过Thread类的对象调用其start()
对比继承Thread类和实现Runnable接口的方式
1.联系:public class Thread implements Runnable
2.相同点:启动线程,使用的是同一个start()方法
3.对比:实现Runnable接口要好一些.
原因:①不影响类的继承,因为类是单继承的
②针对于有共享数据的操作,更适合使用Runnable的方式 ,换句话说,实现Runnable接口的方式,实现了代 码和数据的分离
4.面试题:创建多线程有几种方法? 继承Thread类,实现Runnable接口,实现Callable接口,使用线程池
模仿车站买票程序。开启3个窗口卖票。总票数为100张。—–使用实现的方式
1.问题:出现了重票和错票
2.问题出现的原因:一个窗口在没有售完票的情况下,其他的窗口参与进来,操作ticket,导致ticket输出的错 误。
3.解决的方案:当某一个窗口完全操作完ticket以后,其他窗口应该才被允许进来,继续操作ticket。
4.java如何实现的?同步机制:①同步代码块 ②同步方法
4.1 同步代码块:
synchronized(同步监视器){
//需要被同步的代码
}
说明:需要被同步的代码:即为操作共享数据的代码
共享数据:多个线程共同操作的数据。比如:ticket
同步监视器:俗称:锁。 可以由任何一个类的对象充当。 要求:保证多个线程共用同一把锁!
4.2 同步方法:将操作共享数据的方法,声明为同步的。此方法即为同步方法。
使用同步方法解决实现方式的线程安全问题。
1.默认的同步方法(非静态的)的锁是:当前对象,也就是this.
2.默认的同步方法(静态的)的锁是:当前类本身.
使用同步方法解决继承方式的线程安全问题:
注意:继承的方式中,要慎用同步方法。
5.好处:线程的同步机制,解决了线程的安全问题。
6.劣势:在操作共享数据过程中,是单线程的。
模仿车站买票程序。开启3个窗口卖票。总票数为100张 —–使用实现的方式
解决线程安全问题的方式三:ReentrantLock
存在线程的安全问题,使用Lock的方式解决线程安全问题。
面试题:同步的方式和Lock的方式,解决线程安全方面的异同?
同:解决了线程安全问题
异:同步的方式:同步监视器在线程执行完操作共享数据的代码以后,自动释放
Lock的方式:需要显式的调用unlock()方法之后,才能保证其他线程操作共享数据。
死锁:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁,是我们开发中需要规避的!
线程的通信:
wait():一个线程在执行过程中,一旦调用此方法,则此线程进入阻塞状态,等待其他线程来唤醒自己。
notify():一个线程在执行过程中,一旦调用此方法,则会唤醒被wait()的一个线程。高优先级的要优先被唤醒。
notifyAll():一个线程在执行过程中,一旦调用此方法,则会唤醒所有被wait()的线程。
例题:使用两个线程打印 1-100. 线程1, 线程2 交替打印
注意点:1.此三个方法必须使用在同步中。
2.此三个方法的调用者是同步监视器!否则,如果三个方法的调用者不是同步监视器,报异常。
3.此三个方法定义在Object类
面试题:sleep() 和 wait() 的异同?
1.方法声明在哪? Thread:sleep() Object:wait()
2.共同点:使得当前线程进入阻塞状态
3.使用的范围要求:sleep()使用没有情境的要求;wait()必须使用在同步代码块或同步方法中
4.都使用在同步当中的话:wait()需要唤醒:notify()/notifyAll(); sleep():不会释放锁;wait()会释放锁
String:字符串
1.字符串声明的数据,会存储在字符串常量池中。第一次声明时,需要创建相应的字符串。之后,如果声明的变量,其值与之前存在的字符串内容相同,则直接引用现成的字符串。
2.面试题:String s3 = new String(“javaEE”);创建的对象,在内存中生成了几个对象?
String:代表着不可变的字符序列。
1.String类的声明public final class String implements java.io.Serializable, Comparable
①String声明为final,不可被继承。
②实现Serializable:表明String可序列化。浏览器/客户端<—>服务器端 进程1<—->进程2 “{name=Tom,age=12}” JSON:本质就是String
③String重写了hashCode()/equals():常常将Map的key声明为String型。
④实现Comparable接口:String可以比较大小。
⑤实现CharSequence接口:String的底层声明了char[] value数组。
2.如何理解String的不可变性:
①向现有的字符串后添加新的字符串,必须声明新的字符串空间
②将现有的字符串替换为新的字符串,必须声明新的字符串空
③只替换现有字符串中的指定某个字符,也必须声明新的字符串空间
String类与其它结构的转换:
1.String 与包装类、基本数据类型变量间的转换
String–>包装类、基本数据类型:调用包装类Xxx的parseXxx(String s)方法
包装类、基本数据类型 –>String:调用String的valueOf(xxx xxx);
2.String 与 字节数组间的转换
String –> 字节数组:调用String类的getBytes()
字节数组–>String:new String(byte[] buffer,startIndex,length)
3.String 与 字符数组间的转换
String –> 字符数组:调用String类的toCharArray()
字符数组 –>String:new String(char[] cbuf,startIndex,length)
public String substring(int startpoint):返回当前字符串中从startPoint位置开始,到末尾的子字符串。
public String substring(int start,int end):返回当前字符串中从startPoint位置开始,到end结束的左闭右开区间的子字符串。
pubic String replace(char oldChar,char newChar):将字符串中指定的所有oldChar替换为newChar.
public String replaceAll(String old,String new):将字符串中指定的所有old替换为new.
public String trim():去除字符串首尾的空格
public String concat(String str):连接两个字符串
public boolean contains(CharSequence s):判断当前字符串中是否包含s.
public String[] split(String regex)根据给定正则表达式的匹配拆分此字符串。
public int length():返回当前字符串的长度
public char charAt(int index):获取指定索引位置的字符
public boolean equals(Object anObject):比较两个字符串内容是否相等。
public int compareTo(String anotherString):比较两个字符串的大小
public int indexOf(String s):返回s在当前字符串中首次出现的位置。如果不存在,返回-1.
public int indexOf(String s ,int startpoint):
public int lastIndexOf(String s):返回s在当前字符串中末次出现的位置。如果不存在,返回-1.
public int lastIndexOf(String s ,int startpoint):
public boolean startsWith(String prefix):判断当前的字符串是否以指定的prefix字符串开始的
public boolean endsWith(String suffix):判断当前的字符串是否以指定的suffix字符串结束的
public boolean regionMatches(int firstStart,String other,int otherStart ,int length) ?
面试题:
String:不可变的字符序列;底层使用char[]存储
StringBuffer:可变的字符序列;线程安全的,效率低;底层使用char[]存储
StringBuilder:可变的字符序列;线程不安全的,效率高,(jdk 5.0新增);底层使用char[]存储
类比:String —>数组; StringBuffer —>Vector StringBuilder —>ArrayList
ArrayList list = new ArrayList();
list.add(123);//new Object[10];
….
扩容:1.5倍的方式扩容。
这里:
String str = new String();//new char[0];
str.length();
String str1 = new String(“abc”);//new char[]{‘a’,’b’,’c’};
对比:
StringBuffer s1 = new StringBuffer();//char[] value = new char[16]
StringBuffer s2 = new StringBuffer(10);//char[] value = new char[10]
s1.append(“abc”);//value[0] = ‘a’,value[1] = ‘b’,value[2] = ‘c’;
…
每次添加时,都需要判断底层的char[]是否能够盛装下新要添加的字符串。
如果不能盛装下,需要扩容。默认扩容为原来的2倍 + 2.
启示:StringBuffer s1 = new StringBuffer(int capacity);开发中建议使用此构造器。
StringBuffer中的方法:
StringBuffer append(String s),
StringBuffer append(int n) ,
StringBuffer append(Object o) ,
StringBuffer append(char n),
StringBuffer append(long n),
StringBuffer append(boolean n),
StringBuffer insert(int index, String str)
public StringBuffer reverse()
StringBuffer delete(int startIndex, int endIndex):删除当前可变字符串中从startIndex到endIndex结束的左闭右开区间的数据。
public char charAt(int n )
public void setCharAt(int n ,char ch)
StringBuffer replace( int startIndex ,int endIndex, String str)
public int indexOf(String str)
public String substring(int start,int end)
public int length()
总结:
增:append(Xxx xxx)
删:delete(int startIndex, int endIndex
改:setCharAt(int n ,char ch) / replace( int startIndex ,int endIndex, String str)
查:charAt(int n)
插:insert(int index, String str)
长度:length()
遍历:使用for + charAt()