java基础----多线程(二)

来源:互联网 发布:淘宝网店软文 编辑:程序博客网 时间:2024/05/29 02:45
java基础----多线程(二)
今天我们学习多线程的第二部分,我说过多线程都是非常重要的内容

15、同步的两种表现形式:
(1)同步代码块:
可以指定需要获取哪个对象的同步锁,使用synchronized的代码块同样需要锁,但他的锁可以是任意对象
考虑到安全问题,一般还是使用同一个对象,相对来说效率较高。


注意:
**虽然同步代码快的锁可以使任何对象,但是在进行多线程通信使用同步代码快时,
 必须保证同步代码快的锁的对象和,否则会报错。
**同步函数的锁是this,也要保证同步函数的锁的对象和调用wait、notify和notifyAll的对象是
 同一个对象,也就是都是this锁代表的对象。
格式:
synchronized(对象)
{
需同步的代码;
}
(2)同步函数
同步方法是指进入该方法时需要获取this对象的同步锁,在方法上使用synchronized关键字,
使用this对象作为锁,也就是使用了当前对象,因为锁住了方法,所以相对于代码块来说效率相对较低。
注:静态同步函数的锁是该方法所在的类的字节码文件对象,即类名.class文件
格式:
修饰词 synchronized 返回值类型 函数名(参数列表)
{
需同步的代码;
}


在jdk1.5后,用lock锁取代了synchronized,个人理解也就是对同步代码块做了修改,
并没有提供对同步方法的修改,主要还是效率问题吧。
16、多线程的单例设计模式:保证某个类中内存中只有一个对象
(1)饿汉式:
class Single
{
private Single(){}//将构造函数私有化,不让别的类建立该类对象
private static final Single s=new Single();//自己建立一个对象
public static Single getInstance()//提供一个公共访问方式
{
return s;
}
}
(2)懒汉式:
class Single
{
private Single(){} 
private static Single s;
public static Single getInstance()
{
if(s==null)
s=new Single();
return s;
}
}
饿汉式和懒汉式的区别:
**
饿汉式是类一加载进内存就创建好了对象;
懒汉式则是类加载进内存的时候,对象还没有存在,只有调用了getInstance()方法时,对象才开始创建。
**
懒汉式是延迟加载,如果多个线程同时操作懒汉式时就有可能出现线程安全问题,解决线程安全问题
可以加同步来解决。但是加了同步之后,每一次都要比较锁,效率就变慢了,
所以可以加双重判断来提高程序效率。
如将上述懒汉式的Instance函数改成同步:
public static Single getInstance()
{
if(s==null)
{
synchronized(Single.class)
{
if(s==null) 
s=new Single();
}
}
return s;
}
17、死锁
两个线程对两个同步对象具有循环依赖时,就会发生死锁。即同步嵌套同步,而锁却不同。
18、wait()、sleep()、notify()、notifyAll()
wait():使一个线程处于等待状态,并且释放所持有的对象的lock。 
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。 
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,
而是由JVM确定唤醒哪个线程(一般是最先开始等待的线程),而且不是按优先级。 
Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
18、为什么wait()、notify()、notifyAll()这些用来操作线程的方法定义在Object类中?
(1)这些方法只存在于同步中;
(2)使用这些方法时必须要指定所属的锁,即被哪个锁调用这些方法;
(3)而锁可以是任意对象,所以任意对象调用的方法就定义在Object中。
19、多线程间通讯:
多线程间通讯就是多个线程在操作同一资源,但是操作的动作不同.
(1)为什么要通信
多线程并发执行的时候, 如果需要指定线程等待或者唤醒指定线程, 那么就需要通信.比如生产者消费者的问题,
生产一个消费一个,生产的时候需要负责消费的进程等待,生产一个后完成后需要唤醒负责消费的线程,
同时让自己处于等待,消费的时候负责消费的线程被唤醒,消费完生产的产品后又将等待的生产线程唤醒,
然后使自己线程处于等待。这样来回通信,以达到生产一个消费一个的目的。
(2)怎么通信
在同步代码块中, 使用锁对象的wait()方法可以让当前线程等待, 直到有其他线程唤醒为止.
使用锁对象的notify()方法可以唤醒一个等待的线程,或者notifyAll唤醒所有等待的线程.
多线程间通信用sleep很难实现,睡眠时间很难把握。


20、Lock和Condition
实现提供比synchronized方法和语句可获得的更广泛的锁的操作,可支持多个相关的Condition对象
Lock是个接口
锁是控制多个线程对共享数据进行访问的工具。


JDK1.5中提供了多线程升级的解决方案:
将同步synchonized替换成了显示的Lock操作,将Object中的wait、notify、notifyAll替换成了Condition对象。
该对象可以Lock锁进行获取


Lock的方法摘要:
void lock()  获取锁。 
Condition newCondition() 返回绑定到此 Lock 实例的新 Condition 实例。 
void unlock() 释放锁。
Condition方法摘要:
void await() 造成当前线程在接到信号或被中断之前一直处于等待状态。
void signal() 唤醒一个等待线程。          
void signalAll() 唤醒所有等待线程。
  
21、停止线程:
stop方法已经过时,如何停止线程?
停止线程的方法只有一种,就是run方法结束。如何让run方法结束呢?
开启多线程运行,运行代码通常是循环体,只要控制住循环,就可以让run方法结束,也就是结束线程。


