首页 > 程序开发 > 软件开发 > Java >

Java基础知识(五)

2016-04-29

1 在HashTable中同步和如何实现HashMap的同步 1 同步意味着在一个时间点只能有一个线程可以修改hash表,任何线程在执行HashTable的更新操作前都需要获取对象锁,其他线程则等待锁的释放。 2 HashMap可以通

1.在HashTable中同步和如何实现HashMap的同步

1. 同步意味着在一个时间点只能有一个线程可以修改hash表,任何线程在执行HashTable的更新操作前都需要获取对象锁,其他线程则等待锁的释放。

2. HashMap可以通过Map m=Collection.synchronizedMap(new HashMap())来达到同步的效果。具体而言,该方法返回一个同步的Map,该Map封装了底层的HashMap的所有方法,使得底层的HashMap即使是在多线程的环境中也是安全的。

2.自定义类型作为HashMap和HashTable注意的问题

1. 当有重复的键值时,不会创建新的映射关系

public static void test1(){

System.out.println("Use user defined class as key");

HashMap hm=newHashMap();

hm.put("aaa", "bbb");

hm.put("aaa", "ccc");

Iterator iter =hm.entrySet().iterator();

while(iter.hasNext()){

Map.Entry entry=(Map.Entry)iter.next();

String key=(String)entry.getKey();

String val=(String)entry.getValue();

System.out.println(key+" "+val);

}

}

public static void main(String[] args) {

test1();

}

结果是:aaa bbb

2. key值得equals为true是不创建新的键值关系,下面是两个对象作为键,所以他们的equals返回false,即可以创建新的键值关系。

public static voidtest2(){

System.out.println("Use String as key");

HashMap hm= newHashMap();

Person p1=new Person("111","name1");

Person p2=new Person("111","name1");

hm.put(p1, "address1");

hm.put(p2, "address1");

Iterator iter=hm.entrySet().iterator();

while(iter.hasNext()){

Map.Entry entry=(Map.Entry)iter.next();//???????

Person key=(Person) entry.getKey();

String val=(String)entry.getValue();

System.out.println("key="+key+" value="+val);

}

结果是:Use String as key

key=id=111,name=name1 value=address1

key=id=111,name=name1 value=address1

3. 可以根据对象的内容来判断两个对象是否相等来添加键值对,避免上述情况。根据对象的内容来判断两个内容是否相等,所以这里重新定义了默认Object类的equals()和hashCode()方法用来判断键的hashCode和equals,而不是对象的hashCode。

public int hashCode(){

return id.hashCode();

}

public booleanequals(Object obj){

Person p=(Person) obj;

if(p.id.equals(this.id))

return true;

else

return false;

}

3.Collection和Colletions的区别

Collection是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法。实现该接口的类主要有List和Set。

Collections是针对集合类的一个包装类,它提供一系列静态方法以实现对各种集合的搜索、排序、线程安全化等操作。Collections类不能实例化(就是不能:类名对象名 = new 类名(参数1,参数2...参数n)这样的创建),服务于Collection框架。

4.线程,进程,多线程

线程是指程序执行过程中,能够执行程序代码的一个执行单元。线程有4个状态:运行、就绪、挂起和结束。

进程是指一段正在执行的程序。线程被称为轻量级进程,它是程序执行的最小单元,一个进程可以有很多线程,各个线程之间共享程序的内存空间(代码段、数据段和堆空间)及一些进程级资源(如打开的文件),但是各个线程拥有自己的栈空间。在操作系统级别上,程序的执行都是以进程为单位的,进程中通常会有多个线程互不影响地并发执行。使用多线程的便利:1)可以减少程序的响应时间;2)与进程相比,线程的创建和切换开销更小。3)多cpu或多核计算机本身就具有执行多线程的能力;4)使用多线程能简化程序结构,便于理解和维护。

5.同步与异步的区别

1. 同步:当线程需要访问同一个资源时,同步机制需要以某种顺序来确保该资源在某一时刻只能被一个线程使用,同步机制能够保证资源的安全。实现同步的操作就得获得每一个线程对象的锁,保证在同一时刻只有一个线程能够进入临界区(访问互斥资源的代码块),在这个锁被释放之前,其他线程就不能进入。可以通过使用synchronized关键字来实现同步。但是并非同步控制越多越好。

