黑马程序员——线程Thread二(线程安全)
2015-08-24 16:47
495 查看
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
在前一篇的文章中,介绍了实现线程的两个方法,一个是继承Thread类,一个是实现Runable方法,而且比较了两个区别。在文章最后,我写了一个4个售票员卖一百张票的例子,但那个例子存在一个问题,在运行时会出现,卖出-1,-2张票得情况。这就是线程存在的安全问题。
线程的并发安全问题
线程安全问题主要是指,多条线程执行访问同一个资源,个线程在切换时出现的安全问题。
线程安全问题出现的因素:1.多个线程在操作共享的数据。
2.操作共享数据的线程代码有多条。
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。
线程安全的解决办法:
要求一个线程操作共享数据时,只有当其完成操作完成共享数据时,其他线程才有机会执行操作共享数据。
方式一:同步代码块synchronized(同步监视器){
///操作共享数据的代码
}
同步监视器:俗称锁,任何一个类的对象都可以充当锁。但想要保证线程安全,所有线程必须持有同一把锁。如果使用实现Runable接口的方法来实现多线程的话,线程内的同步代码块建议考虑使用this作为锁。
方式二:同步方法
在函数上加上synchronized修饰符即可。
同步函数和同步代码块的区别:
1.同步函数的锁是固定的this。
2.同步代码块的锁是任意的对象。建议使用同步代码块。
由于同步函数的锁是固定的this,同步代码块的锁是任意的对象,那么如果同步函数和同步代码块都使用this作为锁,就可以实现同步。
静态的同步函数使用的锁是默认是该函数所属字节码文件对象,可以用getClass方法获取,也可以用当前类名.class
释放锁的方法:wait();不释放锁的方法:sleep();yield();suspend();
死锁:不同线程分别占用了对方线程需要的同步资源,自己不放弃资源都在等待对方释放资源,就产生了死锁,思索时我们需要避免的问题。同步中嵌套同步且锁不同就会产生死锁
示例:
线程间通信:
如下三个方法必须使用在同步代码块,或者同步方法中
wait();当在同步中执行到此方法,则该线程“等待”,直至其他线程的notify方法将其唤醒,唤醒后将继续执行wait();后的代码
notify/notifyAll,在同步中执行到此方法,则唤醒一个或所有被wait的线程
这些方法是定义在Object类中的,为什么被定义在Object类中呢?
因为只有同一个锁上被等待的线程,才可以被同一个锁上的notfy();唤醒。也就是说等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的,所以定义在Object类中
while(flag)和if(flag)判断flag的区别,flag如果在while内,也必须回去判断flag的真假,如果flag在if内,则不需要回去判断flag的真假。
notifyAll出现的原因,因为需要唤醒对方线程。
notify容易出现只唤醒本方线程的情况,导致程序中的所有线程等待。
线程同步的示例,生产者,消费者模式:
在thread中还可以将synchronized用lock替换。将Object中的wait,notify,notifyAll用condition对象的lock替换,替换后的声场这消费者实例:
当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除,强行让线程恢复到运行状态中,这样可以操作标记,让线程结束,Thread类提供了interrupt();
interrupt示例:
以上就是关于Thread的内容
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
在前一篇的文章中,介绍了实现线程的两个方法,一个是继承Thread类,一个是实现Runable方法,而且比较了两个区别。在文章最后,我写了一个4个售票员卖一百张票的例子,但那个例子存在一个问题,在运行时会出现,卖出-1,-2张票得情况。这就是线程存在的安全问题。
线程的并发安全问题
线程安全问题主要是指,多条线程执行访问同一个资源,个线程在切换时出现的安全问题。
线程安全问题出现的因素:1.多个线程在操作共享的数据。
2.操作共享数据的线程代码有多条。
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。
线程安全的解决办法:
要求一个线程操作共享数据时,只有当其完成操作完成共享数据时,其他线程才有机会执行操作共享数据。
方式一:同步代码块synchronized(同步监视器){
///操作共享数据的代码
}
同步监视器:俗称锁,任何一个类的对象都可以充当锁。但想要保证线程安全,所有线程必须持有同一把锁。如果使用实现Runable接口的方法来实现多线程的话,线程内的同步代码块建议考虑使用this作为锁。
class Bank{ privateintsum; synchronized(this){ sum=sum+num; } }
方式二:同步方法
在函数上加上synchronized修饰符即可。
class Bank{ privateintsum; publicsynchronizedvoidadd(intnum){ //同步函数 /sum=sum+num; System.out.println("sum="+sum);} }
同步函数和同步代码块的区别:
1.同步函数的锁是固定的this。
2.同步代码块的锁是任意的对象。建议使用同步代码块。
由于同步函数的锁是固定的this,同步代码块的锁是任意的对象,那么如果同步函数和同步代码块都使用this作为锁,就可以实现同步。
静态的同步函数使用的锁是默认是该函数所属字节码文件对象,可以用getClass方法获取,也可以用当前类名.class
释放锁的方法:wait();不释放锁的方法:sleep();yield();suspend();
死锁:不同线程分别占用了对方线程需要的同步资源,自己不放弃资源都在等待对方释放资源,就产生了死锁,思索时我们需要避免的问题。同步中嵌套同步且锁不同就会产生死锁
示例:
class Ticket implements Runnable{ private static int num = 100; Object obj = new Object(); boolean flag = true; public void run(){ if(flag ){ while(true ){ synchronized(obj ){ show(); } } } else while(true ) show(); } public synchronized void show(){ synchronized(obj ){ if(num > 0){ try{ Thread. sleep(10); } catch(InterruptedException e){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "...function..." + num--); } } } } class DeadLockDemo{ public static void main(String[] args){ Ticket t = new Ticket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); try{ Thread. sleep(10); } catch(InterruptedException e){ e.printStackTrace(); } t. flag = false ; t2.start(); } }
线程间通信:
如下三个方法必须使用在同步代码块,或者同步方法中
wait();当在同步中执行到此方法,则该线程“等待”,直至其他线程的notify方法将其唤醒,唤醒后将继续执行wait();后的代码
notify/notifyAll,在同步中执行到此方法,则唤醒一个或所有被wait的线程
这些方法是定义在Object类中的,为什么被定义在Object类中呢?
因为只有同一个锁上被等待的线程,才可以被同一个锁上的notfy();唤醒。也就是说等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的,所以定义在Object类中
while(flag)和if(flag)判断flag的区别,flag如果在while内,也必须回去判断flag的真假,如果flag在if内,则不需要回去判断flag的真假。
notifyAll出现的原因,因为需要唤醒对方线程。
notify容易出现只唤醒本方线程的情况,导致程序中的所有线程等待。
线程同步的示例,生产者,消费者模式:
public class Resource {//资源 private String name; private int count=1; boolean flag=false; public synchronized void setRes(String name){ while (flag) {//用while不用if的原因是,让被唤醒的线程再一次判断标记 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } 4000 this.name=name+":"+count++; System.out.println(Thread.currentThread().getName()+"生产者-- "+this.name); flag=true; this.notifyAll(); } public synchronized void out(){ while (!flag) {//用while不用if的原因是,让被唤醒的线程再一次判断标记 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+"消费者-- --"+this.name); flag=false; this.notifyAll(); } } public class Producer implements Runnable {//生产者 private Resource r; Producer(Resource r){ this.r=r; } @Override public void run() { while (true){ r.setRes("商品"); } } } /** * 消费者 */ public class Consumer implements Runnable { private Resource r; public Consumer(Resource r) { this.r=r; } @Override public void run() { while (true) r.out(); } } ** * 生产消费者实例,多个生产者,多个消费者,生产一个,消费一个。 */ public class ProCon { public static void main(String[] args){ Resource r=new Resource(); Thread t1=new Thread(new Producer(r)); Thread t2=new Thread(new Consumer(r)); Thread t3=new Thread(new Producer(r)); Thread t4=new Thread(new Consumer(r)); t1.start(); t2.start(); t3.start(); t4.start(); } }
在thread中还可以将synchronized用lock替换。将Object中的wait,notify,notifyAll用condition对象的lock替换,替换后的声场这消费者实例:
public class Resource2 { private String name; private int count=1; boolean flag=false; Lock lock=new ReentrantLock(); Condition condition_pro=lock.newCondition(); Condition condition_con=lock.newCondition(); public void setRes(String name){ lock.lock(); try { while (flag)//用while不用if的原因是,让被唤醒的线程再一次判断标记 condition_pro.await(); this.name=name+":"+count++; System.out.println(Thread.currentThread().getName()+"生产者-- "+this.name); flag=true; condition_con.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void out(){ lock.lock(); try { while (!flag)//用while不用if的原因是,让被唤醒的线程再一次判断标记 condition_con.await(); System.out.println(Thread.currentThread().getName()+"消费者-- --"+this.name); flag=false; condition_pro.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.lock(); } } }
当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除,强行让线程恢复到运行状态中,这样可以操作标记,让线程结束,Thread类提供了interrupt();
interrupt示例:
public class StopThread implements Runnable{ private boolean flag=true; @Override public synchronized void run() { while (flag){ try { wait(); } catch (InterruptedException e) { //e.printStackTrace(); System.out.println(Thread.currentThread().getName() + "--Exception"); flag=false; } System.out.println(Thread.currentThread().getName()+"--run"); } } public void changeFlag(){ flag=false; } } public class StopDemo { 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(); t1.interrupt(); t2.interrupt(); break; } System.out.println(Thread.currentThread().getName()+"--"+num); } System.out.println("over"); } }
以上就是关于Thread的内容
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树
- [原创]java局域网聊天系统