疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Java多线程_基本操作


 

 
什么是多线程:
  进程:正在运行的程序,QQ 360 ......
 
       线程:就是进程中一条执行程序的执行路径,一个程序至少有一条执行路径。(360中的杀毒 电脑体检 电脑清理 同时运行的话就需要开启多条路径)
 
  每个线程都有自己需要运行的内容,而这些内容可以称为线程要执行的任务。
 
  开启多线程是为了同时运行多部分代码。
 
  好处:解决了多部分需要同时运行的问题
 
  弊端:如果线程过多,会导致效率很低(因为程序的执行都是CPU做着随机 快速切换来完成的)
 
  JVM在启动时,就有着多个线程启动
 
  在多线程运行过程中,如有线程抛出了异常,那么该线程会停止运行(出栈),但是并不影响其他线程的正常运行。
 
线程的四种状态:
 
 
创建线程:
  方法一:继承Thread类
 
  方法二:实现Runnable接口
 
  分析:创建线程的目的是为了开辟一条执行路径,让线程去运行指定的代码(执行路径的任务),实现和其他线程同时执行任务。
 
     JVM创建的主线程任务都定义在了mian方法中;自定义的线程任务将要封装在Thread类的run方法中。
 
 
 
  方法一的步骤:1:继承Thread类   2:重写run方法(封装任务)  3:实例化Thread类的子类对象   4:调用start方法开启线程(这个方法会调用run方法来执行任务)
 
复制代码
 1 class Text{
 2     public static void main(String[] args) {
 3         aThread Dome=new aThread();
 4         Dome.start();
 5         System.out.println(Thread.currentThread().getName());
 6     }
 7 }
 8 class aThread extends Thread{
 9     public void run(){
10         System.out.println(Thread.currentThread().getName());
11     }
12 }
复制代码
currentThread()是一个静态方法,它返回的是当前正在运行线程的这个对象,然后调用getName方法返回这个线程的名字;
 
名字是Thread-编号(从0开始),主函数的名字就是main。
 
Thread类中有个接收String类型参数的构造方法,我们传入的数据可以自定义线程的名字。如下代码:
 
复制代码
1 class aThread implements Runnable{
2     aThread(String a){
3         super(a);
4     }
5     public void run(){
6         System.out.println(this.getName());
7     }
8 }
复制代码
 
 
  如果一个子类已经有了父类,但是它需要开启多线程来执行任务,那么就要用到Runnable这个接口来扩展该子类的功能(避免了java的单继承局限性)
 
  Runnable这个接口只有一个run一个抽象方法,所以Runnable接口的出现仅仅是为了用run封装任务而存在的;而且Thread类也实现了Runnable接口
 
  
 
  方法二的步骤:1:实现Runnable接口 2:重写run方法(封装任务) 3:创建Thread线程a  创建Runnable子类对象t  
 
         4:将t作为参数传递给a(Thread有个构造方法是用来接收Runnable类型参数的;因为任务都封装在了Runnbale子类的run方法中,
 
           在开启线程的时候就要明确线程的任务,否则Thread会调用自己的run方法,Runnbale子类中的任务将永远不会被执行到)
 
         5:开启线程(start)
 
复制代码
 1 class Text{
 2     public static void main(String[] args) {
 3         aThread t=new aThread();
 4         Thread a=new Thread(t);
 5         a.start();
 6         System.out.println(Thread.currentThread().getName());
 7     }
 8 }
 9 class aThread implements Runnable{
10     
11     public void run(){
12         System.out.println(Thread.currentThread().getName());
13     }
14 }
复制代码
  实现Runnable接口的好处:1.将任务从Thread子类中分离出来,进行单独的封装;按照面向对象的思想将任务封装成了对象
 
               2.避免了JAVA单继承的局限性
 
 
 
多线程安全问题:
  原因: 1.多个线程操作共享数据
 
             2.操作共享数据的代码有多条
 
                  当一个线程在执行共享数据的多条代码时,有其他的线程参与进来,就会导致线程安全问题的产生。
 
  例:
 
复制代码
 1 class Text{
 2     public static void main(String[] args) {
 3         aThread t=new aThread();
 4         Thread a=new Thread(t);
 5         Thread a1=new Thread(t);
 6         Thread a2=new Thread(t);
 7         Thread a3=new Thread(t);
 8         a.start();
 9         a1.start();
10         a2.start();
11         a3.start();
12     }
13 }
14 class aThread implements Runnable{
15     private int mun=100;
16     public void run(){
17         while(mun>0){
18             try{Thread.sleep(5);}catch(InterruptedException e){}
19             /*为了能够看得清楚,让线程冻结,sleep会抛出异常由于Runnable接口并没有抛出异常
20             ,所以其子类也不能抛,只能try catch去捕捉异常。*/
21             System.out.println(Thread.currentThread().getName()+"....."+mun--);
22         }/*运行的结果中会出项这样的情况Thread-3.....42  Thread-0.....42
23                 (如果是卖票的话我们可以理解为这两个线程都卖了42号这个票,这是不允许的)*/
24     }
25 }
复制代码
解决问题:
  思路:当有线程操作共享数据的代码块时,不允许其他线程参与进来,即同步(简单说就是给共享数据的代码块安个锁,线程进来的时候都要判断一下是不是有别的线程正在操作共享数据代码块)
 
  同步的好处:解决了安全问题
 
  同步的弊端:相对而言效率降低了(因为每次都需要判断锁)
 
  注意:这几个线程一定要使用同一个锁
 
  方法一:同步代码块
 
  方法二:同步函数
 
  关键字:synchronized
 
  
 
  实现方法一(同步代码块):
 
