码迷,mamicode.com
首页 > 编程语言 > 详细

javase_多线程

时间:2015-09-06 16:09:43      阅读:186      评论:0      收藏:0      [点我收藏+]

标签:

一、进程和线程概念
1、进程:是一个正在执行中的程序。

2、线程:就是进程中的一个独立的控制单元,程序的一条执行路径,一个进程中至少有一个线程。

3、.class编译的时候有一个javac.exe,JVM启动也就是程序运行的时候有一个进程java.exe.java.exe进程中至少一个线程负责java程序的执行这个线程运行的代码存在于main方法中,该线程称之为主线程。其实jvm启动不止一个线程,还有负责垃圾回收机制的线程。

4、线程的运行状态图

技术分享

二、自定义线程第一种方式的步骤

1、定义类继承Thread。

2、复写Thread类中的run方法,将自定义代码存储在run方法,让线程运行,此处有模板方法设计模式的思想。

3、调用线程的start方法,该方法两个作用:启动线程,线程进入就绪状态,调用run方法。

例1:

class Demo extends Thread{
  @Override
public void run(){ //或者把run方法写在ThreadDemo1中,ThreadDeom1继承Thread. for(int x=0; x<100; x++) System.out.println("demo run----"+x); } } public class ThreadDemo1 { public static void main(String[] args) { Demo d = new Demo();//创建好一个线程。 d.start();//开启线程并执行该线程的run方法。 //d.run();//仅仅是对象调用方法。而线程创建了,并没有运行。 for(int x=0; x<100; x++) System.out.println("Hello World!--"+x); } }

例2:

public class ThreadDemo extends Thread{
    @Override
    public void run() {
        for(int i=0;i<100;i++)
            System.out.println("t1---"+i);
    }
    
    public static void main(String[] args) {
        ThreadDemo t1 = new ThreadDemo();
        t1.start();
        for(int i=0;i<100;i++)
            System.out.println("main---"+i);
    }
}

运行结果分析:
发现运行结果每一次都不同,因为多个线程都获取cpu的执行权,cpu执行到谁,谁就运行,明确一点,在某一个时刻,只能有一个程序在运行(多核除外)。cpu在做着快速的切换,以达到看上去是同时运行的效果,我们可以形象把多线程的运行描述为在互相抢夺cpu的执行权。这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长,cpu说了算。

例3:

public class ThreadDemo2 extends Thread{
    @Override
    public void run() {
        for(int i=0;i<100;i++)
            System.out.println(getName()+"---"+i);
    }
    public static void main(String[] args) {
        ThreadDemo2 t1 = new ThreadDemo2();
        ThreadDemo2 t2 = new ThreadDemo2();
        t2.setName("t2");
        t1.start();
        t2.start();
        for(int i=0;i<100;i++)
            System.out.println("main---"+i);
    }
}

例4:

public class ThreadDemo3 extends Thread{
    public ThreadDemo3(String name) {
        super(name);
    }
    @Override
    public void run() {
        for(int i=0;i<100;i++)
            System.out.println((Thread.currentThread()==this)+"-"+currentThread().getName()+"-"+i);
    }
    public static void main(String[] args) {
        ThreadDemo3 t1 = new ThreadDemo3("t1");
        ThreadDemo3 t2 = new ThreadDemo3("t2");
        t1.start();
        t2.start();
        for(int i=0;i<100;i++)
            System.out.println("main---"+i);
    }
}

运行结果分析:

1、线程都有自己默认的名称,Thread-编号,该编号从0开始。

2、setName()或者构造函数,设置线程名称,getName()获取线程名称。

3、static Thread currentThread(),获取当前线程对象。

三、自定义线程的第二种方式

例:简单的卖票程序,多个窗口同时卖票。

class Tick extends Thread{
    public Tick(String name) {
        super(name);
    }
    private int num=100;
    @Override
    public void run() {
        while(true)
            if(num>0)
                System.out.println(getName()+"--sale--"+num--);
    }
}

public class TicketDemo2 {
    public static void main(String[] args) {
        Tick t2 = new Tick("t2");
        Tick t1 = new Tick("t1");
        t2.start(); 
        t1.start(); 
    }
}

运行结果分析:

无法实现,没有共享num,因为不是同一个线程对象,各自有各自的堆栈空间,引出自定义线程的第二种方式。

 

第二种方式步骤:

1、定义类实现Runnable接口

2、覆盖Runnable接口中的run方法,将线程要运行的代码存放在该run方法中。

3、通过Thread类建立线程对象。

4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。

5、调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。

第二种方式解决方案:

class Ticket implements Runnable{
    private int num = 100;
    public void run(){
        while(true)
            if(tick>0)
                System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
    }
}

public class  TicketDemo{
    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);//创建了一个线程
        t1.start();
        t2.start();
        t3.start();
    }
}

