Java基础-线程
线程进阶
1、多线程(线程间通信-示例代码)
线程间通信:其实就是多个线程在操作同一个资源,但操作动作不同。 以下是一个例子,可是没加同步,存在隐患。
/*
* 需求:一堆煤(共同资源),两个工人(线程),一个负责搬煤(输入名称),一个负责运煤(输出名字)。
*/
class Res
{
String name;
String sex;
}
class Input implements Runnable
{
private Res r ;
Input(Res r)
{
this.r = r ;
}
public void run()
{
int x = 0 ;
while(true)
{
if(x==0)
{
r.name = "mike";
r.sex = "man";
}
else
{
r.name = "LiLi";
r.sex = "girl";
}
x = (x+1)%2;
}
}
}
class Output implements Runnable
{
private Res r ;
Output(Res r )
{
this.r = r ;
}
public void run()
{
while(true)
{
System.out.println(r.name+"...."+r.sex);
}
}
}
class InputOutDemo
{
public static void main(String[]args)
{
Res r = new Res();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
输出结果:
mike....man
mike....man
mike....girl
mike....girl
LiLi....man
LiLi....man
LiLi....man
mike....girl
mike....girl
LiLi....man
mike....girl
LiLi....man
LiLi....girl
........
其结果不如人意,这是由于代码没有同步而造成的。
2、多线程(线程间通信-解决安全问题)
基于上一节的例子改进,对两线程执行的共同资源进行了同步上锁。
class Res
{
String name;
String sex;
}
class Input implements Runnable
{
private Res r ;
Input(Res r)
{
this.r = r ;
}
public void run()
{
int x = 0 ;
while(true)
{
synchronized(r)//上锁,同样的对象
{
if(x==0)
{
r.name = "mike";
r.sex = "man";
}
else
{
r.name = "LiLi";
r.sex = "girl";
}
x = (x+1)%2;
}
}
}
}
class Output implements Runnable
{
private Res r ;
Output(Res r )
{
this.r = r ;
}
public void run()
{
while(true)
{
synchronized(r)//上锁,同样的对象
{
System.out.println(r.name+"...."+r.sex);
}
}
}
}
class InputOutDemo
{
public static void main(String[]args)
{
Res r = new Res();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
输出结果:
LiLi....girl
mike....man
mike....man
mike....man
mike....man
......
因此结果符合客观的需求了。
3、多线程(线程间通信-等待唤醒机制)
类 Object 是类层次结构的根类。每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。在Object类中,wait方法,notify方法,notifyAll方法,都是用在同步中。
wait(long timeout):在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。
wait():在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
notify():唤醒在此对象监视器上等待的单个线程。
notifyAll():唤醒在此对象监视器上等待的所有线程。
由上一节课的结果发现,打印出来的结果还是存在不靠谱现象,就是同样的信息重复打印,原因是当input线程输入完信息后,output线程抢到了cpu执行权,进行输出,而output又在input线程未再次输入前又抢到了执行权,如何解决该问题:等待唤醒机制。
以下是上一节课改进后的代码:
class Res
{
String name;
String sex;
boolean flag;
}
class Input implements Runnable
{
private Res r ;
Input(Res r)
{
this.r = r ;
}
public void run()
{
int x = 0 ;
while(true)
{
synchronized(r)
{
if(r.flag) //在Ipunt同步锁里加了这标示表示该线程执行了一次就要等待Output同步锁里的代码执行一次,两功能交叉同时运行。
{
try
{
r.wait();
}
catch(Exception e)
{
}
}
if(x==0)
{
r.name = "mike";
r.sex = "man";
}
else
{
r.name = "LiLi";
r.sex = "girl";
}
x = (x+1)%2;
r.flag =true;
r.notify();//唤醒线程
}
}
}
}
class Output implements Runnable
{
private Res r ;
Output(Res r )
{
this.r = r ;
}
public void run()
{
while(true)
{
synchronized(r)
{
if(!r.flag)//在Output同步锁里加了这标示表示该线程执行了一次就要等待Input同步锁里的代码执行一次,两功能交叉同时运行。
{
try
{
r.wait();
}
catch(Exception e)
{
}
}
System.out.println(r.name+"...."+r.sex);
r.flag = false;
r.notify();//唤醒线程
}
}
}
}
class InputOutDemo
{
public static void main(String[]args)
{
Res r = new Res();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
输出结果:
LiLi....girl
mike....man
LiLi....girl
mike....man
LiLi....girl
.......
wait方法,notify方法和notifyAll方法这些都是使用在同步中,因为要对待有监视器(锁)的线程操作。所以要使用在同步中。因为只有同步才具有锁。 为什么这些操作线程的方法要定义Object类中呢? 因为这些方法在操作同步线程时,都必须要标识他们所操作线程只有的锁。只有同一个锁上的被等待的线程,可以被同一个锁上notify唤醒,不可以对不同锁中的线程进行唤醒。而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。
4、多线程(线程间通信-代码优化)
class Res//人物信息类
{
private String name;
private String sex;
private boolean flag = false; //线程通信标识符,为了让线程交叉有序的切换
public synchronized void set(String name , String sex)//把需要扔到线程run方法里的代码都封装成带锁的功能
{
if(flag)//加了通信机制的同步锁方法
{
try
{
this.wait();
}
catch(Exception e )
{
}
}
this.name = name;
this.sex = sex;
flag = true;
this.notify();
}
public synchronized void out()//把需要扔到线程run方法里的代码都封装成带锁的功能
{
if(!flag)//加了通信机制的同步锁方法
{
try
{
this.wait();
}
catch(Exception e )
{
}
}
System.out.println(name+"....."+sex);
flag = true;
this.notify();
}
}
class Input implements Runnable
{
private Res r ;
Input(Res r)
{
this.r = r;
}
public void run()//在Input的run方法中调用Res类中的加了通信机制的带锁方法(同步方法)。
{
int x = 0;
while(true)
{
if(x==0)
{
r.set("mike", "man");
}
else r.set("lili","girl");
x= (x+1)%2;
}
}
}
class Output implements Runnable
{
private Res r;
Output(Res r)
{
this.r = r;
}
public void run()//在Output的run方法中调用Res类中的加了通信机制的带锁方法(同步方法)。
{
while(true)
{
r.out();
}
}
}
class InputOutputDemo
{
public static void main(String[]args)
{
Res r = new Res();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}
输出结果:
mike.....man
lili.....girl
mike.....man
lili.....girl
mike.....man
lili.....girl
mike.....man
....
5、多线程(线程间通信-生产者消费者)
class Resource
{
private String name;
private int count =1;
private boolean flag = false ;
public synchronized void set(String name)
{
while(flag)//不用if判断是为了让多个生产者线程都要重新进行标识判断
{
try
{
this.wait();
}
catch(Exception e)
{
}
}
this.name = name +".."+count++;
System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
flag = true;
this.notifyAll();//不用notify的原因是由于被wait了的线程都存放在了线程池里,等待按顺序被唤醒,这样有可能没把对消费者的线程给唤醒
}
public synchronized void out()
{
while(!flag)//不用if判断是为了让多个消费者线程都要重新进行标识判断
{
try
{
this.wait();
}
catch(Exception e)
{
}
}
System.out.println(Thread.currentThread().getName()+".....消费者..."+this.name);
flag = false;
this.notifyAll();//不用notify的原因是由于被wait了的线程都存放在了线程池里,等待按顺序被唤醒,这样有可能没把对生产者的线程给唤醒
}
}
class Producer implements Runnable
{
private Resource res;
Producer(Resource res)
{
this.res = res;
}
public void run()
{
while (true)
{
res.set("+商品+");
}
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer (Resource res)
{
this.res = res;
}
public void run()
{
while (true)
{
res.out();
}
}
}
class ProducerConsumerDemo
{
public static void main(String []args)
{
Resource res = new Resource();
new Thread(new Producer(res)).start();
new Thread(new Consumer(res)).start();
new Thread(new Producer(res)).start();
new Thread(new Consumer(res)).start();
}
}
输出结果:
Thread-0...生产者..+商品+..1
Thread-1.....消费者...+商品+..1
Thread-0...生产者..+商品+..2
Thread-1.....消费者...+商品+..2
...
6、多线程(线程间通信-生产者消费者JDK5.0升级版)
上一节课生产者消费者程序问题的总结: * 对于多个生产者和消费者,为什么要定义while判断标记? 原因:让被唤醒的线程再一次判断标记。
- 为什么定义notifyAll? 因为需要唤醒对方线程。因为只用notify,容易由于线程池的特性而只唤醒了本方线程的情况,导致程序中的所有线程都等待,但该等待不是所谓的死锁。
升级后的Java java.util.concurrent.locks包里的接口Lock
接口Lock:Lock实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的Condition对象。
接口Condition:Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
/*
* JDK1.5中提供了多线程升级解决方案。将同步synchronized替换成现成lock操作。
* 将Objcet中的wait,notify,notifyAll,替换了condition对象。该对象可以lock锁进行获取,
* 该示例中,实现了本方只唤醒对方操作。
* */
import java.util.concurrent.locks.*;
class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition condition_pro = lock.newCondition();
private Condition condition_con = lock.newCondition();
public void set (String name )throws InterruptedException
{
lock.lock();//上锁
try
{
while (flag)
{
condition_pro.await(); //只冻结t1 , t2 (生产者线程)
}
this.name = name+"..."+count++;
System.out.println(Thread.currentThread().getName()+"....生产者...."+this.name);
flag = true;
condition_con.signal();//只唤醒t3,t4(消费者线程)
}
finally
{
lock.unlock();
}
}
public void out()throws InterruptedException
{
lock.lock();
try
{
while(!flag)
{
condition_con.await();
}
System.out.println(Thread.currentThread().getName()+"....消费者..........."+this.name);
flag = false;
condition_pro.signal();//唤醒t1,t2的线程(生产者)
}
finally
{
lock.unlock();
}
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.out();
}
catch(InterruptedException e)
{
}
}
}
}
class Producer implements Runnable
{
private Resource res;
Producer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.set("+商品+");
}
catch(InterruptedException e)
{
}
}
}
}
class ProducerConsumerDemo
{
public static void 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(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
输出结果:
......
Thread-0....生产者....+商品+...32599
Thread-3....消费者...........+商品+...32599
Thread-1....生产者....+商品+...32600
Thread-2....消费者...........+商品+...32600
Thread-0....生产者....+商品+...32601
Thread-3....消费者...........+商品+...32601
Thread-1....生产者....+商品+...32602
Thread-2....消费者...........+商品+...32602
.....
7、多线程(停止线程)
Thread类里的stop方法已经过时,那么如何停止线程? 只有一种,run方法结束。开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run()方法结束,也就是线程结束。
class StopThread implements Runnable
{
private boolean flag = true;
public synchronized void run()
{
while(flag)
{
try
{
//wait();//注释掉的冻结线程方法。若不注释,程序还是停不下来,线程冻结了。
}
catch(Exception e)
{
System.out.println(Thread.currentThread().getName()+".....Exception");
}
System.out.println(Thread.currentThread().getName()+"....run");
}
}
public void changeFlag()
{
flag = false;
}
}
class StopThreadDemo
{
public static void main(String []args)
{
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
int num = 0;
while(true)
{
if(num++==60)
{
st.changeFlag();
break;
}
System.out.println(Thread.currentThread().getName()+"......."+num);
}
}
}
输出结果:
main.......1
Thread-0....run
main.......2
Thread-0....run
.......
main.......59
Thread-0....run
main.......60
Thread-0....run
把注释掉的wait方法重新去掉注释,当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除,强制让线程恢复到运行状态中来,这就可以操作标记让线程结束。Thread类提供了该方法,interrupt()方法。
8、多线程(守护线程)
setDaemon(boolean on) 方法,将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java虚拟机退出。 参数on:如果为true,则将该线程标记为守护线程。
9、多线程(Join方法)
join() :等待该线程终止。也就是该线程直接抢夺cpu执行权。
class Demo implements Runnable
{
public void run()
{
for(int x=0 ; x<5;x++)
{
System.out.println(Thread.currentThread().getName()+"...."+x);
}
}
}
class JoinDemo
{
public static void main(String[]args) throws InterruptedException
{
Demo d = new Demo();
Thread t0 = new Thread(d);
Thread t1 = new Thread(d);
t0.start();
t1.join();//抢夺cpu资源
t1.start();
for(int x= 0;x<5;x++)
{
System.out.println("main..."+x);
}
System.out.println("over");
}
}
join方法:当t0线程执行到了t1线程的join()方法时,t0线程就会等待,等t1线程都执行完,t0才会执行,join可以用来临时加入线程执行。
10、多线程(优先级&yield)
class Demo implements Runnable
{
public void run()
{
for(int x=0 ; x<5;x++)
{
System.out.println(Thread.currentThread().getName()+"...."+x);
}
}
}
class JoinDemo
{
public static void main(String[]args) throws InterruptedException
{
Demo d = new Demo();
Thread t0 = new Thread(d);
Thread t1 = new Thread(d);
t0.start();
t1.setPriority(Thread.MAX_PRIORITY);//更改线程的优先级。
t1.start();
for(int x= 0;x<5;x++)
{
System.out.println("main..."+x);
}
System.out.println("over");
}
}
MAXPRIORITY:线程可以具有的最高优先级。 MINPRIORITY :线程可以具有的最低优先级。 NORM_PRIORITY:分配给线程的默认优先级。 yield() :暂停当前正在执行的线程对象,并执行其他线程。
技巧:编程时想令代码高效,使用匿名类去使用线程技术。
class ThreadTest
{
public static void main(String[]args)
{
new Thread() //方法1
{
public void run()
{
for(int x=0 ; x<100;x++)
{
System.out.println(Thread.currentThread().getName()+"..."+x);
}
}
}.start();
//方法2
Runnable r = new Runnable()
{
public void run()
{
for(int x = 0 ; x<100;x++)
{
System.out.println(Thread.currentThread().getName()+"..."+x);
}
}
};
new Thread(r).start();
}
}