黑马程序员——java基础日记——多线程(1)
来源:互联网 发布:帝国cms代码高亮 编辑:程序博客网 时间:2024/05/29 03:27
多线程
-----------android培训、java培训、java学习型技术博客、期待与您交流!------------
一、线程的理解
进程是正在执行的程序。在一个进程中至少要要有一个线程。线程是进程中一个负责程序执行的控制单元(执行路径)。 多条执行路径就是多线程。
多线程的好处:
解决了多部分代码同时运行的问题。没个线程都有自己执行的路径(或者说执行内容,代码块),开启多线程能同时执行多个代码块。
多线程的弊端:
线程太多,会导致效率变低。因为多个应用程序同时执行是CPU的快速切换完成的,而切换也是要花费时间的。
二、线程的创建
方式一:通过继承Thread类
1.定义一个类继承Thread类。
2.覆盖Thread类中的run方法。
3.直接创建Thread的子类对象创建线程。
4.调用start方法开启线程并调用线程的任务run方法执行。
如:
package com.heima;class person extends Thread{public void run(){System.out.println("class person is run");}}abstract class test {public static void main(String[] args) {person p = new person();//创建线程p.run();//这里只是调用了线程中的run方法,还没有开启线程//p.start();//这里才是开启线程,开启并调用run。}}
方式二:实现Runnable接口
1.定义类实现Runnable接口。
2.覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3.通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。
4.调用线程对象的start方法开启线程。
如:
package com.heima;class person implements Runnable{public void run(){System.out.println("class person is run");}}abstract class test {public static void main(String[] args) {person p = new person();//新建Runnable子类对象pThread t = new Thread(p);//将p作为参数传递t.start();//开启线程}}
实现Runnable接口的好处:
1.将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象的思想将任务封装成对象。
2.避免了Java单继承的局限性。所以,创建线程的第二种方式较为常用。
多线程与单线程的区别:
如:
单线程:
package com.heima;class person{private String name;public person(String name){this.name=name;}public void show(){for(int i=0;i<10;i++){System.out.println(name+":"+i);}}}abstract class test {public static void main(String[] args) {person p1 = new person("张三");p1.show();person p2 = new person("李四");p2.show();}}
结果:
由此可看出:在单线程中,在在上一句代码执行完,下一句代码才能执行。
多线程:
package com.heima;class person extends Thread{private String name;public person(String name){this.name=name;}public void run(){show();}public void show(){for(int i=0;i<10;i++){System.out.println(name+":"+i+" Threadname="+Thread.currentThread().getName());}}}abstract class test {public static void main(String[] args) {person p1 = new person("张三");p1.start();person p2 = new person("李四");p2.start();}}
结果:
可以看出:两条线程是随机的切换运行的。也就是之前说的同时运行。
三、线程的安全问题
多个线程在操作同一数据的时候容易出现安全问题
如例:
模拟4个线程同时卖100张票。
package com.heima;class Ticket implements Runnable{ private int num = 100; public void run(){ while(true ){ if(num > 0){ try{ Thread. sleep(10); } catch(InterruptedException e){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "...sale..." + num--); } } }}class test{ public static void main(String[] args){ Ticket t = new Ticket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); Thread t3 = new Thread(t); Thread t4 = new Thread(t); t1.start(); t2.start(); t3.start(); t4.start(); }}
结果:
分析:安全问题的原因在于Thread-0通过了if判断后,在执行到“num--”语句之前,num此时仍等于1。CPU切换到Thread-1、Thread-2、Thread-3之后,
这些线程依然可以通过if判断,从而执行“num--”的操作,因而出现了0、-1、-2的情况。
那这写安全问题呀怎么决解捏?
方法一、
使用同步代码块。将被多条线程操作的代码封装起来,当有线程执行这些的代码的时候,其它线程都不能参与执行。
格式:
synchronized(对象){
需要被同步的代码;
}
如:
package com.heima;class Ticket implements Runnable{ private int num = 100; Object obj =new Object(); public void run(){ while(true){ synchronized(obj){ if(num>0){ System.out.println(Thread.currentThread().getName()+"...sale..."+num--); } } } }}class test{ public static void main(String[] args){ Ticket t = new Ticket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); Thread t3 = new Thread(t); Thread t4 = new Thread(t); t1.start(); t2.start(); t3.start(); t4.start(); }}
分析:当num=1时,其他的线程无法进入同步代码块,只能等当前操作的线程执行完才能进入同步代码块。当前线程执行完时,num已经等于0,无法通过判断,故停止售票。
同步的前提:必须有多个线程并使用同一个锁。
好处:解决类线程的安全问题。
弊端:每个线程都要去判断同步上的锁,一旦线程太多,就会降低程序运行效率。
方法二、
使用同步函数。
格式:在函数上加上synchronized修饰符即可。
如:public synchronized void show(){}
同步函数的锁是固定的this,同步代码块的锁是任意的对象,那么如果同步函数和同步代码块都使用this
作为锁,就可以实现同步。
静态的同步函数使用的锁是该函数所属字节码文件对象,可以用getClass方法获取,也可以用当前类名.class
表示。
四、死锁
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
死锁示例:同步嵌套。
package com.heima;class Ticket implements Runnable{ private int num = 100; boolean flag = true; Object obj = new Object(); public void run(){ if(flag ){ while(true ){ synchronized(obj ){ if(num > 0){ 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 test{ 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(); }}
有可能出现这种情况:
分析:run方法中的同步代码块需要获取obj对象锁,才能执行代码块中的show方法。show方法中则要获取this对象锁。
也就是说在程序运行时,当t1获取到run中的obj对象锁执行同步代码块,t2获取show中的this对象锁执行show方法。
同步代码块中的show无法获取this对象锁无法执行,show方法中的同步代码块也无法获取obj对象锁无法执行,从而造成死锁。
五、多线程中的单例设计模式
饿汉式单例设计模式是没有安全问题的,因为不存在多个线程共同操作数据,在这里主要说一下懒汉式单例设计模式。
懒汉式单例设计模式:
package com.heima;class person{private static person p =null;private person(){}public static person getInstance(){if(p==null){//没有同步,容易出现安全问题p=new person();}return p;}}
为了解决安全问题,我们可以添加同步函数来解决。
如:
public static synchronized person getInstance(){ if(p==null){ p=new person(); } return p;}
不过这样每次都要去判断同步上的锁,效率比较低。
我们还可以再优化一下,如使用同步代码块,添加双重判断。
public static person getInstance(){if(p==null){synchronized(person.class){if(p==null)p=new person();}}return p;}
所有的线程先通过第一个if判断,如果对象已经存在,则不用再去看同步的锁,直接获取就行。如果对象不存在,再去判断锁。
获取锁之后,使用第二个if判断对象是否已经存在,不存在才新建,否则跳出锁直接返回对象。这样懒汉式的安全问题就解决啦!!
- 黑马程序员——java基础日记——多线程(1)
- 黑马程序员——java基础学习日记(1)
- 黑马程序员——Java学习日记(七)多线程
- 黑马程序员——java基础学习日记(10)
- 黑马程序员——java基础学习日记(9)
- 黑马程序员——java基础学习日记(8)
- 黑马程序员——Java基础学习日记(6)
- 黑马程序员——java基础学习日记(5)
- 黑马程序员——Java基础学习日记(4)
- 黑马程序员——Java基础学习日记(3)
- 黑马程序员——java基础学习日记(2)
- 黑马程序员_ JAVA学习日记—JAVA中的多线程
- 黑马程序员——Java基础--多线程(1)
- 黑马程序员——Java基础---多线程(1)
- 黑马程序员java学习日记——异常和多线程
- 黑马程序员——学习日记11 java多线程
- 黑马程序员——Java基础---多线程
- 黑马程序员——Java基础---多线程
- Ubuntu 如何切换到root账户
- iOS中常见的项目文件
- python 里的not and or
- 4.Python补充_Python简史
- iOS 获取屏幕分辨率大小错误的解决方法
- 黑马程序员——java基础日记——多线程(1)
- failed to create a child event loop
- HDU 1890 Robotic Sort [splay]
- Mac下texshop出现command not found问题
- Windows下mysql忘记root密码的解决方法
- Java多线程问题总结
- 多元线性回归
- Android 图片特效(一):色相、饱和度与亮度
- httpUrlConnection 的 setDoOutput 与 setDoInput