JAVA多线程学习(一)

一、概念及其理论介绍
1. 线程(轻量级程序)类似于一个程序,也有开始、执行、结束,它是运行在程序内部的一个比进程更小的单元,使用线程的主要应用在于可以在一个程序中同时运行多个任务。每个Java程序都至少有一个线程-主线程。当一个Java程序启动时,JVM会创建主线程,并在该线程中调用程序的main()方法。
2. 多线程就是同时有多个线程在执行。在多CPU的计算机中,多线程的实现是真正的物理上的同时进行。而对于单CPU的计算机而言,实现的只是逻辑上的同时执行,在每个时刻,真正执行的只有一个线程,由操作系统进行线程管理调度,但由于CPU的速度很快,让人柑橘像是多个线程在同时运行。
3. 多线程与多进程的主要区别在于:
进程是一个进程中一段独立的控制流,一个进程可以拥有若干个进程。在多进程设计中各个进程之间的数据块是独立的,一般彼此不影响,要通过信号、管道等进行交流。而多线程设计中各个线程不一定独立,同一任务中的各个线程共享程序 段、数据段等资源。
4.Java中建立线程有两种方法:
一种是继承Thread类,另一种是实现Runnable接口,并通过Thread和实现Runnalbe的类来建立线程。
两种方法的区别:
这两种方法从本质是说是一致的,即都是通过Thread类来建立线程,并运行run()方法。但由于Java不支持多继承,因此,这个线程类如果继承了Thread,就不能再继承其他的类了,通过实现Runnable接口的方法来建立线程,可以在必要的时候继承和业务有关的类,形成清晰的数据类型。
3.Thread类的start()方法将调用run()方法,该方法用于启动线程并执行。但是,start()方法不能多次调用,如不能调用两次thread1.start()方法,否则会抛出一个IlleagalThreadStateException异常。
如:异常图片
二、创建线程
在Java中创建线程有两种方法:使用Thread类和使用Runnalbe接口。在使用Runnable接口时需要建立一个Thread实例。因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或子类的实例。
1.Thread 代码如下:

public class TreadTest {
    public static void main(String args[]){
        Thread t = Thread.currentThread();
        System.out.println("主线程是:" + t);
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2("thread2");
        thread1.start();
        thread1.start();
        thread2.start();
    }

}
class Thread1 extends Thread{
    public void run(){
        System.out.println(Thread.currentThread().getName());
    }
}

class Thread2 extends Thread{
    public Thread2(String name){
        super(name);
    }
    public void run(){
        System.out.println(Thread.currentThread().getName());
    }
}

2.创建线程的另一种方式是实现Runnable接口。Runnable接口中只有一个run() 方法,它为非Thread子类的类提供了一种激活方式。一个类实现Runnable接口后,并不代表该类是个“线程”类,不是直接运行,必须通过使用Thread类的实例才能创建线程并运行。
通过Runnable接口创建线程的步骤如下:
1)定义实现Runnable接口的类,并实现该接口中的run()方法;
2)建立一个Thread对象,并将实现Runnable接口的类的对象作为参数传递给Thread类的构造方法;
3)通过Thread类的start()方法启动线程,并运行。
代码如下:

public class RunnableTest {
    public static void main(String args[]){
        Runable1 ra = new Runable1();
        Thread thread = new Thread(ra);
        thread.start();
        System.out.println("[" + Thread.currentThread().getName() + "]");
        for(int i=0;i<30;i++){
            System.out.print("C");
        }
    }
}

class Runable1 implements Runnable{
    public void run() {
        // 获取当前线程的名字
        System.out.println(Thread.currentThread().getName());
        for(int i=0;i<30;i++){
            System.out.print("A");
        }
    }
}

