黑马程序员-【java基础】-多线程

时间:2022-12-03 10:59:43

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一.多线程概述

   java语言的优势之一就是线程处理较为简单。一般操作系统都支持同时运行多个任务,一个任务通常就是一个程序,每个运行中的程序被称为一个进程。当一个程序运行时,内部可能包含多个顺序执行流,每个顺序执行流就是一个线程。

   进程:同一个操作系统中执行的一个子程序,包含了三部分虚拟CPU,代码,数据。

   多进程:同一个操作系统中执行的多个并行的子程序,可以提高CPU的使用率。

   线程:同一个进程当中并发执行的子程序流。

   多线程:同一个进程中并发执行的多个子程序流。

   并发:进程时并发运行的,OS将时间划分为很多时间片段,尽可能均匀分配给正在运行的程序,微观上进程走走停停,宏观上都在运行,这种运行的现象称为并发。

二.线程编程的两种方法

   1)Thread类

     线程类(Thread)包含了一个可以运行的过程(方法):run()方法。

   2)创建一个具体线程的步骤如下:

              第一,继承Thread类。

第二,覆盖run方法(就是更新运行过程),实现用户自己的过程。

第三,创建线程实例(就是创建一个线程)

第四,使用线程实例的start()方法启动线程,启动后线程会尽快的去并发执行run()

        方法一:继承Thread类

        实现步骤:

1.继承Thread类,覆盖run()方法,提供并发运行的过程。

2.创建这个类的实例

3.使用start()方法开启线程。

  

class MyThread extends Thread
{
public void run()
{
System.out.println("覆盖run()方法");
}
}
public class test {

public static void main(String[] args) {
// TODO Auto-generated method stub
Thread t1=new MyThread();
//调用start()来启动线程,线程启动方法
t1.start();

}

}
           注:

1.main()方法也是一个线程

2.单核,双核,多核处理器的输出结果会不一样

方法二:实现Runnable接口

实现步骤:

1.实现Runnable接口,实现run()方法,提供并发执行的过程

2.创建这个类的实例,用这个实例作为Thread构造器参数,创建Thread类

3.使用start()方法启动线程

class MyThread2 implements Runnable
{

public void run() {
System.out.println("覆盖run()方法");

}
}
public class TestThread2{
public static void main(String[] args) {
Runnable myThread=new MyThread2();
Thread t1=new Thread(myThread);
t1.start();
}
}

三.线程的5种状态

1) New新建状态

当程序使用new关键字创建了一个线程后,该线程就处于新建状态,此时线程还未启动,当线程对象调用start()方法时,线程启动,进入Runnable状态。

2)Runnable可运行状态

      当线程处于Runnable状态时,表示线程准备就绪,等待获取CPU

3)Running运行状态

假如线程获取了CPU,则进入Running状态,开始执行线程体,开始执行run方法。

 4)Block阻塞状态

当如下情况下,线程会进入阻塞状态:

1.线程调用了sleep()方法主动放弃所占CPU资源。

2.线程调用了一个阻塞式IO方法,在该方法返回前,该线程被阻塞。

            当正在执行的线程被阻塞时,其他线程就获得执行权限,

5)Dead死亡状态

1.当线程的run方法执行结束,线程进入Dead状态

2.需要注意的是,不要试图对一个已经死亡的线程进行start()方法,线程死亡后将不能再次作为线程执行,系统会抛出IllegalThreadStateException异常


黑马程序员-【java基础】-多线程

四.线程的同步

    多线程并发访问同一个对象,如果不对线程进行同步控制,破坏了原子操作,则会造成临界资源的数据不一致。

每一个对象都有一个互斥的锁标记和一个锁池,当线程拥有这个对象的锁标记时才能访问这个资源,没有锁标记便进入锁池,保证在同步代码块中只有一个线程,解决了多线程的同步控制问题。

synchronized修饰代码块:对括号内的对象object加锁,只有拿到对象锁标记的线程才能进入该代码块。

class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag=flag;
}
public void run()
{
while(true)
{
if(flag==true)
{
synchronized(myLock.locka)
{
System.out.println("if locka");
synchronized(myLock.lockb)
{
System.out.println("if lockb");
}
}
}

else
{
synchronized(myLock.lockb)
{
System.out.println("else locka");
synchronized(myLock.locka)
{
System.out.println("else lockb");
}
}
}
}
}
}
class myLock
{
static Object locka=new Object();
static Object lockb=new Object();
}
class DeadLockTest
{
public static void main(String[] args)
{
Thread d1=new Thread(new Test(true));
Thread d2=new Thread(new Test(false));
d1.start();
d2.start();

}
}


注意:

1.静态方法可以是同步方法,但是他所锁的并不是当前对象,是类对象

2.抽象方法不能是synchronized同步的方法

3.构造方法不能是synchronized同步的方法

1.等待唤醒机制:

Wait, notify()  notifyAll() 都使用在同步中,因为要对持有监视器(锁)的线程操作。所以要使用在同步中,因为只有同步才具有锁。

 

为什么这些操作线程的方法要定义Object类中呢?

因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。

不可以对不同锁中的线程进行唤醒。

即等待和唤醒必须是同一个锁。

2.如何停止线程?

   开启多线程运行,运行代码通常是循环结构的,只要控制住循环,就可以让run方法结束,从而线程结束。

3.join:当A线程执行到了B线程的.join方法时,A就会等待,等B线程都执行完了,A才执行。Join可以用来临时加入线程执行。

3.并行和并发

并行:是在逻辑上同时发生,指在某一个时间内同时运行多个程序。

并发:物理上同时发生,指在某一个时间点上同时运行多个程序。

4.JVM虚拟机的启动是单线程还是多线程?

多线程,因为垃圾回收也要先启动,否则很容易出现内存溢出。

最简单的也是垃圾回收线程和主线程。