java多线程与并发编程

87 阅读6分钟

什么是线程?

进程想要执行任务就需要依赖线程。换句话说,就是进程中的最小执行单位就是线程,并且一个进程中至少有一个线程。

什么是多线程?

指在同一个进程中同时运行多个线程,如你开启QQ聊天,可以开启多个窗口。

提到多线程这里要说两个概念,就是串行和并行。 **串行:**其实是相对于单条线程来执行多个任务来说的,当我们下载多个文件时,在串行中它是按照一定的顺序去进行下载的,串行在时间上是不可能发生重叠的。 **并行:**下载多个文件,开启多条线程,多个文件同时进行下载,这里是严格意义上的,在同一时刻发生的,并行在时间上是重叠的。

多线程有三大特性:

**原子性:**一个操作或者多个操作,要么全部执行成功,要么全都不执行。

**可见性:**当多个线程访问同一个变量时,如果一个线程修改了这个变量的值,其他线程能够立即看得到修改后的值。

有序性:程序执行的顺序按照代码的先后顺序执行。

什么是并发编程?

所谓并发编程是指在一台处理器上“同时”处理多个任务。并发是在同一实体上的多个事件。多个事件在同一时间间隔发生。

并发编程的目的:

  • 让程序充分利用非计算机资源·
  • 加快程序响应速度(耗时任务、web服务器)
  • 简化异步时间的处理。

实现多线程的方式:

1) 继承 Thread

Thread类本质上是实现了Runnable接口的一个实例,代 表一个线程的实例。启动线程的唯一方法就是通过Thread 类的start()实例方法。

public class MyThread extends Thread {
public void run() {
   System.out.println("MyThread.run()");
   }
}  
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread1.start();
myThread2.start();  

2) 实现 Runnable 接口

如果自己的类已经extends另一个类,就无法直接extends Thread,此时,可以实现一个Runnable接口

public class MyThread extends OtherClass implements Runnable {      public void run() {
System.out.println("MyThread.run()");
     }  
}

3)实现Callable接口通过FutureTask 包装器来创建 Thread 线程

public class CallableDemo implements Callable<String> {
   public static void main(String[] args) throws ExecutionException, InterruptedException {
      ExecutorService executorService = Executors.newFixedThreadPool(1);
      CallableDemo callableDemo = new CallableDemo();
      Future<String> future = executorService.submit(callableDemo);
      System.out.println(future.get());
      executorService.shutdown();    
}    
@Override    
public String call() throws Exception {
    int a = 1;        
    int b = 2;        
    System.out.println(a + b);
    return "执行结果:" + (a + b);    
    } 
} 

线程的状态:

创建完的线程具有生命周期,从创建到死亡,中间还有一些其他状态的变化。

1)新建状态(New) : 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。

2)就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。

3)运行状态(Running) : 线程获取CPU资源后进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。

4)阻塞状态(Blocked) : 阻塞状态是线程因为某种原因让出CPU资源,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种: 等待阻塞 – 通过调用线程的wait()方法,让线程等待某工作的完成。 同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。 其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

5)死亡状态(Dead) : 线程顺利执行完了或者因异常退出了run()方法,该线程结束生命周期。

线程的常用方法:

编号方法说明
1public void start()使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
2public void run()如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
3public final void setName(String name)改变线程名称,使之与参数 name 相同。
4public final void setPriority(int priority)更改线程的优先级。
5public final void setDaemon(boolean on)将该线程标记为守护线程或用户线程。
6public final void join(long millisec)等待该线程终止的时间最长为 millis 毫秒。
7public void interrupt()中断线程。
8public final boolean isAlive()测试线程是否处于活动状态。
9public static void yield()暂停当前正在执行的线程对象,并执行其他线程。
10public static void sleep(long millisec)在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
11public static Thread currentThread()返回对当前正在执行的线程对象的引用。

停止线程:

停止线程是在多线程开发时很重要的技术点,掌握此技术可以对线程的停止进行有效的处理。

在Java中有以下3种方法可以终止正在运行的线程:

  • 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止
  • 使用stop方法强行终止线程,但是不推荐使用这个方法,因为stop和suspend及resume一样,都是作废过期的方法,使用他们可能产生不可预料的结果。
  • 使用interrupt方法中断线程,但这个不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止。

多线程注意点:

多线程编程给我们带来了很多的优势,如充分利用处理器资源,并行做多件事情,但是在开发多线程应用时,我们需要注意的问题有很多,比如线程同步、线程死锁等等。

  • 线程安全 在单线程环境下,不加锁也没有线程安全和不安全问题,但多线程访问时,需要线程同步访问来保证线程安全,如同时有两个线程,线程A和线程B,对同一个账号金额进行修改操作,当采用了加锁机制时,A线程在修改账号金额,B线程不能进行访问,直到A线程读取完,B线程才可使用,这样就不会出现数据不一致或者数据污染。

  • 线程死锁 线程死锁是指多个线程竞争同一个资源,各自阻塞等待其他线程持有的锁,例如: 1.线程1锁住了A资源,然后尝试去对B资源加锁,同时线程2已经锁住了B资源,再尝试对A加锁,为了彼此都能得到所需的资源而相互阻塞等待,从而产生了死锁。

    更多java基础资料等你来拿!!!