2. 异步:与非阻塞类似,每个线程包含运行时自身所需要的数据方法,在输出出入处理时不关心其他进程的状态或行为。不必等待其他线程返回什么后在执行,提高了程序的效率。

6.如何实现Java多线程

1. 继承Thread类,重写run()方法

Thread本质上也实现了Runnable接口的实例,启动线程的唯一方法就是start()方法,然后执行run()方法。通过继承Thread类,重写run()方法,就可以启动新的线程并执行自己定义的run()方法。注意:start()方法后并不是立即执行多线程代码,而是是的该线程变为可运行态。

public class Mythread1 extends Thread {

/**

* 重写run方法

*/

public voidrun(){

System.out.println("thread body");

}

}

开启线程:

Mythread1thread=newMythread1();

thread.start();

2. 实现Runnable接口,并实现该接口的run()方法

public class MyThread2 implements Runnable {

@Override

public voidrun() {

System.out.println("thread body2");

}

}

启动线程:

MyThread2thread2=newMyThread2();

Thread t=new Thread(thread2);//都是要通过Thread的对象的API来控制线程的

t.start();

3. 实现Callable接口,重写call()方法。(callable接口与Runnable功能相似,但是更强大)主要表现在:1)Callable可以在任务结束后提供一个返回值,Runnable无法提供这个功能;2)Callable中的call()方法可以抛出异常;3)运行Callable中的call()方法可以拿到一个Future对象,Future对象表示异步计算的结果,提供检查是否完成的方法。

public class CallableFuture {

/**

* 创建线程类

*/

public static class CallableTest implementsCallable{

public String call(){

return "HelloWorld!";

}

}

public static void main(String[] args) {

ExecutorService threadPool =Executors.newSingleThreadExecutor();

//启动线程

Future future=threadPool.submit(new CallableTest());

try{

System.out.println("waiting thread to finish ");

System.out.println(future.get());

}catch(Exception e){

e.printStackTrace();

}

}

}

当需要实现多线程时,一般推荐实现Runnable接口方法,

7.run()方法和start()方法的区别

start()方法用来启动一个线程,也就是说这个线程可以被JVM通过调用run()方法来调度执行,run()方法结束后,次线程就会终止。如果直接调用run()方法,就会被当做一个普通的函数调用,程序总仍然只有主线程这个线程,即start()能过异步的调用run()方法。

public class ThreadDemo extends Thread {

public voidrun(){

System.out.println("ThreadDemo:begin");

try{

Thread.sleep(1000);

}catch(InterruptedException e){

e.printStackTrace();

}

System.out.println("ThreadDemo:end");

}

}

public class TestThread {

public static void test1(){

System.out.println("test1:begin");

Thread t1=new ThreadDemo();

t1.start();

System.out.println("test1:end");

}

start()方法启动线程是,线程t1是在test1方法结束后才执行的,实现了异步。

public static void test2(){

System.out.println("test2:begin");

Thread t1=new ThreadDemo();

t1.run();

System.out.println("test2:end");

}

public static void main(String[] args) {

test1();

try{

Thread.sleep(5000);

}catch(InterruptedException e){

e.printStackTrace();

}

System.out.println();

test2();

}

}

run()方法启动线程时,线程t1执行完后,主线程才指向test2:end。实现的是同步。

8.多线程同步的实现方法

实现同步机制的方法:

1. synchronized关键字

每个对象拥有一个对象锁,表明在任何时候只允许被一个线程所拥有,当线程调用对象的一度synchronized代码是,需要获取这个锁然后执行相应的代码,执行结束后释放锁。

2. wait()方法与notify()方法

在synchronized代码执行期间,线程可以调用对象的wait()方法,释放对象锁,进入等待状态,并且可以调用notify()方法通知正在等待的其他线程。其中notify()方法仅唤醒一个线程,notifyAll()方法唤醒所有等待这个对象的线程,让它们去竞争锁。

