您的位置:首页 > 职场人生

黑马程序员_Java学习日记第四天-线程、Java1.5的新特性

2012-08-23 18:51 549 查看
------- android培训java培训、期待与您交流! ----------
线程

创建线程的两种方式:
第一种
继承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;

}


int
a=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<Q>

{
private Q q;
public void setObject(Q q)
{
this.q=q;
}
public Q getObject()
{
return q;
}
}

为了让不同方法可以操作不同类型,而且类型还不确定,那么可以将泛型定义在方法上。
class Demo
{
public <T>void show(T t)
{
System.out.println(t);
}
public <Q>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<T>
{
publicvoidshow(T t)
{
System.out.println(t);
}
public <Q>voidprint (Q q)
{
System.out.println(q);
}
publicstatic<W>voidget (W w) //静态方法不可以访问类上定义的泛型。
//如果静态方法操作的引用数据类型不确定,可以将泛型定义在方法上。
{
System.out.println(w);
}
}
publicclassGeneric
{
publicstaticvoid main(String[] args)
{
Demo<String> d=newDemo <String> ();
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<person1> al=new ArrayList<person1>();
al.add(new person1("zhangsan1",17));
al.add(new person1("zhangsan2",18));
al.add(new person1("zhangsan3",27));
printconn(al);
ArrayList<student1> all=new ArrayList<student1>();
all.add(new student1("lisi1",17,"male"));
all.add(new student1("lisi2",27,"female"));
all.add(new student1("lisi3",37,"male"));
printconn(all);
}
publicstaticvoidprintconn(ArrayList<?extends
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<String,Integer> hm=new HashMap<String,Integer> ();

hm.put("a",1);

hm.put("v",3);

hm.put("y",6);

Set <String>arr2=hm.keySet();

for(String i:arr2)

{

System.out.println(i+"::"+hm.get(i));

}

for(Map.Entry<String, Integer>i:hm.entrySet())

{

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培训、期待与您交流! ----------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: