黑马程序员 多线程
来源:互联网 发布:num在c语言中什么意思 编辑:程序博客网 时间:2024/05/17 03:05
多线程是Java中一个非常重要的概念,它能使得一个进程中多个模块同时运行。比如常见的杀毒软件,在扫描木马的时候还能同时扫面插件、清理垃圾等等。如果一次只能做一件事情,那效率就低下许多了,但是多线程中可能存在安全隐患,应用的时候必须要注意。
进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立的控制单元。线程在控制着进程的执行,一个进程中至少有一个线程。
JVM启动的时候会有一个进程java.exe。该进程中至少一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。
扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程。
自定义一个线程有两种方式:
1. 定义一个类继承Thread,并复写其中的run()方法,并且new这个类后调用start方法启动。
2. 定义一个类实现Runnable接口,并复写其中的run()方法,再用new Thread(new 这个类).start方法启动。
下面用一个例子来说明这两种方法:
package test;public class Test4 {//用两种方式启动两个线程,加上主线程,共3个线程,各自打印名字+ipublic static void main(String[] args) {new Demo1().start();new Thread(new Demo2()).start();for(int i=0;i<30;i++){System.out.println(Thread.currentThread().getName()+"_"+i);}}}//继承Thread类的实现方式class Demo1 extends Thread{public void run(){for(int i=0;i<30;i++){System.out.println(Thread.currentThread().getName()+"_"+i);}}}//实现Runnable接口的实现方式class Demo2 implements Runnable{public void run(){for(int i=0;i<30;i++){System.out.println(Thread.currentThread().getName()+"_"+i);}}}随便截取一小段打印结果:
Thread-0_6Thread-0_7main_0Thread-0_8main_1Thread-1_0Thread-0_9Thread-1_1main_2Thread-1_2Thread-0_10Thread-1_3main_3Thread-1_4可以看出3个线程是并发执行的,交替打印,并无先后顺序。这便是多线程的好处所在了,可以同时执行多个任务,对于程序的效率性大规模提高。
但是多线程在访问同一个变量的时候,会出现安全性的问题,因为程序执行时一个CUP只能同时执行一个线程,多个线程是随机交替执行,可能导致一个线程还没运行完,另一个线程操作了这个数据,可能出现错误。
导致安全问题的出现的原因:
1. 多个线程访问出现延迟。
2. 线程随机性。
注:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。
所以,为了解决这个问题,便出现了线程同步的操作。
线程的同步:
格式:synchronized(锁的对象) { 需要同步的代码;}
特点:
同步的前提:
1. 同步需要两个或者两个以上的线程
2. 多个线程使用的是同一个锁
未满足这两个条件,不能称其为同步。
同步的弊端:
当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
下面演示同步的安全问题:
package test;public class Test5 {public static void main(String[] args) {Cus c = new Cus();Thread t1 = new Thread(c); //创建2个客户存钱,检测程序的安全问题Thread t2 = new Thread(c);t1.start();t2.start();}}class Bank{private int sum;public void add(int n){//synchronized(this) //注释掉的部分是同步代码,打印出有同步代码,和无同步代码的结果//{sum = sum + n;try{Thread.sleep(10);}catch(Exception e){} //为了看到现象,每次线程等待0.01秒System.out.println("银行总存款 sum="+sum);//}}}//这个一个客户类,每个客户每次存入银行100元,共存3次class Cus implements Runnable{private Bank b = new Bank();public void run(){for(int x=0; x<3; x++){b.add(100); }}}不加同步代码,打印结果为:
银行总存款 sum=200银行总存款 sum=200银行总存款 sum=400银行总存款 sum=400银行总存款 sum=600银行总存款 sum=600可以看到,这里存在很严重的安全问题,可能因为多线程的随机执行问题,使得打印结果错误,即程序出现了安全问题,必须用同步解决。
去掉同步的注释,即加上同步代码,打印结果为:
银行总存款 sum=100银行总存款 sum=200银行总存款 sum=300银行总存款 sum=400银行总存款 sum=500银行总存款 sum=600这个是理想的结果一样,成功解决了安全问题。所以在使用多线程时必须注意安全性的问题,这个隐患发生的概率是很小,但是一旦发生,就是致命的问题。
同步中的synchronized可以直接加在方法上,当作修饰符用,这样就不能指定同步的锁了。那么此时用到的锁是哪个呢?在非静态的方法中,同步用到的锁就是对象本身this。而在静态方法中,同步用到的是该方法所在类的字节码文件对象,即 (类名.class) 文件。
线程间的通信:
多个线程间是计算机随机交易执行的,那么有没有方法使得它们按照一定顺序执行呢?如果有,那么各个线程之间必然有一个通信的方式,使得线程知道对方的状态,这便是线程间的通信了。
首先,我们需要知道线程到底存在哪几个状态:
package test;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class Test6 {public static void main(String[] args) throws InterruptedException {Resource res = new Resource(); //创建一个源类,生成、消费公用里面的数据Produce pro = new Produce(res);Consume con = new Consume(res);new Thread(pro).start(); //两个生产线程、两个消费线程new Thread(pro).start();new Thread(con).start();new Thread(con).start();}}//建立一个商品生产、消费的类,要求交替执行线程,生产一个商品,消费一个商品。class Resource{private String name; //商品名字private int count = 1;//商品编号private boolean flag = false;//创建用于同步线程的锁,和新特性Conditionprivate Lock lock = new ReentrantLock();private Condition condition_pro = lock.newCondition();private Condition condition_con = lock.newCondition();//定义一个生产商品的方法public void produce(String name) throws InterruptedException{lock.lock();try {while (flag) //标记为了按生产一个消费一个的顺序执行condition_pro.await();this.name = name + (count++);System.out.println(Thread.currentThread().getName() + "。。生产了。。"+ this.name);flag = true;condition_con.signal();}finally{lock.unlock(); //释放锁必须执行}}//定义一个消费商品的方法public void consume() 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();}}}//定义生产商品的线程class Produce implements Runnable{private Resource res;Produce(Resource res){this.res = res;}public void run(){while(true){try {res.produce("商品");} catch (Exception e) {System.out.println("线程出问题了");}}}}//定义消费商品的线程class Consume implements Runnable{private Resource res;Consume(Resource res){this.res = res;}public void run(){while(true){try {res.consume();} catch (Exception e) {System.out.println("线程出问题了");}}}}这个程序运行结果截取一部分:
Thread-0。。生产了。。商品14639Thread-2。。消费了。。。。商品14639Thread-1。。生产了。。商品14640Thread-3。。消费了。。。。商品14640Thread-0。。生产了。。商品14641Thread-2。。消费了。。。。商品14641Thread-1。。生产了。。商品14642Thread-3。。消费了。。。。商品14642Thread-0。。生产了。。商品14643Thread-2。。消费了。。。。商品14643Thread-1。。生产了。。商品14644Thread-3。。消费了。。。。商品14644Thread-0。。生产了。。商品14645Thread-2。。消费了。。。。商品14645运行结果整齐的交替打印,并无安全问题发生,可见用新特性来实现同步有着效率更高的好处。
- 黑马程序员 多线程
- 黑马程序员:多线程
- 黑马程序员-java多线程
- 黑马程序员--java 多线程
- 黑马程序员_java多线程
- 黑马程序员-java多线程
- 黑马程序员_多线程
- 黑马程序员 多线程
- 黑马程序员_JAVA多线程
- 黑马程序员—多线程
- 黑马程序员- 多线程
- 黑马程序员_多线程
- 黑马程序员--多线程
- 黑马程序员_多线程
- 黑马程序员--Java多线程
- 黑马程序员---多线程
- 黑马程序员__多线程
- 黑马程序员_多线程
- 求子数组的最大和
- log4j的一些配置
- 开博宣言,我的键盘之路
- JS获取服务器时间的方法
- CSDN博文精选:最受欢迎的系列专栏博客推荐
- 黑马程序员 多线程
- Win7自家OFFICE完美抠图 比ps更简单
- TOJ 1545
- 目前市场上流行的嵌入式操作系统
- ios xml 解析 (1)
- java反射机制
- jQuery页面滚动图片等元素动态加载实现
- 代码随记
- jogl glDepthFunc glhint