并发编程2-创建线程及多线程风险

本文详细介绍了Java中创建和管理线程的各种方法,包括继承Thread类、实现Runnable接口、使用匿名内部类、Callable接口、定时器、线程池以及Spring框架的多线程实现。此外,还探讨了线程间的数据共享、线程安全性和多线程环境下的风险。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.创建线程

1.1 继承Thread类

NewThreadExtend

public class NewThreadExtend extends Thread {

    public NewThreadExtend(String name) {
        super(name);
    }

    @Override
    public void run() {
        while (!interrupted()) {//和interrupt();搭配使用
            System.out.println(getName() + "线程执行了 .. ");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        NewThreadExtend d1 = new NewThreadExtend("first-thread");
        NewThreadExtend d2 = new NewThreadExtend("second-thread");

        d1.setDaemon(true);//守护线程

        d1.start();
        d2.start();

//		d1.stop(); //已过期.
        d1.interrupt();//JDK6才出现
    }

}


NewThreadExtendAndDaemon(守护线程)


public class NewThreadExtendAndDaemon extends Thread {

    public NewThreadExtendAndDaemon(String name) {
        super(name);
    }

    @Override
    public void run() {
        while(true) {
            System.out.println(getName() + "线程执行了 .. ");
        }
    }

    public static void main(String[] args) {
        NewThreadExtendAndDaemon d1 = new NewThreadExtendAndDaemon("first-thread");
        NewThreadExtendAndDaemon d2 = new NewThreadExtendAndDaemon("second-thread");

        d1.setDaemon(true);//守护线程
        d2.setDaemon(true);//守护线程

        d1.start();
        d2.start();

        try{
            Thread.sleep(2000);
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}

NewThreadExtendAndInterrupt

public class NewThreadExtendAndInterrupt extends Thread {

    public NewThreadExtendAndInterrupt(String name) {
        super(name);
    }

    @Override
    public void run() {
        while (!interrupted()) {//和interrupt();搭配使用
            System.out.println(getName() + "线程执行了 .. ");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        NewThreadExtendAndInterrupt d1 = new NewThreadExtendAndInterrupt("first-thread");
        NewThreadExtendAndInterrupt d2 = new NewThreadExtendAndInterrupt("second-thread");

        d1.start();
        d2.start();

        d1.interrupt();//JDK6才出现
    }
}

NewThreadExtendAndStop

public class NewThreadExtendAndStop extends Thread {

    public NewThreadExtendAndStop(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() + "线程执行了 .. ");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        NewThreadExtendAndStop d1 = new NewThreadExtendAndStop("first-thread");
        NewThreadExtendAndStop d2 = new NewThreadExtendAndStop("second-thread");

        d1.start();
        d2.start();

        d1.stop(); //已过期(    @Deprecated).
    }

}

1.2 实现Runnable接口

/**
 * 作为线程任务存在
 */
public class NewThreadRunnable implements Runnable {

	@Override
	public void run() {
		while(true) {
			System.out.println("thread running ...");
		}
	}

	public static void main(String[] args) {
		Thread thread = new Thread(new NewThreadRunnable());
		thread.start();
	}

}

以上两种都需要覆盖run()方法,但是继承只能继承一个父类,使用接口的方式解耦,更灵活.
Java中Runnable和Thread的区别

1.3 匿名内部类的方式

只运行一次的时候更简单实用

public class NewThreadAnonymousInner {

    /**
     anonymous1 extend thread start ..
     anonymous2 Runnable thread start ..
     anonymous1and2 extend thread start ..
     */
    public static void main(String[] args) {
        anonymous1();
        anonymous2();
        anonymous1and2();
    }

    //继承Thread覆盖方式
    public static void anonymous1() {
        //(子类继承覆盖方法,{}即可)
        new Thread(){
            @Override
            public void run() {
                super.run();
            }
        }.start();
    }

    //实现Runnable覆盖方式
    public static void anonymous2() {
        //传参的位置new对象是 implement的简写方式
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("anonymous2 Runnable thread start ..");
            }
        }).start();
    }

    //继承Thread(子)覆盖并且实现Runnable覆盖方式(父的target),子覆盖父的
    //runnable的run也是传给thread,最后被thread的run覆盖
    public static void anonymous1and2() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("anonymous1and2 Runnable thread start ..");
            }
        }) {
            public void run() {
                System.out.println("anonymous1and2 extend thread start .. ");
            };
        }.start();
    }
}