运行结果分析:

发现实现了num的共享,虽然也不是同一个线程对象,观察源码发现,调用的都是t对象的run方法,只有一个t对象,所以实现了共享,但是打印出0,-1,-2等错票,多线程的运行出现了安全问题。

问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。


解决办法:
对多条操作共享数据的语句,只能保证里面有一个线程执行,在执行过程中,其他线程不可以参与执行。

Java对于多线程的安全问题提供了专业的解决方式,就是同步代码块。

synchronized(对象){
  需要被同步的代码
}
此处的对象就是锁(可以是任意对象),持有锁的线程可以在同步中执行,没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁,同步代码块底层其实有一个标志变量,比如0标志有线程,1标志没有线程,每个线程要进入前先检查标志。


好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源,但是必须的,为了安全。

6、同步代码块解决方案:

class Ticket3 implements Runnable{
    private  int tick = 1000;
    Object obj = new Object();
    public void run(){
        while(true){
            synchronized(obj){
                if(tick>0){
                    try{Thread.sleep(10);}catch(Exception e){}
                    System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
                }
            }
        }
    }
}

public class  TicketDemo3{
    public static void main(String[] args) {
        Ticket3 t = new Ticket3();
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        Thread t3 = new Thread(t);
        t1.start();
        t2.start();
        t3.start();
    }
}

7、面向对象的思想模拟火车站买票,结果做到最后发现,也是生产者消费者问题,有购票人是消费者,售票员是生产者,票是生产的产品,售票的窗口是装产品的容器。生活中大部分这种并发操作都是生产者消费者问题。

8、同步函数实例:

银行有一个金库,有两个储户分别存300元,每次存100,存3次。

目的:该程序是否有安全问题,如果有,如何解决?

同步函数,可以将操作共享数据的代码抽出封装到同步函数中,同步函数默认锁匙this,同步函数和同步代码块可以相互转换。

如何找问题:
1,明确哪些代码是多线程运行代码。
2,明确共享数据。
3,明确多线程运行代码中哪些语句是操作共享数据的。

class Bank{
    private int sum;
    //Object obj = new Object();
    public synchronized void add(int n){
        //synchronized(obj){
            sum = sum + n;
            try{Thread.sleep(10);}catch(Exception e){}
            System.out.println("sum="+sum);
        //}
    }
}

class Cus implements Runnable{
    private Bank b = new Bank();
    public void run(){        
        for(int x=0; x<3; x++)
            b.add(100);
    }
}

class  BankDemo{
    public static void main(String[] args) {
        Cus c = new Cus();
        Thread t1 = new Thread(c);
        Thread t2 = new Thread(c);
        t1.start();
        t2.start();
    }
}

9、同步的前提:
1、必须要有两个或者两个以上的线程。
2、必须是多个线程使用同一个锁,也就是锁定的是同一个对象。

例:

