Java多线程第一部分

本文详细介绍了Java中多线程的创建,包括通过Thread类、Runnable接口以及Callable和FutureTask的方式,并对比了它们的优缺点。此外,还讲解了线程的生命周期、调度策略如优先级、休眠、让步和插队,以及线程安全、同步方法、死锁问题。最后讨论了线程通讯、线程组和异常处理。
摘要由CSDN通过智能技术生成

多线程是实现并发机制的一种手段,多线程是为了同步完成多项任务,不是为了提高程序运行效率。

1 多线程的开启

1.1 Thread类创建多线程

class MyThread extends Thread{
    public void run(){  		//重写run
        int i = 1;
        while (i<=3) {
            System.out.println(this.getName() + ":" + i);
            i++;
        }
    }
}

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

/*运行结果:

Thread-1:1
Thread-0:1
Thread-1:2
Thread-0:2
Thread-1:3
Thread-0:3

进程已结束,退出代码为 0

*/

Thread提供了以下函数:
1.String getName():返回调用该方法的线程名字;
2.void start():使该线程开始执行;
3.void setName(String name):为线程设置名字,在默认情况下,主线程的名字为 main,用户启动的多个线程的名字依次为 Thread-0、Thread-1、Thread-2、…、Thread-n 等;
4.String getName():返回调用该方法的线程名字;
5.boolean isAlive():测试线程是否处于活动状态;

1.2 Runnable 接口创建多线程

采用 Runnable 接口的方式创建的多个线程可以共享同一个 target 对象的实例变量(Java只支持单继承)

ublic static void main(String[] args) {
        MyRunnable r = new MyRunnable();
        Thread th1 =new Thread(r,"线程1");// 将 target 作为运行目标来创建创建 Thread 类的对象
        Thread th2 =new Thread(r,"线程2");
        th1.start();;// 调用线程对象的 start() 方法来启动该线程
        th2.start();
    }
}
class MyRunnable implements Runnable{
        public void run(){
            int i = 1;
            while (i<=3) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
                i++;
            }
        }
}
/*运行结果:

线程2:1
线程1:1
线程2:2
线程1:2
线程2:3
线程1:3

进程已结束,退出代码为 0


*/

1.3 使用 Callable 和 FutureTask 创建线程

Runnable方法没有返回值,也不能抛出异常。Callable 接口提供了一个 call() 方法(可以有返回值,可以声明抛出异常)可以作为线程执行体。

class MyCallable implements Callable<Integer> {
    int i = 1;
    int sum = 0;
    public Integer call(){      //重写call方法
    for(i=1 ; i<4 ; i++ ) {
        System.out.println(sum += i);
    }
    return sum;
}
}

public class Demo03 {
    public static void main(String[] args) {
        //创建MyCallable对象
        MyCallable mc = new MyCallable();
        //使用FutureTask来包装MyCallable对象
        FutureTask<Integer> ft = new FutureTask<Integer>(mc);
		
        Thread thread = new Thread(ft);
        thread.start();     //线程就绪
		//用get方法读取ft的返回值
        try {
            int sum = ft.get();
            System.out.println("最终结果为: " + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println(e);
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}

1.4 创建线程的三种方式对比

1.继承 Thread 类
线程类已经继承了 Thread 类,不能再继承其它父类

2.实现Runnable接口创建多线程
避免了单继承的局限

3.实现 Runnable、Callable 接口的方式创建多线程
避免了单继承的局限,有返回值,可抛出异常。
弊端:编程较为复杂,如果需要访问当前线程,则必须使用 Thread. currentThread() 方法。

2 线程的生命周期

新建、可运行(就绪、运行)、阻塞、等待、计时等待、终止/死亡

3.0 线程的调度

如果一台电脑只有一个cpu,每个线程只有得到cpu的使用权的时候才能执行指令。java虚拟机按照一定的规律对其进行调度。

3.1 线程的优先级

java中可以通过设置优先级来改变线程的调用顺序。

setPriority(Thread.MIN_PRIORITY);//设置低优先级
setPriority(Thread.MAX_PRIORITY);//设置高优先级
//setPriority()为Thread类的方法

3.2 线程休眠

Thread类存在静态方法sleep()方法使其休眠(进入阻塞状态)

static void sleep(long millis)				//指定毫秒数
static void sleep(long millis, int nanos)	//指定毫秒数和微秒数

3.3 线程让步

yield()方法也可以使线程暂停,但是不会使其阻塞

3.4 线程插队

Thread中提供join()方法,当一个线程中调用其他线程的 join() 方法时,此线程将被阻塞,直到被调用的线程运行完成为止

3.5 后台线程

又叫守护线程,垃圾回收机制就是一个后台线程,如果所有的前台线程都死亡了,虚拟机便会退出,后台线程不会单独运行。
Thread中steDeamon(boolean on )可以创建后台线程。

4.多线程同步

4.1线程安全

当多个线程同时运行同一代码使,可能出现运行错误问题,这个问题可能会在多次运行时暴露出来。经典例题为——窗口买票问题。

public class Demo04 {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        Thread t1 = new Thread(ticket);
        Thread t2 = new Thread(ticket);
        Thread t3 = new Thread(ticket);
        t1.start();
        t2.start();
        t3.start();
    }
}

class Ticket implements Runnable{
    //定义票的总数
    private int n = 5;

    @Override
    public void run() {
        for(int i = 1 ; i <=100 ; i++){
            if (n>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("正在卖第" + n + "张票,剩余" + --n + "张票");
            }
        }

    }
}
/*运行结果:

正在卖第5张票,剩余4张票
正在卖第4张票,剩余3张票
正在卖第5张票,剩余4张票
正在卖第3张票,剩余2张票
正在卖第3张票,剩余0张票
正在卖第3张票,剩余1张票

进程已结束,退出代码为 0

*/

可以看到,第五和第三张票都被卖了多次,实际多次运行结果各有不同,但是都可以说明,线程的运行不同步,这就是线程安全问题。

4.2 同步方法

方法一:同步监视器可以解决上述问题

public class Demo04 {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        Thread t1 = new Thread(ticket);
        Thread t2 = new Thread(ticket);
        Thread t3 = new Thread(ticket);
        t1.start();
        t2.start();
        t3.start();
    }
}

class Ticket implements Runnable{
    //定义票的总数
    private int n = 5;