//运行结果
anonymous1 extend thread start ..
anonymous2 Runnable thread start ..
anonymous1and2 extend thread start ..

1.4 带返回值的线程Callable


import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

//传入的是Integer类型,返回的就是Integer类型
public class NewThreadCallable implements Callable<Integer> {

	//类似run方法
	@Override
	public Integer call() throws Exception {
		System.out.println("正在进行紧张的计算....");
		Thread.sleep(3000);
		return 1;
	}

	public static void main(String[] args) throws Exception {
	
		NewThreadCallable threadCallable = new NewThreadCallable();
		FutureTask<Integer> task = new FutureTask<>(threadCallable);
		Thread t = new Thread(task);
		t.start();//启动的时候则call()开始执行了

		System.out.println("我先干点别的。。。");

		Integer result = task.get();
		System.out.println("线程执行的结果为:" + result);
	}
}

1.5 定时器

import java.util.Timer;
import java.util.TimerTask;

public class ThreadTimerTask {

	public static void main(String[] args) {

		Timer timer = new Timer();
		//源码:public abstract class TimerTask implements Runnable
		timer.schedule(new TimerTask() {
			@Override
			public void run() {
				// 实现定时任务
				System.out.println("timertask is run");
			}
		}, 0, 1000);
	}
}

另有quartz可用.

1.6 线程池的实现


public class NewThreadPool {
    public static void main(String[] args) {
        //固定线程池
        ExecutorService threadPool1 = Executors.newFixedThreadPool(10);
        //长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
        ExecutorService threadPool2 = Executors.newCachedThreadPool();

        for (int i = 0; i < 1000; i++) {
            threadPool1.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("threadPool1->"+Thread.currentThread().getName());
                }
            });
        }
        threadPool1.shutdown();

        for(int i=0;i<1000;i++){
            threadPool2.execute(
                    new Runnable() {
                        @Override
                        public void run() {
                            System.out.println("threadPool2->"+Thread.currentThread().getName());
                        }
                    }
            );

        }
    }
}

Java 四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor

1.7 Spring实现多线程

  • @Async
@Async
method注解

Spring Boot系列二 Spring @Async异步线程池用法总结

3.8 Lambda表达式实现

代码简洁,实现更方便,并发支持好,代码性能高,

public class LambdaThread {
    public static void main(String[] args) {
        List<Integer> values = Arrays.asList(10, 20, 30, 40);
        int res = new LambdaThread().add(values);
        System.out.println("计算的结果为:" + res);
    }


    public int add(List<Integer> values) {
        //values.parallelStream().mapToInt(i->i).sum();//集合数据相加
        //values.stream().forEach(System.out :: println);//会发现打印结果为按照顺序的,且每次一样
		values.parallelStream().forEach(System.out :: println);//会发现打印结果为无序的,且每次不一样.说明并行
        //values.parallelStream().forEachOrdered(System.out :: println);//如果要并行计算有序
        //根据value的类型,自动猜测类型为Integer
        return values.parallelStream().mapToInt(i -> i * 2).sum();
    }
}

1.8 Thread和Runnable的区别

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
总结:
实现Runnable接口比继承Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类

1.9 注意

  • main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。
  • 在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM就是在操作系统中启动了一个进程。

2.多线程风险

2.1 活跃性问题

2.1.1 死锁

哲学家就餐,资源相互需要,互占用,互不释放.
jconsole选中进程,然后进入线程,有按钮操作"检测死锁"来查看死锁情况.

cmd->jsoncole->选择进程-->线程-->检测死锁

2.1.2 饥饿

餐厅排队吃饭,一窗口,新来就插队,还不走,有一个一直排不上,就饥饿,优先级造成的.

产生原因:
高优先级吞噬所有低优先级的CPU时间片
线程被永久堵塞在一个等待进入同步块的状态.(某方法加了锁,2个线程等待进入,其中1个进入之后事情处理不完出不来了,另1个线程就一直再等待,这也是饥饿问题)
等待的线程永远不被唤醒

如何尽量避免饥饿问题:
设置合理的优先级 ---- setPriority(),尽量解决
使用锁来代替 ---- synchronized

2.1.3 活锁

两个线程相互不停的谦让,然后又相互占用了.(2个人独木桥,相互让路例子)

2.2 性能问题

上下文切换(单cpu),时间片

2.3 线程安全性问题

    • 多线程环境下
    • 多个线程共享一个资源
    • 对资源进行非原子性操作

满足以上三点就会有线程安全性问题

–整理于网络

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值