一.创建线程
1.继承Thread
2.实现Runnable接口
二.线程互斥
synchronized (互斥对象)
实现的原理是这个互斥对象每次只能允许一个线程进入,其他的则进入这个互斥对象的等待队列中.
三.线程同步
wait()和notify() notifyAll()
这三个方法用于协调多个线程对共享数据的存取,所以必须在synchronized语句块内使用。synchronized关键字用于保护共享数据,阻止其他线程对共享数据的存取,但是这样程序的流程就很不灵活了,如何才能在当前线程还没退出synchronized数据块时让其他线程也有机会访问共享数据呢?此时就用这三个方法来灵活控制。
wait()方法使当前线程暂停执行并释放对象锁标示,让其他线程可以进入synchronized数据块,当前线程被放入对象等待池中。当调用notify()方法后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中,只有锁标志等待池中线程能够获取锁标志;如果锁标志等待池中没有线程,则notify()不起作用。
notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。
Notes:
用while()synchronized()时候不要省略括号,不然编译器会优化为永久获得.
IllegalMonitorStateException异常
也就是当前的线程不是此对象监视器的所有者。也就是要在当前线程锁定对象,才能用锁定的对象此行这些方法,需要用到synchronized ,锁定什么对象就用什么对象来执行notify()
,
notifyAll()
,
wait()
,
wait(long)
,
wait(long, int)操作,否则就会报IllegalMonitorStateException异常
//exapmle 1,锁定方法所属的实例对象:
public synchronized void method(){
//然后就可以调用:this.notify()...
//或者直接调用notify()...
}
//exapmle 2,锁定方法所属的实例的Class:
public Class Test{
public static synchronized void method(){
//然后调用:Test.class.notify()...
}
}
//exapmle 3,锁定其他对象:
public Class Test{
public Object lock = new Object();
public static void method(){
synchronized (lock) {
//需要调用 lock.notify();
}
}
}
四.其他几个方法:
1、sleep()
使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据。注意该方法要捕捉异常。
例如有两个线程同时执行(没有synchronized)一个线程优先级为MAX_PRIORITY,另一个为MIN_PRIORITY,如果没有Sleep()方法,只有高优先级的线程执行完毕后,低优先级的线程才能够执行;但是高优先级的线程sleep(500)后,低优先级就有机会执行了。
总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的线程有执行的机会。
2、join()
join()方法使调用该方法的线程在此之前执行完毕,也就是等待该方法的线程执行完毕后再往下继续执行。注意该方法也需要捕捉异常。
3、yield()
该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。
interrupt方法的效果是它会设置线程的状态位,但是对于正在运行状态的线程并不会导致它被立即中断。只有执行的线程在检测到相关信息后采取对应措施。我们常用的措施一般是调用isInterrupted方法或者捕捉被调用方法抛出的InterruptedException来做处理。整体的应用并不复杂。
wait()和notify(),notifyAll()是Object类的方法,sleep()和yield()是Thread类的方法。
(1)、常用的wait方法有wait()和wait(long timeout);
void wait() 在其他线程调用此对象的 notify() 方法或者 notifyAll()方法前,导致当前线程等待。
void wait(long timeout)在其他线程调用此对象的notify() 方法 或者 notifyAll()方法,或者超过指定的时间量前,导致当前线程等待。
wait()后,线程会释放掉它所占有的“锁标志”,从而使线程所在对象中的其他shnchronized数据可被别的线程使用。
wait()h和notify()因为会对对象的“锁标志”进行操作,所以他们必需在Synchronized函数或者 synchronized block 中进行调用。如果在non-synchronized 函数或 non-synchronized block 中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。。
(2)、Thread.sleep(long millis)必须带有一个时间参数。
sleep(long)使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会被执行;
sleep(long)可使优先级低的线程得到执行的机会,当然也可以让同优先级的线程有执行的机会;
sleep(long)是不会释放锁标志的。
(3)、yield()没有参数
sleep 方法使当前运行中的线程睡眠一段时间,进入不可以运行状态,这段时间的长短是由程序设定的,yield方法使当前线程让出CPU占有权,但让出的时间是不可设定的。
yield()也不会释放锁标志。
实际上,yield()方法对应了如下操作;先检测当前是否有相同优先级的线程处于同可运行状态,如有,则把CPU的占有权交给次线程,否则继续运行原来的线程,所以yield()方法称为“退让”,它把运行机会让给了同等级的其他线程。
sleep 方法允许较低优先级的线程获得运行机会,但yield()方法执行时,当前线程仍处在可运行状态,所以不可能让出较低优先级的线程此时获取CPU占有权。在一个运行系统中,如果较高优先级的线程没有调用sleep方法,也没有受到I/O阻塞,那么较低优先级线程只能等待所有较高优先级的线程运行结束,方可有机会运行。
yield()只是使当前线程重新回到可执行状态,所有执行yield()的线程有可能在进入到可执行状态后马上又被执行,所以yield()方法只能使同优先级的线程有执行的机会
package bingone;
/*
* 多线程学习
* 资源互斥模型1
* 生产者-消费者模型2
* 读者鞋者模型3
*/
class ThreadTest1 implements Runnable{
/*
* 这里不能写做互斥方法
* 线程与互斥方法分开,那样也可以用同一方法的互斥量
*/
public void onlyOne(){
synchronized (ThreadTest.mm) {
System.out.println("Thread "+Thread.currentThread().getName()+ " test change from "+ThreadTest.test +" to "+(++ThreadTest.test));
}
}
public void run(){
onlyOne();
}
}
/*
* 读者写者问题
*/
class ThreadTest2 implements Runnable {
public int t;
public ThreadTest2(int t) {
super();
// TODO Auto-generated constructor stub
this.t=t;
}
public void write() throws Exception{
while(true){
synchronized (ThreadTest.rw) {
if(ThreadTest.reader>0)
ThreadTest.rw.wait();
int k = ThreadTest.test;
ThreadTest.test += (int)( Math.random()*100);
System.out.println(System.currentTimeMillis()+" write Thread "+Thread.currentThread().getName()+" "+ThreadTest.test);
}
long time = System.currentTimeMillis();
while(time+1000>System.currentTimeMillis());
}
}
public void read() throws Exception{
while(true){
synchronized (ThreadTest.rw) {
ThreadTest.reader++;
}
System.out.println(System.currentTimeMillis()+ " read Thread "+Thread.currentThread().getName()+" "+ThreadTest.test+"=====");
synchronized (ThreadTest.rw) {
ThreadTest.reader--;
if(ThreadTest.reader==0)
ThreadTest.rw.notify();
}
long time = System.currentTimeMillis();
while(time+1000>System.currentTimeMillis());
}
}
public void run(){
try {
if(t%2==1)
write();
else read();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class ThreadTest{
public static int test = 0;
public static Object rw = new Object();
public static Object ww = new Object();
public static Object mm = new Object();
public static Integer reader = 0;
public static boolean writer = false;
public static void main(String[] args) throws Exception {
System.out.println("in the main Thread");
Thread [] t = new Thread[100];
for(int i = 0;i<100;i++){
// t[i] =new Thread(new ThreadTest1(),"test"+i );
t[i] =new Thread(new ThreadTest2(i),"test"+i );
// t[i] =new Thread(new ThreadTest3(),"test"+i );
t[i].start();
}
for(Thread x:t)
x.join();//使主程序最后退出
System.out.println("main Thread exit...");
}
}