三、线程状态
线程的状态分为七种:new(新生状态),runnable(就绪状态),running(运行状态),waiting(等待状态),sleeping(睡眠状态),block(阻塞状态),dead(死亡状态)。
线程详细介绍
1)new
当使用new来新建一个线程时,一个新的线程就诞生了。但除非在构造函数中调用了start()方法,否则这个新线程将作为一个新对象待在内存中基本什么事情也不做。当对这个线程调用了start()方法,或者这个线程的状态由new改变为runnable后,调度程序就可以把处理器分给这个线程。
线程处于等待状态时,可以通过Thread类的方法来设置线程的各种属性,如线程的优先级(setPriority)、线程名(setName)和线程的类型(setDaemon)etc.
2.runnable、running
把处理器分配给一个处于runnable的线程之后,这个线程的状态就变成了running。如果一个处于running状态的线程能够运行到结束或因某个为捕获的异常而使线程终止时,它的状态就变为dead。否则,这个线程的命运就取决于当前是否还有其他线程等待处理器运行。如果这个线程在当前所有处于runnable状态的线程中具有最高优先级,那么它就会继承执行,除非这个线程被运行程序的平台划分为时间片。当线程被划分为时间片时,一个处于running状态的线程将分配到一个固定间隔的处理器时间。具有相同优先级的线程的时间片调度将导致这些线程被轮流执行。如果正在执行的线程的代码包含了yeild()方法,那么这个处于running状态的线程将停止运行,并把处理器交给其他线程。
可以通过Thread类的isAlive()方法来判断线程是否处于运行状态。当线程处于running状态时,isAlive()返回true,当isAlive()返回false时,可能线程处于等待状态,也可以处于静止状态。

package TreadTest;

public class LifeCycle {
    public static void main(String args[]){
        Thread3 thread3 = new Thread3();
        System.out.println("等待状态[isAlive:" + thread3.isAlive() + "]");
        thread3.start();
        System.out.println("运行状态[isAlive:" + thread3.isAlive() + "]");
        try {
            thread3.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("线程结束[isAlive:" + thread3.isAlive() + "]");
    }
}

class Thread3 extends Thread{

    public void run() {
        int i = 0;
        while((++i)<1000)
        ;
    }
}

运行结果:
这里写图片描述
- 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
- 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
线程在下列情况会结束:

  • 线程run()方法的结尾;

  • 线程抛出一个未捕获异常或Error;

  • 调用interupt()方法中断线程;

  • 调用join()方法等待线程结束;

  • 调用stop()方法直接停止线程。
    四、线程优先级
    线程的优先级代表该线程的重要程度,当有多个线程同时处于可执行状态并等待获得CPU时间时,线程调度系统根据各个线程的优先级来决定CPU分配时间,优先级高的线程有更大的机会获得CPU时间。
    每一个线程都有一个优先级。默认情况下优先级通过静态常亮Thread.NORM_PRIORITY定义,该常亮的值为5。每个新线程均继承创建线程的优先级。线程的优先级可以通过setPriority()/getPriority()方法设置和获取,可将线程的优先级设置为MIN_PRIORITY(1)和MAX_PRIORITY(10)之间的值。
    当线程睡眠后,优先级就失效了,若想保证优先级继续有效,可考虑使用join()方法解决。
    注意:线程优先级高度依赖于操作系统,线程优先级不能保证线程的执行次序,应尽量避免使用线程优先级作为构建任务执行顺序的绝对标准。
    五、多线程
    一个小例子,通过多线程实现窗口中标签字颜色的不断变化,按钮中的文字不断改变。

package TreadTest;

import java.awt.Color;
import java.awt.Font;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;


public class MutiThreadDemo extends JFrame {
    JPanel p;
    JLabel lbl;
    JButton btn;
    String s[]={"Good","mornming","everybody","!!!"};
    public MutiThreadDemo(){
        super("多线程");
        p=new JPanel();
        lbl=new JLabel("颜色");
        //设置字体
        lbl.setFont(new Font("宋体",Font.BOLD,20));
        btn=new JButton(s[0]);
        //设置字体
        btn.setFont(new Font("黑体",Font.ITALIC,20));
        p.add(lbl);
        p.add(btn);
        this.add(p);
        this.setSize(300,200);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //创建子线程ColorChange,并启动
        new ColorChange().start();
        //创建子线程TextChange,并启动
        new TextChange().start();
    }

//定义子线程,让标签字的颜色不断变化
    class ColorChange extends Thread{
        public void run(){
            while(this.isAlive()){
                //随机产生颜色的3个基数0~255
                int r = (int)(Math.random()*256);
                int g = (int)(Math.random()*256);
                int b = (int)(Math.random()*256);
                //设置标签的前景颜色,即文字的颜色
                lbl.setForeground(new Color(r,g,b));
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

    //定义子线程,让按钮文本不断变化
    class TextChange extends Thread{
        public void run(){
            int i=0;
            while(this.isAlive()){
                btn.setText(s[i++]);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                if(i==s.length){
                    i=0;
                }
            }
        }
    }

    public static void main(String args[]){
        MutiThreadDemo f = new MutiThreadDemo();
        f.setVisible(true);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值