复制代码
 1 class Text{
 2     public static void main(String[] args) {
 3         aThread t=new aThread();
 4         Thread a=new Thread(t);
 5         Thread a1=new Thread(t);
 6         Thread a2=new Thread(t);
 7         Thread a3=new Thread(t);
 8         a.start();
 9         a1.start();
10         a2.start();
11         a3.start();
12     }
13 }
14 class aThread implements Runnable{
15     private int mun=100;
16     private Object obj=new Object();
17     public void run(){
18         while(mun>0){
19             synchronized(obj){
20                 if(mun<=0)return;
21                 try{Thread.sleep(5);}catch(InterruptedException e){}
22             System.out.println(Thread.currentThread().getName()+"....."+mun--);
23             }    
24         }
25     }
26 }
复制代码
同步代码块的锁是可以自定义的,这里的锁我自定义的是Obiect类的对象obj
 
 
 
  实现方法二(同步函数):
 
复制代码
 1 class Text{
 2     public static void main(String[] args) {
 3         aThread t=new aThread();
 4         Thread a=new Thread(t);
 5         Thread a1=new Thread(t);
 6         Thread a2=new Thread(t);
 7         Thread a3=new Thread(t);
 8         a.start();
 9         a1.start();
10         a2.start();
11         a3.start();
12     }
13 }
14 class aThread implements Runnable{
15     private int mun=100;
16     17     public void run(){
18         while(mun>0)
19         show();
20     }
21     public synchronized void show(){
22             if(mun<=0)return;
23             try{Thread.sleep(5);}catch(InterruptedException e){}
24             System.out.println(Thread.currentThread().getName()+"....."+mun--);    
25     }
26 }
复制代码
同步函数的锁是固定的this
 
静态同步函数的锁,是这个函数所属的字节码对象(class文件)
 
 
 
  建议使用同步代码块
 
死锁:
  同步的嵌套,有两把锁,都拿着对方的锁,导致代码无法继续进行下去。
 
复制代码
 1 class Text{
 2     public static void main(String[] args) {
 3         aThread a=new aThread(true);
 4         aThread a1=new aThread(false);
 5         Thread t=new Thread(a);
 6         Thread t1=new Thread(a1);
 7         t.start();
 8         t1.start();
 9     }
10 }
11 class aThread implements Runnable{
12     private boolean flag;
13     aThread(boolean flag){
14         this.flag=flag;
15     }
16     public void run(){
17         if(flag){
18             synchronized(suo.suo1){
19                 System.out.println("我是if锁1");
20                 synchronized(suo.suo2){
21                     System.out.println("我是if锁2");    
22                 }
23             }
24         }else{
25             synchronized(suo.suo2){
26                 System.out.println("我是elsef锁2");
27                 synchronized(suo.suo1){
28                     System.out.println("我是else锁1");    
29                 }
30             }
31         }
32     }    
33 }
34 class suo{
35     public static final Object suo1=new Object();
36     public static final Object suo2=new Object();
37 }
复制代码
懒汉式在多线程中的应用:
  因为懒汉式并没有直接实例化对象,在线程0判断了if语句后进入临时阻塞状态,线程1也进入了进来,这就导致了实例化不唯一
 
 
 
实例化不唯一问题的示例代码:
 
复制代码
 1 public class Text1 {
 2 
 3     public static void main(String[] args) {
 4         Ab a=new Ab();
 5         Ab b=new Ab();
 6         a.start();
 7         b.start();
 8         try{Thread.sleep(10);}catch(InterruptedException e){}
 9         System.out.print(a.get()==b.get());
10     }
11 }
12 class Single{
13     private static Single a=null;
14     private Single(){
15     }
16     static Single  getSingle(){
17         if(a==null){
18             try{Thread.sleep(10);}catch(InterruptedException e){}
19             a=new Single();
20         }
21         return  a;
22     }
23 }
24 class Ab extends Thread{
25     private Single a;
26     public void run(){
27         a=Single.getSingle();
28     }
29     Single get(){
30         return a;
31     }
32 }
复制代码
多试几次输出的结果就会出现false
 
  
 
  解决问题(同步)(饿汉式代码应该如下修改):
 
复制代码
 1 class Single{
 2     private static Single a=null;
 3     private Single(){
 4     }
 5     static Single  getSingle(){
 6         if(a==null){
 7             synchronized (Single.class){
 8                 if(a==null){
 9                     a=new Single();
10                }
11             }
12         }
13         return  a;
14     }
15 }
复制代码
注意:在原有的饿汉式代码中多加了一个if判断,是为了提高效率,不然的还线程总是会去判断锁,效率下降,这也是不时用同步函数的原因。
 
   加了同步,是为了解决安全问题。
 
所以在开发的时候还是使用饿汉式好