class Ticket3 implements Runnable{
    private  int tick = 100;
    Object obj = new Object();
    boolean flag = true;
    public  void run(){
        if(flag)
            while(true){
                synchronized(obj){  //此处不是同一锁
                    if(tick>0){
                        try{Thread.sleep(10);}catch(Exception e){}
                        System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
                    }
                }
            }
        else
            while(true)
                show();
    }
    public synchronized void show(){//this
        if(tick>0){
            try{Thread.sleep(10);}catch(Exception e){}
            System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
        }
    }
}

public class  TicketDemo3{
    public static void main(String[] args) {
        Ticket3 t = new Ticket3();
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        t1.start();
        try{Thread.sleep(10);}catch(Exception e){}
        t.flag = false;
        t2.start();
    }
}

运行结果分析:

Thread-0....code : 4
Thread-0....code : 3
Thread-1....show.... : 2
Thread-1....show.... : 1
Thread-0....code : 0

结尾处出现0的错误票数,表明还是使用了同步还是存在安全问题,是同步函数和同步代码块没有使用同一个锁,将同步代码块中的锁改为this,则运行结果正常。

 

10、如果同步函数被静态修饰后,使用的锁是什么呢?

通过验证,发现不在是this。因为静态方法中也不可以定义this。

静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。

静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class

 

11、回顾之前单例设计模式

1.饿汉式

class Single{
    private static final Single s = new Single();
    private Single(){}
    public static Single getInstance(){
        return s;
    }
}

2.懒汉式

class Single{
    private static Single s = null;
    private Single(){}
    public static  Single getInstance(){
        if(s==null){
            synchronized(Single.class){
                if(s==null)
                    //--->A;
                    s = new Single();
            }
        }
        return s;
    }
}

四、死锁

同步中嵌套同步时,会发生死锁。

例1:

class Test implements Runnable{
    private boolean flag;
    Test(boolean flag){
        this.flag = flag;
    }
    public void run(){
        if(flag){
            while(true){
                synchronized(MyLock.locka){
                    System.out.println(Thread.currentThread().getName()+"...if locka ");
                    synchronized(MyLock.lockb){
                        System.out.println(Thread.currentThread().getName()+"..if lockb");                    
                    }
                }
            }
        }
        else{
            while(true){
                synchronized(MyLock.lockb){
                    System.out.println(Thread.currentThread().getName()+"..else lockb");
                    synchronized(MyLock.locka){
                        System.out.println(Thread.currentThread().getName()+".....else locka");
                    }
                }
            }
        }
    }
}

class MyLock{
    static Object locka = new Object();
    static Object lockb = new Object();
}

public class  TicketDemo3{
    public static void main(String[] args) {
        new Thread(new Test(true)).start();
        new Thread(new Test(false)).start();
    }
}

例2:

class Ticket implements Runnable{
    private  int tick = 1000;
    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(){//this
        synchronized(obj){
            if(tick>0){
                try{Thread.sleep(10);}catch(Exception e){}
                System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
            }
        }
    }
}

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(Exception e){}
        t.flag = false;
        t2.start();
    }
}

五、生产者消费者问题

例1:

class Product{
    private int id;
    Product(int id){
        this.id=id;
    }
    public String toString() {
        return "Product [id=" + id + "]";
    }
}

class Box{
    Product[] p=new Product[10];
    int index=0;
    public synchronized void put(Product pro){
        while(index==p.length){
            try {wait();} catch (InterruptedException e) {}
        }
        notifyAll();
        p[index]=pro;
        index++;
    }
    public synchronized Product pop(){
        while(index==0){
            try {wait();} catch (InterruptedException e) {}
        }
        notifyAll();
        index--;
        return p[index];
    }
}

class Producter implements Runnable{
    Box box=null;
    Producter(Box box){
        this.box=box;
    }
    public void run(){
        for(int i=0;i<20;i++){  //每个生产者生产20个
            Product pro = new Product(i);
            box.put(pro);
            System.out.println(Thread.currentThread().getName()+"生产了:"+pro);
        }
    }
}