3. Lock接口

1)lock(),以阻塞的方式获取锁,也就是获得锁就返回,或者等待(一直处于阻塞状态)别的线程释放锁后得到所再返回。2)tryLock()以非阻塞的方式获得锁,即尝试性地去获得锁,得到锁返回true。3)tryLock(long thimeout,TimeUnit unit)。参数为等待的时间,在等待时间内获得锁返回true,否则false。4)lockInterruptibly(),若获得锁立即返回;否则,当前此案成处于休眠状态,直到获得锁,或者当前线程被背的线程中断。

public classTest {

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

final Lock lock = new ReentrantLock();

lock.lock();

Thread t1=new Thread(new Runnable(){

public void run(){

try{

lock.lockInterruptibly();

}catch(InterruptedException e){

System.out.println(" interrupted");

}

}

});

t1.start();

t1.interrupt();

Thread.sleep(1);

}

}

如果将代码中的lock.lockInterruptibly()替换成lock.lock()编译会报错,因为lock.lock()不会抛出异常,忽略interrupt。

9.sleep()方法与wait()方法的区别

1. 原理不同,sleep()方法是Thread的静态方法,是线程用来控制自身流程的,它会使此线程暂停一段时间,而把执行机会让给其他线程,等到计时时间一到,就会“苏醒”,即就是给自己的执行设置了闹钟,在一定的时间内执行多少次。。而wait()方法是Object类的方法,用于线程间的通信,这个方法会使当前拥有该对象锁的进程等待,知道其他线程调用notify()方法时才醒过来(上述中有)。

2. 对锁的处理机制不同。由于sleep()方法的主要作用是让线程暂停执行一段时间,时间一到则自动恢复,不涉及线程间的通信,因此调用sleep()方法并不会释放锁。而wait()方法线程会释放掉它所占用的锁,从而使线程所在对象中的其他同步数据可别其他线程调用。

3. 使用区域不同,wait()方法必须放在同步控制方法或者同步语句块中使用,sleep()放在任何地方使用。

sleep()与yield()方法的区别

1)sleep()方法给其他线程运行机会时不考虑线程的优先级,而yield()方法只会给相同或更高优先级的线程以运行的机会。2)yield()只是使当前线程重新回到可执行状态。3)sleep()方法声明抛出InterruptedException,yield没有声明异常。4)sleep具有更好的可移植性。

10.终止线程的方法

stop()和suspend()方法,Thread.stop()来终止线程,会释放已经锁定的所有监视资源。suspend()容易发生死锁,(死锁指的是两个或两个以上的进程在执行过程中,因争夺资源而造成的一张互相等待的现象,如果无外力作用将无法推进。)因为调用suspend()方法不会释放锁。这两种方法来终止线程具有不确定性或容易死锁。不建议使用

一般建议采用的方法是让线程自行结束进入Dead状态。在实现时,通过设置一个flag标志来控制循环是否执行,来让线程离开run()方法从而终止线程。或者使用interrupt()方法打破阻塞且被调用是抛出异常,通过run()方法捕获这个异常来让线程安全退出。

public class MyThread2 {

public static void main(String[] args) {

Thread thread=new Thread(new Runnable(){

public void run(){

System.out.println("thread go to sleep");

try{

//用休眠来模拟线程被阻塞

Thread.sleep(8000);

System.out.println("thread finish");

}catch(InterruptedException e){

System.out.println("thread is interupted");

}

}

});

thread.start();

thread.interrupt();

}

}

11.synchronized与Lock的区别

synchronized使用Object对象本身的notify,wait、notifyAll调度机制,而Lock可以使用Condition进行线程之间的调度,完成同步实现的所有功能。主要区别有:

1. 用法不一样。在需要同步的对象中加入synchronized控制,它可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。Lock需要显示地指定起始位置和终止位置。前者是JVM执行的,后者是通过代码实现的有更精确的线程语义。

2. 性能不一样。在资源竞争不强烈是,synchronized的性能优于ReetrantLock,但是竞争激烈时后者性能优于前者。

3. 锁机制不一样。

相关文章
最新文章
热点推荐