java多线程

来源:互联网 发布:网络犯罪的例子 编辑:程序博客网 时间:2024/05/29 07:41
多线程
1.1 多线程的概念


进程:正在进行中的程序。
线程:进程中一个负责执行的控制单元。


解析:
1.一个进程中可以有多个线程,称之为多线程。
2.一个进程中至少有一个线程,称为主线程

3.开启多个线程是为了同时运行多部分代码,每个线程都有自己运行的内容,这个内容称为线程需要执行的任务。


多线程的好处:解决了多部分代码同时运行的问题。
多线程的弊端:线程太多会导致效率的降低。
程序执行特点:其实, 多个应用程序同时执行都是CPU在做着快速的切换完成的。 这个切换是随机的。 CPU的切换是需要花
费时间的, 从而导致了效率的降低。
1.2 线程创建于执行


创建的方式之一:继承Thread类
1.定义一个继承Thread类。
2.覆盖Thread类中的run方法。
3.直接创建Thread的子类对象创建线程。
4.调用start方法开启线程并调用线程的run方法。


thread中的几种常见方法:
1.Thread.currentThread():获取当前现成的对象。
2.t.getName();获取t线程对象的名称。格式:Thread-编号(从0开始)


示例:
[java] view plaincopy
class Demo extends Thread{  
    String name ;  
    Demo(String name){  
        this.name = name;  
    }  
    public void run(){  
        for(int x=0;x<10;x++){  
            System.out.println(name+"... x="+x+"... ThreadName="+Thread.currentThread().getName());  
        }         
    }   
}  
  
class ThreadDemo{  
    public static void main(String[] args){  
        Demo d1 = new Demo("旺财");  
        Demo d2 = new Demo("小黄");  
  
        d1.start();  
        d2.start();  
          
        for(int x=0;x<10;x++){  
            System.out.println("x="+x+"... ThreadName="+Thread.currentThread().getName());  
        }     
    }  
}  


运行结果:
发现三个输出语句无序的打印出来。


创建的方式之二:实现Runnable接口
1.定义一个类实现Runnable接口。
2.覆盖接口中的run方法,将线程将要执行的代码放入其中。
3.通过Thread类创建对象,并把实现了Runnable的子类对象作为参数传给Thread类的构造函数。
4.调用Thread中的start方法,开启线程。


实现Runnable的好处:
1.将线程的任务从线程子类中分离出来,进行了单独的封装,安装面向对象的思想将任务封装成了对象。
2.避免了java单继承的局限性。
3.实现Runnable方法更为常见。
示例:
[java] view plaincopy
class Demo implements Runnable{  
    public void run(){  
        show();  
    }  
    public void show(){  
        for(int x=0;x<10;x++){  
            System.out.println("x="+x+"... ThreadName="+Thread.currentThread().getName());  
        }  
    }  
}  
  
class ThreadDemo{  
    public static void main(String[] args){  
        Demo d1 = new Demo();  
          
        Thread t1 = new Thread(d1);  
        Thread t2 = new Thread(d1);  
  
        t1.start();//可简写为new Thread(d1).start();  
        t2.start();  
    }  
}  


运行结果:两个线程无序的把0-9依次打印出来。
1.3 线程安全性问题。


举例说明问题:
需求:模拟4个窗口卖票的问题。
代码:
[java] view plaincopy
class Ticket implements Runnable{  
    private int num = 100;  
    public void run(){  
        while(true){          
            if(num>0){  
                System.out.println(Thread.currentThread().getName()+"...sale...."+num--);  
            }         
        }     
    }  
}  
  
class TicketDemo{  
    public static void main(String[] args){  
        Ticket t = new Ticket();  
          
        new Thread(t).start();  
        new Thread(t).start();  
        new Thread(t).start();  
        new Thread(t).start();  
    }  
}  


运行结果:
发现运行出现两个99和出现0,-1的情况。


分析:
因为cpu执行多线程时,是在多线程之间快速切换的,当线程-0执行到num=1但为对其自减就停止了,此时线程-1进来了num没有变化,输出后num自减num=0,回到线程-0,输出的就是num=0了。


线程安全问题的产生:
1.多个线程同时操作共享数据时。
2.操作的共享数据的代码有多条。
1.4 线程安全问题的解决方案


思路:将多个线程执行的代码封装在一个空间内,一次只让一个线程执行其中的代码。
方法:使用synchronized(同步)
格式:
synchronized(对象){
需要被同步的代码,即多线程执行的代码;
}


