java多线程学习笔记

在java的学习过程中,不可避免的要接触到多线程的学习,下面是我的一写学习笔记,如有不妥的地方,还望各路大神不吝指教!!!

首先,我们简单说明一下进程和线程,进程简单直译就是正在进行中的程序,在进程中一个负责程序执行的控制单元(也可以理解为执行路径)就是线程。一个进程中可以有多条执行路径,即多路径,至少要有一个线程。开启多个线程,为了共同运行多个代码,每个线程都有自己的运行内容,这个内容就是线程要执行的任务。如果要调用多线程,一般有两种方法:

一、继承Thread类,其中必须要覆盖一个方法函数run(),然后创建线程对象,最后调用start()方法开启线程,start()方法会自动调用run()方法来执行这个线程的任务。

举个例子:

class Test extends Thread{
    private String name;
    public Test(String name){
        this.name = name;
    }
    public void run(){
        for(int i=0; i<10; i++){
            System.out.println(name+"run..."+i);
        }
    }

    public static void main(String[] args){
        Test t1 = new Test("a");
        Test t2 = new Test("b");
        t1.start();
        t2.start();
    }
}

我们会从执行结果中发现,a与b交替执行,这是因为线程a和线程b在互相争夺cpu资源交替执行。另外,我们还会发现在调用的start()方法,实际上是调用run()方法。那么我们为什么不直接去调用run()方法呢?线程的运行需要本地操作系统的支持才可以,查看start()源码会发现:


start()方法调用了start0()的方法,这个方法用了native关键字,这个关键字表示调用本地操作系统的函数,这就是为什么多线程的实现需要本地操作系统的支持。值得注意的是start()方法重复调用的话,会出现java.lang.IllegalThreadStateException异常(昨天我的同学还问我,线程在杀死后还能再次调用start开启吗,我天真地说为什么不呢,然后就呵呵了)。

二、实现Runnable方法实现多线程,当一个类已经是子类的时候,由于java不支持多继承,所以此时不能让该类再继承Thread类,只能用到Runnable接口。首先让一个类实现Runnable接口,然后覆盖接口中的run()方法,将线程的任务代码封装到run方法中,接着通过Thread类创建线程对象,并将Runnable接口的类的对象作为Thread类的构造函数的参数进行传递,最后调用线程对象的start()方法开启线程。

举个例子:

public class Zi extends Fu implements Runnable{
    public void run(){
        for(int i=0; i<10; i++){
            System.out.println(Thread.currentThread().getName()+"run..."+i);
        }
    }
}

public class Fu{
    public void show(){
        System.out.println("Fu is showing...");
    }
}

public class Main(){
    public static void main(String[] args){
         Fu fu = new Fu();
         Zi zi = new Zi();
         Thread t1 = new Thread(zi);
         Thread t2 = new Thread(zi);
         t1.start();
         t2.start();
    }
}

实现Runnable方法有两个好处:1.将线程的任务从Thread的子类中抽离出来,进行单独封装,按照面向对象的思想将任务封装成对象。2.有效的避免了java中单继承的局限性。3.实现Runnable接口适合多个相同的程序代码的线程去处理同一个资源,所以,一般创建多线程的时候,我们经常使用Runnable接口去实现。

值得注意的:main方法也是一个线程,在java中,每次程序至少启动两个线程,一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,就是在操作系统中启动了一个进程。在java中的所有的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到cpu的资源了。另外,主线程可以在子线程结束之前结束,并且子线程不受其影响,不会因为主线程的结束而结束。

最后,连带的说一下线程的其他方法,join()强制执行线程的方法,sleep(2000)线程休眠的方法(参数是休眠的时间按毫秒计算),interrupt()中断线程的方法。

在java程序中,只要前台有一个线程在运行,那么整个java程序进程就不会消失,只有将线程设置为后台线程,这样即使java进程消失了,次后台线程依然能够继续运行(这个又让我想去昨天我同学跟我提到过的线程池的问题,因为线程的每次开启和杀死都是很耗费资源的,我们通过建立线程池可以有效的避免这种资源消耗的过程,线程池中的线程一般都是死循环,不会被杀死,这样就可以保证每次调用线程的时候,避免start()使用完后也不必kill()掉。)

class Test implements Runnable{
    public void run(){
        while(true){
            Systtem.out.println(Thread.currentThread().getName()+"running...");
        }
    }

    public static void main(){
        Test test = new Test();
        Thread demo = new Thread(test,"线程");
        demo.setDaemon(true);
        demo.start();
    }
}

多线程可能会出现安全问题,然而出现这些安全问题的条件是:1.多个线程在操作共享数据。2.操作共享数据的线程代码有多条。

这就会产生一个问题:当一个线程操作共享数据时,其他线程也参与了运算,就会导致线程安全问题的产生。当然解决这一类问题的思路:1.将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候其他线程是不可以参与运算的,必须要当前线程把这些代码执行完毕,其他线程才可以参与运算。解决方法可以用同步代码块来解决,其格式如下:(同步可以理解为在某一时间段中只有一个线程在运行,其他线程必须等到这个线程结束之后才能继续运行)

synchronized(对象){
    //需要同步的代码
}
一般都是把当前对象this当作是同步对象,举个例子(买票的经典例子):

class Ticket implements Runnable{
    private int count = 5;
    public void run(){
        for(int i=0; i<10; i++){
            synchronized(this){
                if(count>0){
                    try{
                        Thread.sleep(1000);
                    }catch(InterruptedException e){
                         e.printStackTrace();
                     }
                     System.out.println(Thread.currentThread().getName()+"正在售票:"+this.count--);
                }
            }
        }
    }
    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();
    }
}
当然也有另一种方法:采用同步方法。

同步方法的格式框架:

synchronized 方法返回类型方法名(参数列表){

      // your codes

}

还用上面同样的例子,采用同步方法来实现如下:

class Ticket implements Runnable{
private int count = 5;
public void run(){
    for(int i=0; i<10; i++)
         sale();
}

public synchronized void sale(){
     if(count > 0){
          try{
               Thread.sleep(1000);
          }catch(InterruptedExcetption e){
                e.printStackTrace();
          }
          System.out.println(Thread.currentThread().getName()+"正在售票"+count);
}
}

    public static void main(String[] args){
         Ticket t = new Ticket();
         Thread t1 = new Thread(t, "窗口1");
         Thread t2 = new Thread(t, "窗口2");
         Thread t3 = new Thread(t, "窗口3");
          t1.start();
          t2.start();
          t3.start();
    }
}
这里需要注意的是,当多个线程共享一个资源的时候需要进行同步,但是过多的同步有可能导致死锁问题。如何解决死锁的问题呢,,,我们回头再说,这个应该挺复杂的,该睡觉了 睡觉




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值