    @Override
    public void run() {
        for(int i = 1 ; i <=100 ; i++){
            synchronized (this){		//同步监视器
            if (n>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("正在卖第" + n + "张票,剩余" + --n + "张票");
            }
            }
        }

    }
}
/*结果:

正在卖第5张票,剩余4张票
正在卖第4张票,剩余3张票
正在卖第3张票,剩余2张票
正在卖第2张票,剩余1张票
正在卖第1张票,剩余0张票

进程已结束,退出代码为 0

*/

方法二:同步方法


public class Demo04 {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        Thread t1 = new Thread(ticket);
        Thread t2 = new Thread(ticket);
        Thread t3 = new Thread(ticket);
        t1.start();
        t2.start();
        t3.start();
    }
}

class Ticket implements Runnable{
    //定义票的总数
    private int n = 5;

    @Override
    public synchronized void run() {		//此处使用了同步方法synchronized关键字
        for(int i = 1 ; i <=100 ; i++){

            if (n>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("正在卖第" + n + "张票,剩余" + --n + "张票");
            }

        }

    }
}

原理:当线程执行同步代码块时,会先检查同步监视器的标志位,默认为1,若有线程在执行代码块,会将标志改为0,下一个编程若看到标志位为0,便会进入堵塞状态,直到标志位为1,继续执行。

4.3 死锁问题

当不同的线程分别占用对方需要的资源不放弃,便会带来是死锁问题。在编写代码的时候应尽量避免,Thread类的suspend()方法也容易导致死锁问题。

5 多线程通讯

不同的线程在执行问题时,如果任务间有联系,则需要多线程通讯。
Objet类的通讯方法:
void wait() 、void notify() 、void notifyAll()。
以上三种方法只有在synchronized方法或者synchrinized代码块中才能使用。

6 线程组和未处理的异常

ThreadGroup可以创建线程组,他可以对线程进行分类管理,Java允许程序直接对线程进行控制。线程在与运行时,不能改变其线程组。
Thread(。。。)
Thread Group(。。。)
还有更多方法,方法的使用方法此处不做过多介绍

public class Demo05 {
    public static void main(String[] args) {
        ThreadGroup tg1 = Thread.currentThread().getThreadGroup();      //获得当前线程组(默认为主线程组)
        System.out.println("主线程的名字为:" + tg1.getName());
        System.out.println("主线程是否为后台线程:" + tg1.isDaemon());
        SubThread st = new SubThread("这是主线程组的线程");
        st.start();
        ThreadGroup tg2 = new ThreadGroup("这是一个新线程组");       //新建一个线程组
        tg2.setDaemon(true);               //设置线程组的后台程序状态
        System.out.println("新线程组的是否为后台线程组:" + tg2.isDaemon());
        SubThread st2 = new SubThread(tg2 , "这是新线程组的线程");
    }
}

class SubThread extends Thread{
    public SubThread(String name){
        super(name);
    }
    public SubThread(ThreadGroup group , String name){          //ThreadGroup代表一个线程组名
        super(group , name);
    }
    public void run(){
        for (int i = 0; i<=5 ;i++){
            System.out.println(this.getName() + "第" + i + "次运行");
        }
    }
}/*运行结果:

主线程的名字为:main
主线程是否为后台线程:false
新线程组的是否为后台线程组:true
这是主线程组的线程第0次运行
这是主线程组的线程第1次运行
这是主线程组的线程第2次运行
这是主线程组的线程第3次运行
这是主线程组的线程第4次运行
这是主线程组的线程第5次运行

进程已结束,退出代码为 0

*/

此处测试了很多种相关线程组的方法,仅供参考。

线程未知异常:当JVM结束该线程之前没有查到对应的Thread.UncaughtExceptionHandler时,需要一个类来检测其异常

public class Demo06 {
    public static void main(String[] args) {
        Thread.currentThread().setUncaughtExceptionHandler(new MyHander());
        int i = 1/0;
        System.out.println("系统走到这了");
    }
}

class MyHander implements Thread.UncaughtExceptionHandler {
    public void uncaughtException(Thread t,Throwable e){
        System.out.println(t + "线程出现了异常" + e);
    }
}
/*运行结果:

Thread[main,5,main]线程出现了异常java.lang.ArithmeticException: / by zero

进程已结束,退出代码为 1

*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值