同步的好处:解决了多线程的安全性问题。
同步的弊端:当线程相当多时,同一时间只有一个线程在执行,降低了运行的效率。
同步的前提:必须有多个线程并使用同一个锁。


卖票修改后代码:
[java] view plaincopy
class Ticket implements Runnable{  
    private int num = 100;  
    Object obj  = new Object;//同步需要锁,锁为一个对象或者一个class文件,这里用Object对象。  
  
    public void run(){  
        while(true){  
            synchronized(obj){  
                if(num>0){  
                System.out.println(Thread.currentThread().getName()+"...sale...."+num--);  
                }  
            }                 
        }     
    }  
}  


利用同步带代码块解决安全性问题:


需求:储户,两个,每个都到银行存钱,每次存入100,共存三次。


[java] view plaincopy
class Bank{  
    private int sum;  
    public void add(int num){  
        synchronizd(this){  
            sum = sum + num;  
            System.out.println("sum = "+sum);  
        }         
    }  
}  
  
class Cus implements Runnable{  
    private Bank b = new Bank();  
    public void run(){  
        for(int x = 1;x<=3;x++){  
            d.add(100);  
        }  
    }  
}  
  
class BankDemo{  
    public static void main(String[] args){  
        Cus c = new Cus();  
  
        new Thread(c).strat();  
        new Thread(c).strat();  
    }  
}  


运行结果:
sum = 100
sum = 200
sum = 300
sum = 400
sum = 500
sum = 600




synchronized可作为修饰符放在函数中,称为同步函数。
格式:
public synchronized void add(int num){
同步代码;
}


同步代码和同步函数的区别:同步代码的锁为自定义的,而同步函数的锁为固定的this。建议使用同步代码块。
静态的同步函数使用的锁是干函数所属字节码文件对象,可以用 对象.getClass() 方法获取,也可以用 类.class() 表示,还可通过 Class.forName(类名) 获取。
1.5 死锁示例
一般出现在同步嵌套中,即同步中有同步,并且锁用的不同。


示例
[java] view plaincopy
class DieLock implement Runnable{  
  
  
    Object obja = new Object();  
    Object objb = new Object();  
    boolean flag = true;  
  
    public void run(){  
        if(flag){  
            synchroinzed(obja){  
                System.out.println("hellow obja");  
                synchroinzed(objb){  
                    System.out.println("hellow objb");  
                }  
            }  
        }  
        else{  
            synchroinzed(objb){  
                System.out.println("hellow objb");  
                synchroinzed(obja){  
                    System.out.println("hellow obja");  
                }  
        }  
    }  
      
}  
  
class Demo{  
    public static void main(String[] args){  
        DieLock d = new DieLock();  
  
        new Thread(d).strat();  
        new Thread(d).strat();  
    }  
}  


死锁运行后出现,光标停在一个位置不动。
1.6 线程间的通讯
当多个线程对同一资源进行操作,而操作的方法却不同时,就需要多线程通讯。
等待/唤醒机制涉及的方法:
1.wait():让线程处于冻结状态,被wait()的线程会被存储到线程池中。
2.notify():唤醒线程池中的线程(按被冻结的顺序)。
3.notifyAll():唤醒线程池中所有的线程。
4.这些方法都被定义在object中。
注意点:
1.这些方法必须定义在同步中,因为这些方法是用于操作线程状态的方法。
2.必须要明确到底操作的是哪个锁上的线程。
3.wait和sleep的区别
  1)wait可以指定时间也可以不指定时间,而sleep必须执行时间。
  2)在同步中,对cup执行权和锁的处理
     wait:释放执行权,释放锁。
    sleep:释放执行权,不释放锁。
4.两种方法都涉及到 InterruptedException 需要处理
输入/输出 姓名/性别 例:
[java] view plaincopy
class Resource{  
    private String name;  
    private String sex;  
    boolean flag = false;  
      
    public synchronized viod set(String name ,String age){  
        if(flag){  
            try{  
                this.wait();  
            }  
            catch(InterruptedException e){  
                e.printStackTrace();  
            }  
        }  
        this.name = name ;  
        this.sex = sex;  
        flag = true;  
        this.notify();        
    }  
  