特殊情况:当线程属于冻结状态,就不会读取循环控制标记,则线程就不会结束。
为解决该特殊情况,可引入Thread类中的Interrupt方法结束线程的冻结状态;
当没有指定的方式让冻结线程恢复到运行状态时,需要对冻结进行清除,强制让线程恢复到运行状态
22、interrupt:
void interrupt() 中断线程: 
中断状态将被清除,它还将收到一个 InterruptedException
22、守护线程(后台线程)
setDaemon(boolean on):将该线程标记为守护线程或者用户线程。
当主线程结束,守护线程自动结束,比如圣斗士星矢里面的守护雅典娜,
在多线程里面主线程就是雅典娜,守护线程就是圣斗士,主线程结束了,
守护线程则自动结束。
当正在运行的线程都是守护线程时,java虚拟机jvm退出;所以该方法必须在启动线程前调用;


守护线程的特点:
守护线程开启后和前台线程共同抢夺cpu的执行权,开启、运行两者都没区别,
但结束时有区别,当所有前台线程都结束后,守护线程会自动结束。
23、多线程join方法:
void join() 等待该线程终止。
void join(long millis)  等待该线程终止的时间最长为 millis 毫秒。
throws InterruptedException         
特点:当A线程执行到B线程的join方法时,A就会等待B线程都执行完,A才会执行
作用: join可以用来临时加入线程执行;
24、多线程优先级:yield()方法
yield():暂停当前正在执行的线程对象,并执行其他线程
setPriority(int newPriority):更改线程优先级
int getPriority() 返回线程的优先级。
String toString() 返回该线程的字符串表示形式,包括线程名称、优先级和线程组
  
(1)MAX_PRIORITY:最高优先级(10级)
(1)Min_PRIORITY:最低优先级(1级)
(1)Morm_PRIORITY:默认优先级(5级)


25、什么是ThreadLocal类,怎么使用它?
ThreadLocal类提供了线程局部 (thread-local) 变量。是一个线程级别的局部变量,并非“本地线程”。
ThreadLocal 为每个使用该变量的线程,提供了一个独立的变量副本,每个线程修改副本时不影响其它线程对象的副本


下面是线程局部变量(ThreadLocal variables)的关键点:
一个线程局部变量(ThreadLocal variables)为每个线程方便地提供了一个单独的变量。
ThreadLocal 实例通常作为静态的私有的(private static)字段出现在一个类中,这个类用来关联一个线程。 
当多个线程访问 ThreadLocal 实例时,每个线程维护 ThreadLocal 提供的独立的变量副本。
常用的使用可在 DAO 模式中见到,当 DAO 类作为一个单例类时,
数据库链接(connection)被每一个线程独立的维护,互不影响。(基于线程的单例)
26、什么时候抛出InvalidMonitorStateException异常?为什么?
调用 wait ()/notify ()/notifyAll ()中的任何一个方法时,如果当前线程没有获得该对象的锁,
那么就会抛出 IllegalMonitorStateException 的异常
也就是说程序在没有执行对象的任何同步块或者同步方法时,
仍然尝试调用 wait ()/notify ()/notifyAll ()时。由于该异常是 RuntimeExcpetion 的子类,
所以该异常不一定要捕获(尽管你可以捕获只要你愿意
作为 RuntimeException,此类异常不会在 wait (),notify (),notifyAll ()的方法签名提及。 
27、在静态方法上使用同步时会发生什么事?
同步静态方法时会获取该类的“Class”对象,所以当一个线程进入同步的静态方法中时,
线程监视器获取类本身的对象锁,其它线程不能进入这个类的任何静态同步方法。
它不像实例方法,因为多个线程可以同时访问不同实例同步实例方法。
28、当一个同步方法已经执行,线程能够调用对象上的非同步实例方法吗?
可以,一个非同步方法总是可以被调用而不会有任何问题。
实际上,Java 没有为非同步方法做任何检查,锁对象仅仅在同步方法或者同步代码块中检查。
如果一个方法没有声明为同步,即使你在使用共享数据Java照样会调用,而不会做检查是否安全,
所以在这种情况下要特别小心。一个方法是否声明为同步取决于临界区访问(critial section access),
如果方法不访问临界区(共享资源或者数据结构)就没必要声明为同步的。
29、在一个对象上两个线程可以调用两个不同的同步实例方法吗?
不能,因为一个对象已经同步了实例方法,线程获取了对象的对象锁。
所以只有执行完该方法释放对象锁后才能执行其它同步方法。
30、什么是线程饿死,什么是活锁?
线程饿死和活锁虽然不像死锁一样是常见的问题,但是对于并发编程的设计者来说就像一次邂逅一样。
当所有线程阻塞,或者由于需要的资源无效而不能处理,不存在非阻塞线程使资源可用。
JavaAPI 中线程活锁可能发生在以下情形:
当所有线程在程序中执行 Object.wait (0),参数为 0 的 wait 方法。
程序将发生活锁直到在相应的对象上有线程调用 Object.notify ()或者 Object.notifyAll ()。

当所有线程卡在无限循环中。


---------------------------闫继闯写于北京---------------------------

黑马ID:疯狂程序

QQ:654070989