class Customer implements Runnable{
    Box box=null;
    Customer(Box box){
        this.box=box;
    }
    public void run(){
        while(true){
            Product pro = box.pop();
            System.out.println(Thread.currentThread().getName()+"消费了:"+pro);
        }
    }
}

public class ProducerAndCustomer {
    public static void main(String[] args) {
        Box box = new Box();
        Producter p = new Producter(box);
        Customer c = new Customer(box);
        new Thread(p).start();
        new Thread(p).start();
        new Thread(p).start();
        new Thread(c).start();
        new Thread(c).start();
        new Thread(c).start();
    }
}

运行结果分析:

wait()函数必须拿到对象锁才能调用,wait()以后这个锁不在归当前线程所持有,sleep()的话还持有锁,notify()叫醒一个正在当前对象锁上等待的线程,wait和notify成对出现。

六、其它函数的使用

1、join

public class TestJoin {    
    public static void main(String args[]){
        Runner r = new Runner();
        Thread t = new Thread(r);
        t.start();
        try{t.join();}catch(InterruptedException e){}    //等待t线程执行完毕再执行主线程,也就是把当前线程合并到主线程
        for(int i=0;i<50;i++)
            System.out.println("主线程:" + i);
    }
}

class Runner implements Runnable {
    public void run() {
        for(int i=0;i<50;i++) 
            System.out.println("SubThread: " + i);
    }
}

 

2、线程优先级

public class TestPriority {
    public static void main(String[] args) {
        Thread t1 = new Thread(new T1());
        Thread t2 = new Thread(new T2());
        t1.setPriority(Thread.NORM_PRIORITY + 3);   //Thread类有且仅有三个关于线程优先级的字段
        t1.start();
        t2.start();
    }
}

class T1 implements Runnable {
    public void run() {
        for(int i=0; i<1000; i++) {
            System.out.println("T1: " + i);
        }
    }
}

class T2 implements Runnable {
    public void run() {
        for(int i=0; i<1000; i++) {
            System.out.println("------T2: " + i);
        }
    }
}

3、yield

public class TestYield {
      public static void main(String[] args) {
        MyThread3 t1 = new MyThread3("t1");
        MyThread3 t2 = new MyThread3("t2");
        t1.start(); t2.start();
      }
}
class MyThread3 extends Thread {
      MyThread3(String s){
          super(s);
      }
      public void run(){
        for(int i =1;i<=100;i++){
          System.out.println(getName()+": "+i);
          if(i%10==0)
            yield();
        }
      }
}

4、sleep,interrupt

如果线程在调用Object类的wait(),或者该类的join()或sleep()方法过程中受阻,则其中断状态将被清除,它还将收到一个InterruptException。  

public class TestInterrupt {
      public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
        try {Thread.sleep(10000);}
        catch (InterruptedException e) {}
        thread.interrupt();
      }
}

class MyThread extends Thread {
      boolean flag = true;
      public void run(){
        while(flag){
          System.out.println("==="+new Date()+"===");
          try {
            sleep(1000);
          } catch (InterruptedException e) {
              System.out.println("xxs");
          }
        }
      }
}

 

七、JDK5.0对Java并发编程提供了新的实现方式用来取代synchronized。  **掌握**

参考如下:

http://www.cnblogs.com/dolphin0520/p/3923167.html

 

ps匿名内部类在多线程代码中使用

class ThreadTest {
    public static void main(String[] args) {
        new Thread(){
            public void run(){
                for(int x=0; x<100; x++)
                    System.out.println(Thread.currentThread().getName()+"....."+x);
            }
        }.start();
        
        for(int x=0; x<100; x++)
            System.out.println(Thread.currentThread().getName()+"....."+x);

        Runnable r  = new Runnable(){
            public void run(){
                for(int x=0; x<100; x++)
                    System.out.println(Thread.currentThread().getName()+"....."+x);
            }
        };
        new Thread(r).start();
    }
}

 

javase_多线程

标签:

原文地址:http://www.cnblogs.com/wangweiNB/p/4784270.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!