    public synchronized void out(){  
        if(!flag){  
            try{  
                this.wait();  
            }  
            catch(InterruptedException e){  
                e.printStackTrace();  
            }  
        }  
        System.out.println("name = "+name+"sex = "+sex);  
        flag = false;  
        this.notify();  
    }   
}  
  
class Input implements Runnable{  
    Resource r ;  
    Input(Resource r){  
        this.r = r;  
    }  
      
    public void run(){  
        int x = 0  
        while(true){  
            if(x==0)  
                r.set("张华","男");  
            if(x==1)  
                r.set("丽丽","女");  
            x=(x+1)%2;  
        }  
    }  
}  
  
class Output implement Runnable{  
    Resource r;  
    Out(Resource r){  
        this.r = r;  
    }  
      
    public void run(){  
        while(true){  
            r.out();  
        }  
    }  
}  
  
class ResourceDemo{  
    public static void main(String[] args){  
        Resource r = new Resource;  
          
        new Thread(new Input(r)).strat();  
        new Thread(new OutPut(r)).strat();  
    }  
}  


运行结果:
name = 张华sex =男
name = 丽丽sex =女
无序交替出现。
注意:当对资源的同一方法有多个线程在操作,判断用while,而且唤醒用notifyAll()。
1.7 JDK1.5特性(Lock)
Lock接口:出现代替同步代码块或者同步函数,将同步的隐式操作变成了显示操作。同时更为灵活,可以一个锁上加上多个监视器。
格式:
1.获得锁对象
  Lock lock = new ReentrantLock();
  lock.lock();
2.获得监视器(可获得多个监视器)
  Condition con_p = lock.newCondition();
  Condition con_c = lock.newCondition();
3.等待(有抛出异常)
  con_p.await();
4.唤醒
  con_p.signal();
5.释放锁(在finally语句中为一定要执行语句)
  lock.unlock();


示例:
多个生产者、消费者


[java] view plaincopy
class Resource{  
    String name ;  
    int count = 1;  
    boolean flag = false;  
      
    Lock lock = new ReentrantLock();  
  
    Condition con_p = lock.newCondition();  
    Condition con_c = lock.newCondition();  
  
  
    public void set(String name){  
        lock.lock();  
  
        try{  
            while(flag){  
                try{  
                    con_p.await();  
                }catch(InterruptedException e){  
                    e.printStackTrace();  
                }  
            }  
            this.name = name + count;  
            count++  
            System.out.println(Thread.currentThread().getName()+"..生产.."+this.name);  
            flag = true;  
            con_c.signal();  
        }  
        finally{  
            lock.unlock();  
        }  
    }  
  
    public void out(){  
        lock.lock();  
  
        try{  
            while(!flag){  
                try{  
                    con_c.await();  
                }catch(InterruptedException e){  
                    e.printStackTrace();  
                }  
            }  
            System.out.println(Thread.currentThread().getName()+"..消费.."+this.name);  
            flag = false;  
            con_p.signal();  
        }  
        finally{  
            lock.unlock();  
        }  
    }  
}  
  
class Producer implements Runnable{  
    Resource r;  
    Producer(Resource r){  
        this.r = r;  
    }  
      
    public void run(){  
        while(true){  
            r.set("烤鸭");  
        }  
    }  
}  
class Consumer implements Runnable{  
    Resource r;  
    Producer(Resource r){  
        this.r = r;  
    }  
      
    public void run(){  
        while(true){  
            r.out();  
        }  
    }  
}  
class ResourceDemo{  
    public static void main(String[] args){  
        Resource r = new Resource;  
          
        new Thread(new Producer(r)).strat();  
        new Thread(new Producer(r)).strat();  
        new Thread(new Consumer(r)).strat();  
        new Thread(new Consumer(r)).strat();  
    }  
}  


运行结果:
生产消费同一产品依次输出。
1.8 线程中其他方法:
1.停止线程:stop方法已经过时,怎么控制线程的结束,线程任务中都会有循环结构,只要控制循环就能结束任务。
2.t.interrupt():强制将处于冻结状态的t线程唤醒,但出现了InterruptedException异常需要处理。
3.t.setDeamon(true):守护线程,随着主线程的结束而结束。
4.t.join():抢夺执行权,等t线程运行完后在执行其他线程内容。
5.t.toString:返回线程的字符串表示形式:线程名称 优先级 线程组
6.t.setPriority(5):设置t线程的优先级(0-9),默认为5
7.t.yeild():停止t线程,执行其他线程。

0 0
原创粉丝点击