探讨一下!Java并发编程基础篇一

在这里插入图片描述
Java并发编程想必大家都不陌生,它是实现高并发/高流量的基础,今天我们就来一起学习这方面的内容。

什么是线程?什么是进程?他们之间有什么联系?

简单来说,进程就是程序的一次执行过程,它是系统进行资源分配和调度的基本单位。线程与进程类似,但是线程是更小的执行单位。一个进程在执行过程中可产生多个线程,正因为如此,线程也被称为轻量级的进程 。线程和进程在于进程大多是独立工作的,而各线程则不一定,同一进程下的线程极有可能相互干扰。学习了以上内容,回答第一个问题想必就不在话下了。

Talk is cheap,shou me the code.接下来就让我们通过创建线程,来开启我们的并发之旅吧。

线程的创建

创建线程通常有三种方式,分别为继承 Thread 类(重写run方法),实现Runable接口以及使用FutureTask方式。

继承Thread类

public class MyThread extends Thread {
 @Override
 public void run() {
 System.out.println(Thread.currentThread()+"I am a new thread ");
 }
 
 public static void main(String[] args) {
 MyThread thread1=new MyThread();
 MyThread thread2=new MyThread();
 thread1.start();
 thread2.start();
 }
}

如上述的代码所示,我们继承Thread并且重写了run方法,并开启了两个线程。

该代码执行结果如下:

Thread[Thread-0,5,main]I am a new thread Thread[Thread-1,5,main]I am a new thread

值得一提的是,创建完Thread对象后线程并没有启动,直到调用了start方法才真正启动。

那么问题就来了, 我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?

我们new一个Thread,线程就进入了新建状态,而调用了start方法之后它才会进入就绪状态,等待CPU时间片的分配。就绪状态+时间片=线程真正运行,它会自动执行run方法。如果直接调用run方法,main线程会把它当做一个普通的方法去执行,这不是多线程工作(仍然在主线程执行)。

实现Runnable接口

   public class MyThread implements Runnable {
     @Override
     public void run() {
     System.out.println(Thread.currentThread() + "I am a new thread ");
     }
    }
     
     public static void main(String[] args) {
     MyThread thread1 = new MyThread();
     MyThread thread2 = new MyThread();
     new Thread(thread1).start();
     new Thread(thread2).start();
     }

此代码执行结果和代码一相似,由于Java只支持单继承,如果使用继承Thread的方式就不能再继承其他的类,而Runnable没有这个限制,但是他们有一个共同的缺点,任务没有返回值,我们接着来看最后一种方式。

使用FutureTask

public class CallerTask implements Callable<String> {
 @Override
 public String call() throws Exception {
 return Thread.currentThread() + " hello world";
 }
}
 public static void main(String[] args) {
 FutureTask<String> futureTask1 = new FutureTask<>(new CallerTask());
 FutureTask<String> futureTask2 = new FutureTask<>(new CallerTask());
 new Thread(futureTask1).start();
 new Thread(futureTask2).start();
 try {
 String result1 = futureTask1.get();
 String result2 = futureTask2.get();
 System.out.println(result1);
 System.out.println(result2);
 } catch (InterruptedException e) {
 e.printStackTrace();
 } catch (ExecutionException e) {
 e.printStackTrace();
 }
 }

该代码执行结果如下:

Thread[Thread-0,5,main] hello world Thread[Thread-1,5,main] hello world

如上述代码所示,我们实现了Callable接口的call()方法,在main函数中创建了两个FurtureTask对象,并且将其启动。通过futureTask.get()方法拿到了我们的返回值。

并发程序的常用方法

wait() 方法

当一个线程调用一个共享变量的wait方法的时候,该线程会被阻塞挂起。如果wait() 方法事先没有获取锁,那么它就会抛出异常。 如果发生了以下几种情况,wait状态将会被打破。

其他线程调用了该共享对象的notify() 或notifyAll()方法
其他线程调用了该线程的interrupt() 方法
当前线程调用共享对象的wait() 方法的时候,当前线程只会释放当前共享对象的锁,当前线程持有的其他共享对象的锁不会被释放。

notify() 方法

一个线程调用共享对象的notify() 方法之后,会唤醒一个处于等待状态的线程,它并不会确定的唤醒某个线程,而是由JVM决定。被唤醒的线程并不能继续执行,因为它的锁状态已经发生了改变,必须去竞争获取锁,才能进入就绪状态。

notifyAll() 方法

notifyAll()方法会唤醒该共享变量上所有处于等待状态的线程,但是值得注意的是,它只会唤醒调用这个方法之前被阻塞的线程。之后阻塞的它就无能为力了。

sleep方法

调用该方法会暂时让出指定时间的执行权,也就是在此期间不参与CPU的调度,但是该线程所拥有的监视器资源,毕如锁还是持有不让出的。到了指定时间之后,他就会处于就绪状态,等待获取CPU,重新进入运行状态。

接下类我们谈谈sleep()和wait()的联系和区别

最重要的一点就是sleep没有释放锁,而wait释放了锁
两者都可以暂停线程的执行
wait()常常用于线程之间的交互/通信
wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify() 或者 notifyAll() 方法。sleep() 方法执行完成后,线程会自动苏醒。
本篇文章主要介绍了线程的创建以及并发过程的常用方法,希望认真读完的你有所收获。
第一:看完点赞,感谢您的认可;

第二:随手转发,分享知识,让更多人学习到;

第三:记得点关注,每天更新的!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值