多线程(四)并发与并行,守护线程,创建线程方式,Runnable与Callable,notify与notifyAll,sleep与wait,start与run

1、并发与并行的区别?

并发是在同一个CPU上,在若干道程序中进行多路复用,通过分配时间片的方式。
并行是在多个CPU上,每个CPU运行一个程序。

2、守护线程是什么?

守护线程在线程中拥有最低的优先级,用于为系统中的其他对象和线程提供服务。将一个线程设置为守护线程是在线程创建之前调用线程对象的setDaemon方法。典型的守护线程是JVM的垃圾回收线程。当所有的非守护线程都结束的时候,程序也就终止了,虚拟机就退出了。
需要注意的点:
(1)thread.setDaemon(true)必须在thread.start()之前设置,否则会报IlegalThreadStateException异常
(2)在Daemon中产生的新线程也是Daemon的
(3)守护线程永远不去访问固有资源,如文件、数据库等,因为它们会在任何时候甚至一个操作的中间发生中断。

3、创建线程的方式

(1)继承Thread类。
重写run方法,创建Thread类子类的实例,直接start()即可。

(2)实现Runnable接口
实现Runnable接口,并重写run方法。创建Runnable是实现类的实例,并把这个实例作为参数创建Thread对象。然后调用start()方法。

(3)使用Callable和Future创建线程

  • 1、创建Callable接口的实现类,并实现call()方法。然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。
  • 2、使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值。
  • 3、使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
  • 4、调用FutureTask对象的get()方法获得子线程执行结束后的返回值。
public class TestCallable{
	public static void main(String[] args) {
		ThreadSub sub = new ThreadSub();
		
		// 用FutureTask接收返回参数
		FutureTask<String> result = new FutureTask<>(sub);
		
		// FutureTask也实现了Runnable接口
		new Thread(result).start();
		
		try {
			// 等线程执行完之后,获取返回值
			String str = result.get();
			
			System.out.println(str);
		} catch (InterruptedException e) {
			
			e.printStackTrace();
		} catch (ExecutionException e) {
			
			e.printStackTrace();
		}
		
	}
}

class ThreadSub implements Callable<String>{

	@Override
	public String call() throws Exception {
		// 可以写逻辑
		String string = "abc";
		// 返回值
		return string ;
	}
}

(4)通过线程池创建线程

4、Runnable和Callable区别?

(1)实现Callable接口的任务线程能返回执行结果,而实现Runnable的不能。
(2)Callable接口的call()方法允许抛出异常,而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛。

5、notify和notifyAll的区别?

     如果某些线程在等待某些条件触发,那么当这些条件为真时,可以使用notify和notifyAll来通知那些等待中的线程重新运行。不同的是,notify只通知一个线程,其他线程不会收到通知;notifyAll是通知所有线程
    比如:如果你的程序中有两个线程,即生产者和消费者。那么当生产者生产消息后,可以通知消费者,让消费者开始消费数据,因为缓存区不为空(有消息待消费)。
如果消费者消费数据了,就会通知生产者,因为缓存区不再为满。

6、sleep()和wait()的区别

(1)sleep是Thread线程类的方法,wait是Object顶级类的方法。
(2)sleep可以在任何地方使用,而wait只能在同步块或者同步方法中使用。
(3)sleep和wait调用后,都会暂停当前线程并让出CPU的执行时间。但不同的是sleep不会释放当前持有的对象的锁资源,到时间会继续执行。而wait会放弃所有锁并需要notify或者notifyAll后重新获取到对象资源后才能继续执行。
(4)sleep必须捕获异常,而wait/notify/notifyAll不需要

7、start()和run()方法的区别?

start()方法:可以启用线程,使线程处于就绪状态,并没有运行,一旦得到时间片,就开始执行run方法,run方法运行结束,线程就此终止。
run方法:只是thread的一个普通方法调用。

public static void main(String[] args) {
	
	ThreadTwo threadTwo = new ThreadTwo();
	threadTwo.start();
	System.out.println("pong");
}

class ThreadTwo extends Thread{
	@Override
	public void run(){
		System.out.println("ping");
	}
}

输出:pongping


public static void main(String[] args) {
	
	ThreadTwo threadTwo = new ThreadTwo();
	threadTwo.run();
	System.out.println("pong");
}

class ThreadTwo extends Thread{
	@Override
	public void run(){
		System.out.println("ping");
	}
}

输出:pingpong
由此可见,start方法只是启动线程,run方法只是一个普通方法调用。

8、一个线程连续调用两次start()会出现什么?
// start 方法源码
 public synchronized void start() {
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
            
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
	private native void start0();

会抛出IllegalThreadStateException异常,由于start0()方法的实现已经封装在dll中了,但是肯定是在start0方法中修改了线程状态值threadStatus,如果再次进入,就会抛异常。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值