------- android培训、java培训、期待与您交流! ----------
多线程
线程间通信
其实就是多个线程在操作同一个资源,
但是操作的动作不同。
示意图
例子
class Res{
String name;
String sex;
boolean flag = false;
int x=0;
public synchronized void set(){
if(flag)
try{this.wait();}catch(Exception e){}
if(x==0){
this.name = "mike";
this.sex = "man";
}
else
{
this.name="丽丽";
this.sex="女女";
}
x = (x+1)%2;
this.flag = true;
this.notify();
}
public synchronized void get(){
if(!this.flag)
try{this.wait();}catch(Exception e){}
System.out.println(this.name+"..."+this.sex);
this.flag = false;
this.notify();
}
}
class Input implements Runnable{
private Res r;
Input(Res r){
this.r = r;
}
public void run(){
while(true){
r.set();
}
}
}
class Output implements Runnable{
private Res r;
Output(Res r){
this.r = r;
}
public void run(){
while(true){
r.get();
}
}
}
public 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();
}
}
在上面的代码中线程t1和t2,一个在添加姓名和性别,而另一个是在打印姓名和性别。添加时按照一个男一个女的顺序添加。并且保证添加一个打印一个。
上面的代码就是线程间通讯的一个例子。一个线程负责添加,另一个线程负责打印添加的内容。在代码中分别将信息添加方法set()和信息打印方法get()加锁,成为同步函数。两个方法使用的是同一个锁,这就保证了,当有一个线程在对信息进行操作时(包括添加和打印),其他的线程无法计入同步函数,这样就保证了安全,不会打印出错误信息。
同步中的方法
wait(): 在其他线程调用此对象的notify() 方法或 notifyAll()方法前,导致当前线程等待。此时线程将释放cpu执行权,并且释放锁,此时其它锁可以进入此同步中。
notify():
唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个wait()方法,在对象的监视器上等待。
简单理解为可唤醒已经失去cpu执行权的线程,使之获得cpu执行资格,当然该线程不一定马上
就会得到cpu执行权,这还要排队等前面的线程释放cpu执行权。另外此方法会优先唤醒,处于同一同步代码中的线程。
notifyAll():
唤醒在此对象监视器上等待的所有线程。线程通过调用其中一个wait方法,在对象的监视器上等待。与notify作用类似。区别在于,它会无差别的唤醒所有等待线程,不会区分是否在同一同步代码中。
sleep(long millis):
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。该线程不丢失任何监视器的所属权。
此方法不是同步中特有的,此方法将释放cpu执行权,而不会释放锁。也就是说在同步中,即使线程处于sleep()的状态中,其他线程也进来不了,因为锁没有释放。
注意:wait(),notify(),notifyAll(),方法只能在同步中,否则代码运行时会抛出非法监视异常。
Lock接口
JDK1.5 中提供了多线程升级解决方案。将同步Synchronized替换成实现Lock操作。
产生原因
synchronized可以解决同步中的安全问题,但是也存在一些不足
synchronized方法和代码块,会隐式的为进入这些代码的线程加锁,但代码中有多层锁时,线程
就会依次加上这些锁,并且要按照相反的顺序去释放这些锁,只有在退出相应代码时锁才会被
释放。这种方式太呆板,当需要在同步代码A中释放同步B中的锁定,这种方式就无能为力了。
此时我们需要一种更加灵活的方式来处理同步问题,Lock技术就依次产生。Lock技术可提供比synchronized更广泛的操作,下面只介绍与synchronized相关内容。
Lock与synchronized的关系
1,将synchronized 替换成了Lock接口。
将隐式锁,升级成了显示锁。
Lock中的方法
获取锁:lock();
放锁:unlock();注意:释放的动作一定要执行,所以通常定义在finally中。
获释取Condition对象:newCondition();
2,将Object中的wait,notify,notifyAll方法都替换成了Condition的await,signal,signalAll。
和以前不同是:一个同步代码块具备一个锁,该所以具备自己的独立wait和notify方法。
现在是将wait,notify等方法,封装进一个特有的对象Condition,而一个Lock锁上可以有多 个Condition对象。
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();
}
finally
{
lock.unlock();//释放锁的动作一定要执行。
}
}
// t3 t4
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();
}
finally
{
lock.unlock();
}
}
}
注意lock.unlock()一定要执行,即使发生异常锁得到释放,以便其它线程进入。
线程停止
只有一种,run方法结束。
开启多线程运行,运行代码通常是循环结构。
只要控制住循环,就可以让run方法结束,也就是线程结束。
class StopThread implements Runnable{
private boolean flag = true;
public synchronized void run(){
while(flag)
{
System.out.println(Thread.currentThread().getName()+"..run");
}
}
public void changeFlag()
{
flag = false;
}
}
public 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){
//此时改变标记,flag为false,run方法中的循环结束,线程t1,t2也结束
st.changeFlag();
break;
}
System.out.println(Thread.currentThread().getName()+"..."+num);
}
System.out.println("over");
}
}
特殊情况:
当线程处于了冻结状态。就不会读取到标记。那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。
需要强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。
Thread类提供该方法 interrupt();
class StopThread implements Runnable{
private boolean flag = true;
public synchronized void run(){
while(flag)
{
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println(Thread.currentThread().getName()+"..Exception");
//如果不在此处更改标记,下一次循环,线程又将进入冻结状态
flag = false;
}
System.out.println(Thread.currentThread().getName()+"..run");
}
}
public void changeFlag()
{
flag = false;
}
}
public 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){
t1.interrupt();//使线程从冻结状态中恢复过来,此时线程会抛出InterruptedException异常
t2.interrupt();
//st.changeFlag();调用此方法不能保证在线程t1和t2进行标记判断前,标记已更改。
break;
}
System.out.println(Thread.currentThread().getName()+"..."+num);
}
System.out.println("over");
}
}
线程中的其它方法
join
当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。
class Demo implements Runnable{
public void run()
{
for(int x=0;x<100;x++){
System.out.println(Thread.currentThread().toString()+".."+x);
// Thread.yield();
}
}
}
public class JoinDemo {
public static void main(String[] args) throws InterruptedException {
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
// t1.setPriority(Thread.MAX_PRIORITY);
t1.join();
t2.start();
for(int i=0;i<40;i++){
System.out.println(Thread.currentThread().getName()+".."+i);
}
}
}
可见当线程t1,执行完成后,才开始接着执行主线程。
setDaemon
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
也就是说,主线程结束时,此线程也会结束,该方法必须在启动线程前调用。
yield
暂停当前正在执行的线程对象,并执行其他线程。
setPriority
更改线程的优先级,优先级范围1-10,api中已定义好了几个常量
MIN_PRIORITY,MAX_PRIORITY,NORM_PRIORITY。
t.setPriority(MAX_PRIORITY)线程t被设置为最大优先级。