线程
创建线程的两种方式:
第一种
继承thread类,新建子类
第二种
1 定义类实现Runnable接口
2 覆盖Runnable接口中的run方法
3 通过thread类建立线程
4 将Runnable接口的子类对象当作实际参数传递给thread类的构造函数
5 调用thread类的start方法开启线程。
线程的基本概念、线程的基本状态以及状态之间的关系
一个程序中可以有多条执行线索同时执行,一个线程就是程序中的一条执行线索,每个线程上都关联有要执行的代码,即可以有多段程序代码同时运行,每个程序至少都有一个线程,即main方法执行的那个线程。如果只是一个cpu,它怎么能够同时执行多段程序呢?这是从宏观上来看的,cpu一会执行a线索,一会执行b线索,切换时间很快,给人的感觉是a,b在同时执行,好比大家在同一个办公室上网,只有一条链接到外部网线,其实,这条网线一会为a传数据,一会为b传数据,由于切换时间很短暂,所以,大家感觉都在同时上网。
状态:就绪,运行,synchronized阻塞,wait和sleep挂起,结束。wait必须在synchronized内部调用。
调用线程的start方法后线程进入就绪状态,线程调度系统将就绪状态的线程转为运行状态,遇到synchronized语句时,由运行状态转为阻塞,当synchronized获得锁后,由阻塞转为运行,在这种情况可以调用wait方法转为挂起状态,当线程关联的代码执行完后,线程变为结束状态。
多线程有几种实现方法?同步有几种实现方法?
多线程有两种实现方法,分别是继承Thread类与实现Runnable接口
同步的实现方法有两种,分别是synchronized,wait与notify
wait():使一个线程处于等待状态,并且释放所持有的对象的锁。
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常,sleep不会释放所持有对象的锁。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
举例(实现Runnable接口)
class Ticketimplements Runnable
{
privateintnumber = 100;
Object obj=new Object();
publicvoid run()
{
while(true)
{
synchronized(obj)
{
if(number>0)
{
try{Thread.sleep(10);}
catch(Exception e){}
System.out.println(Thread.currentThread().getName()+" sell ticket"+number--);
}
}
}
}
}
publicclass ThreadDemo {
publicstaticvoid main(String[] args) {
Ticket t=new Ticket();
Thread s1=new Thread(t);
Thread s2=new Thread(t);
Thread s3=new Thread(t);
Thread s4=new Thread(t);
s1.start();
s2.start();
s3.start();
s4.start();
}
}
1.由懒汉式联想到的问题
class SingleimplementsRunnable
{
privatestatic Singles=null;
Single()
{
System.out.println(Thread.currentThread().getName()+"---newSingle()");
}
publicvoid run()
{
while(true)
{
if(s==null)
{
System.out.println(Thread.currentThread().getName()+"---entered,waiting...");
synchronized(Single.class)
{
if(s==null)
{//此处的大括号,用于验证抢到执行权的线程的执行过程,如果有两个线程进入第一层null循环,如果不加此处的大括号,会出现如下结果
main---new Single()
Thread-0---entered,waiting... //这里线程0先进来,拿到锁,在new Single()之后,执行权被Thread-1拿走,但Thread-1没锁,等Thread-0执行完再执行
Thread-0---new Single()
Thread-1---entered,waiting...
Thread-0---entered
Thread-0---out
Thread-1---entered
Thread-1---out
如下结果
main---new Single()
Thread-0---entered,waiting... //这里锁已经被线程0拿到,然后,线程1抢到执行权,因为锁在线程0手里,只能等到线程0执行完,线程1才能执行
Thread-1---entered,waiting...
Thread-0---new Single()
Thread-0---entered
Thread-0---out
Thread-1---entered
Thread-1---out
以及如下结果
main---new Single() //仍然是线程0先拿到执行权并拿到锁,在打印entered之后,线程1进入,无锁,等待线程0执行完再执行
Thread-0---entered,waiting...
Thread-0---new Single()
Thread-0---entered
Thread-1---entered,waiting...
Thread-0---out
Thread-1---entered
Thread-1---out
s=newSingle();
System.out.println(Thread.currentThread().getName()+"---entered");
}
System.out.println(Thread.currentThread().getName()+"---out");
}
}
}
}
}
publicclass LazyTest
{
publicstaticvoidmain(String[]args)throwsException
{
Singlet=new Single();
Threadt1=new Thread(t);
Threadt2=new Thread(t);
t1.start();
t2.start();
}
2.死锁举例
class DeadLock implements Runnable
{
private boolean flag=true;
DeadLock(boolean flag)
{
this.flag=flag;
}
public void run()
{
if(flag)
{
synchronized(Mylock.locka)
{
System.out.println(Thread.currentThread().getName()+"---get locka");
System.out.println(Thread.currentThread().getName()+"---if locka");
synchronized(Mylock.lockb)
{
System.out.println(Thread.currentThread().getName()+"---entered if lockb");
}
}
}
else
{
synchronized(Mylock.lockb)
{
System.out.println(Thread.currentThread().getName()+"---get lockb");
System.out.println(Thread.currentThread().getName()+"---else lockb");
synchronized(Mylock.locka)
{
System.out.println(Thread.currentThread().getName()+"---entered else locka");
}
}
}
}
}
class Mylock
{
static Object locka=new Object();
static Object lockb=new Object();
}
public class JustForTest
{
public static void main(String args[]) throws Exception
{
Thread t1=new Thread(new DeadLock(true));
Thread t2=new Thread(new DeadLock(false));
t1.start();
// Thread.sleep(1);
t2.start();
}
}
打印结果为:
Thread-0---get locka //线程0先抢到执行权,线程0拿到locka锁
Thread-1---get lockb //这时被线程1抢到执行权,线程1拿到lockb锁
Thread-0---if locka //执行权被线程0抢回,并输出if locka,之后线程0要继续执行,则必须拿到锁lockb,因为lockb锁已经被线程1拿到并且方法未执行完,线程1不会释放锁,转到线程1运行
Thread-1---else lockb //线程1运行并打印else lockb,线程1要继续运行,则需拿到locka锁,而locka锁在线程0处,线程0方法未执行完不能释放locka锁。于是线程0和线程1都需要对方的锁才能执行完,而双方因为方法体未执行完都不能释放自己的锁,于是出现了死锁
代码举例(多线程之间通信):
class res
{
private Stringname;
private Stringsex;
booleanflag =false;
publicsynchronizedvoid set(String name,String sex)
{
if(flag)
try{this.wait();}
catch(InterruptedException e){};
this.name=name;
this.sex=sex;
this.flag=true;
this.notify();
}
publicsynchronizedvoid output()
{
if(!flag)
try{this.wait();}
catch(InterruptedException e){};
System.out.println(name+"的性别是---"+sex);
this.flag=false;
this.notify();
}
}
class InPutimplements Runnable
{
private resr;
InPut(res r)
{
this.r=r;
}
inta=0;
publicvoid run()
{
while(true)
{
if(a==0)
{
r.set("Mike","Male");
}
else
{
r.set("莉莉","女");
}
a=(a+1)%2;
}
}
}
class OutPutimplements Runnable
{
private resr;
OutPut(res r)
{
this.r=r;
}
publicvoid run()
{
while(true)
{
r.output();
}
}
}
publicclassInputOutput
{
publicstaticvoid main(String[] args)
{
res r=new res();
new Thread(new InPut(r)).start();
new Thread(new OutPut(r)).start();
}
}
JDK1.5新特性:
提供了多线程升级解决方案,
将同步Synchronized替换成现在的lock操作,
将Object中的wait,notify,notifyall替换成现在的condition对象,
该对象可以对lock锁进行获取
举例
importjava.util.concurrent.locks.*;
class resource
{
private Stringname;
intnum=1;
booleanflag =false;
private Locklock=new ReentrantLock();
private Conditioncondition_pro=lock.newCondition();
private Conditioncondition_con=lock.newCondition();
publicvoid set(String name)throws InterruptedException
{
lock.lock();
try
{
while(flag)
condition_pro.await();
this.name=name+"--"+num++; System.out.println(Thread.currentThread().getName()+"生产者"+this.name);
flag=true;
condition_con.signal();
}
finally
{
lock.unlock();
}
}
publicvoid output()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 Producerimplements Runnable
{
private resourcer;
Producer(resource r)
{
this.r=r;
}
publicvoid run()
{
while(true)
{
try{r.set("商品");}
catch(Exception e){}
}
}
}
class Consumerimplements Runnable
{
private resourcer;
Consumer(resource r)
{
this.r=r;
}
publicvoid run()
{
while(true)
{
try
{
r.output();
}
catch(Exception e){}
}
}
}
publicclass ProducerConsumerDemo
{
publicstaticvoid 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(con);
Thread t3=new Thread(pro);
Thread t4=new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
如何让线程自动结束?代码如下(在main函数中控制,让run方法停下)
class Runimplements Runnable
{
booleanflag=true;
publicsynchronizedvoid run()
{
while(flag)
try{wait();}
catch(Exception e)
{System.out.println(Thread.currentThread().getName()+"线程启动");
flag=false;
}
}
publicvoid ChangeFlag()
{
flag=false;
}
}
publicclass StopThread
{
publicstaticvoid main(String[] args)throws InterruptedException {
Run run=new Run();
Thread t1=new Thread(run);
t1.start();
int num=0;
while(true)
{
if(num++ == 50)
{
t1.interrupt();
break;
}
System.out.println(Thread.currentThread().getName()+num);
}
}
}
String的replace方法调用举例:
publicclass StringMethods {
publicstaticvoid main(String[] args)
{
String s="HelloJava";
String s1=s.replace("Java","viva");
String s2=s.replace("","e");
sop(s);
sop(s1);
sop(s2);
}
publicstaticvoid sop(Object obj)
{
System.out.println(obj);
}
}
从JDK1.5之后增加了StringBuilder功能,但不保证同步。StringBuffer是线程同步的。
建议多线程用StringBuffer,单线程用StringBuilder(效率高)。
基本数据类型转换成字符串:
基本数据类型+"";
基本数据类型.toString(基本数据类型值)
如Integer.toString(34);//将34转换成"34"
字符串转基本数据类型:
xxxa=Xxx.parsexxx(String);
例如int a=Integer.parseInt("123");
JDK1.5版本后的新特性:
泛型:
JDK1.5版本以后出现的新特性,用于解决安全问题,是一个安全机制。
好处
1.将运行时期出现的问题ClassCastException转移到了编译时期。方便于程序员解决问题,让运行时期问题减少,安全。
2.避免了强制转换的麻烦。
在使用java提供的对象时,什么时候写泛型呢?
通常在集合框架中很常见。
只要见到<>就要定义泛型。
什么时候定义泛型类?
当类中要操作的应用数据类型不确定时,早期定义Object来完成扩展,现在定义泛型来扩展。
泛型类定义的泛型,在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。
泛型类举例:
classUtils
{
private Q q;
public void setObject(Q q)
{
this.q=q;
}
public Q getObject()
{
return q;
}
}
为了让不同方法可以操作不同类型,而且类型还不确定,那么可以将泛型定义在方法上。
class Demo
{
public
{
System.out.println(t);
}
public void print (Q q)
{
System.out.println(q);
}
}
publicclassGenericDemo
{
publicstaticvoid main(String[] args)
{
Demod=newDemo();
d.show("haha");
d.show(new Integer(4));
d.print("heihei");
}
}
运行结果:haha
4
heihei
泛型类和泛型方法的结合:
class Demo
{
publicvoidshow(T t)
{
System.out.println(t);
}
public voidprint (Q q)
{
System.out.println(q);
}
publicstatic
//如果静态方法操作的引用数据类型不确定,可以将泛型定义在方法上。
{
System.out.println(w);
}
}
publicclassGeneric
{
publicstaticvoid main(String[] args)
{
Demo
d.show("haha");
d.print("heihei");
d.print(new Integer(5));
Demo.get("xixi");
}
}运行结果:haha
heihei
5
Xixi
?通配符:也可以理解为占位符。
泛型的限定:
?ExtendsE:可以接收E类型或者E类型的子类型。上限。
?SuperE:可以接收E类型或者E类型的父类型。
通配符使用举例:
import java.util.*;
class person1
{
private Stringname;
privateintage;
person1(String name,int age)
{
this.name=name;
this.age=age;
}
public String getName()
{
returnname;
}
publicint getAge()
{
returnage;
}
}
class student1extends person1
{
private Stringname;
privateintage;
private Stringsex;
student1(String name,int age,String sex)
{
super(name,age);
this.sex=sex;
}
public String getSex()
{
returnsex;
}
}
publicclassGenericDemo
{
publicstaticvoid main(String[] args)
{
ArrayList
al.add(new person1("zhangsan1",17));
al.add(new person1("zhangsan2",18));
al.add(new person1("zhangsan3",27));
printconn(al);
ArrayList
all.add(new student1("lisi1",17,"male"));
all.add(new student1("lisi2",27,"female"));
all.add(new student1("lisi3",37,"male"));
printconn(all);
}
publicstaticvoidprintconn(ArrayListextends person1> al)
{
Iterator extends person1> it=al.iterator();
while(it.hasNext())
{
System.out.println(it.next().getName());
}
}
}
高级for循环
格式:
For{数据类型变量名:被遍历的集合(collections)或者数组}、
{}
对集合进行遍历,只能获取集合元素,但是不能对集合进行操作。
迭代器除了遍历,还可以进行remove集合中元素的操作。
如果是用ListIterator,还可以在遍历过程中对集合进行增删改查的动作。
传统for循环和高级for循环有什么区别呢?
高级for有一个局限性,必须有被遍历的目标。
建议在遍历数组的时候,还是使用传统for,因为传统for可以定义角标。
代码举例:
for(String s:arr1)
{
System.out.println(s);
}
int[] arr2={1,65,9};
for(int s:arr2)
{System.out.println("s="+s);}
HashMap
hm.put("a",1);
hm.put("v",3);
hm.put("y",6);
Set
for(String i:arr2)
{
System.out.println(i+"::"+hm.get(i));
}
for(Map.Entry
{
System.out.println(i.getKey()+"---"+i.getValue());
}
可变参数:
其实就是上一种数组参数的简写形式,不用每一次都手动的建立数组对象。
只要将要操作的元素作为参数传递即可,隐式将这些参数封装成了数组。
方法的可变参数:
在使用时注意,可变参数一定要定义在参数列表的最后面。
static Import 静态导入
当类名重名时,需要指定具体的包名。
当方法重名时,指定具体所属的对象或类名。
静态导入可以简化书写,在导入相应的类之后,可以直接调用该类的方法和字段,而不需要通过类名.方法或类名.字段的形式访问,例如导入Math类,
sqrt(pow(x, 2), pow(y, 2));
看起来比
Math.sqrt(Math.pow(x, 2), Math.pow(y, 2));
要清晰的多,尤其是在大量使用Math类的方法时。导入Calendar类,
if(d.get(DAY_OF_WEEK) == MONDAY)
看起来比
if(d.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY)
容易多了。
//Integerx=new Integer(4);
Integerx=4;(自动装箱)需要判断x是否为null !!!
X=x+2;//对象加整数(自动拆箱)
Integerx=128;
Integery=128;
sop("x==y:"+(x==y)+";");
//结果为false
Integera=127;
Integerb=1278;
sop("a==b:"+(a==b)+";");
//结果为true,因为在JDK1.5之后,如果数值在byte范围内并已经存在,系统不会开辟新的存储空间。
-------android培训、java培训、期待与您交流! ----------