线程(一)

简介: 线程(一)

一、线程和进程的对比:

1、不管线程也好,还是进程也好,他都是cpu的一个时间片,表示的就是一个时间段

2、进程是系统中的最小的执行单元,相当于每开一个记事本,开一个qq等等都是一个进程

3、在下面的图中就是开启了一些进程。

进程就是一个应用程序而这个进程就是系统当中的最小的执行的单元

4、进程是由多个线程组成,而线程是进程中的最小的执行单元,所以说只要有进程,它一定会至少有一个线程。因为进程是无法完成功能的,而进程是由线程去组成。

5、多个线程共享一个进程中所有资源,比如本地栈,栈方法,PC寄存器,上下文

二、种类:

1、单进程和单线程:好比一个人在一张桌子去吃饭,这里一个桌子是一个进程,一个人在上面去吃饭好比是一个线程,而这个线程又是单一的。

2、单进程多线程:好比车道,一个车道上面有多个车辆在跑。也好比多个人在一个桌子上面吃饭,所有的线程共享了进程中的资源。

3、多进程多线程 :每一个人在各自的桌子上吃饭。

而我们主要学的就是多线程。jvm上只能进程线程的创建和运行,所有的调度是由系统来调度的,不是jvm来调度的。

三、线程的运行方式:

1、只要运行一个程序就会有一个线程,比如在运行程序的时候会有一个main方法,这个main方法就是一个主线程,而所有的程序在主线程中都是由上到下来执行的。

加一个子线程的时候就会不按照这样的顺序来执行的了,这时所有的线程可能会同时执行,也有可能不会同时去执行。这就看线程获取时间片的权限。

2、两个子线程在程序中是就绪的状态,等待由系统去调度它。谁先获取到时间片,谁就先执行,否则会继续去等待系统的调度,然后最后到main线程会汇总。所以说这时就不是顺序执行了。

总结:

1、线程不会像普通程序一样顺序执行,而是根据系统分配资源权限来执行。

2、线程由Runnable的就绪状态变为Running的状态的时候必须由系统来调度才可以。

3、系统是通过时间片来控制线程来执行的,时间片是系统调度资源。

4、当我们去实现一个线程的时候,由程序是监控不了的,只有系统来监控让线程去执行的。

创建一个线程------》调用start()变为Runnable----->由系统调度变为Running

四、线程的运行方式:

程序->创建一个线程->start->Runnable

系统调度就绪状态中的线程->Running状态

五、java中怎么创建线程

Thread类:

线程简单的使用:

1、首先创建一个不带线程的例子:这个例子不能会走到run2的方法。因为当调用run1的方法的时候就死循环了。

默认的主线程中的程序执行的顺序是从上到下的。运行的结果如下:

这就是没有用线程是做不到的。

在学习线程的时候不要用Juint,因为juint是有问题的,因为主线程里面才会有子线程

2、使用线程的例子,代码如下:

这样执行的话,也会执行t1,因为启动线程不是调用run方法,而是start方法。

运行的结果如下:

这时t1,t2都执行上面的run方法是由系统来调度器来调度执行的,由runnable变为Running的时候来执行的,而不需要程序去执行它。

总结:谁先竞争到时间片就谁先执行,是两个不同的线程在主线程中执行不同的功能。

三·、创建游戏线程类,语音线程类,如何去中断线程呢:

1、使用暴力的方式去中断线程是不行的:stop方法是不推荐使用的,因为这里会有数据的安全性问题,比如我在写汉字,一下就给停止了,这时可能会出现数据不安全的问题。

  1. public class TestThread {
  2.    public static void main(String[] args) {
  3.        GameThread gameThread=new GameThread();
  4.        VoiceThread voiceThread=new VoiceThread();
  5.        gameThread.start();
  6.        voiceThread.start();
  7.        voiceThread.stop();//@Deprecated用这个注解标注的代表过时的方法
  8.    }
  9.    /**
  10.     * 游戏线程类
  11.     */
  12.    private static class GameThread  extends Thread{
  13.        @Override
  14.        public void run() {
  15.            while(true){
  16.                System.out.println("执行t1");
  17.            }
  18.        }
  19.    }
  20.    /**
  21.     * 语音线程类
  22.     */
  23.    private static class VoiceThread  extends Thread{
  24.        @Override
  25.        public void run() {
  26.            while(true){
  27.                System.out.println("执行t2");
  28.            }
  29.        }
  30.    }
  31. }

运行结果如下:这时就一直执行t1。

2、让线程挂起,也是不推荐使用的,这个方法也被抛弃掉了:

运行的结果如下:这时也都打印t1。

总结:

1、还有一个resume苏醒的方法,也是不推荐使用的。因为这几个方法在线程中使用会导致线程死锁的问题。

2、多个线程会同时去执行,只是相互的去抢占资源。

3、线程和主方法是没有关系的,不会等待所有的线程执行完。

4、把线程挂起,和沉睡,都不会去决定一个线程是否先执行,是由系统的调度器来决定的,只是说让其他的线程去执行的占有率变高了。如果存在对象锁的话,这时挂起和沉睡的线程是不会释放锁的。

死锁:当这个线程被挂起的时候,而又去苏醒它的时候,如果在苏醒之前,这个线程已经被激活了,这时就会出现死活的概念。

四、join方法:只有当子线程执行完,才会去执行主线程,代码如下:

  1. public class TestThread {
  2.    public static void main(String[] args) {
  3.        GameThread gameThread = new GameThread();
  4.        VoiceThread voiceThread = new VoiceThread();
  5.        gameThread.start();
  6.        voiceThread.start();
  7.        //voiceThread.stop();//@Deprecated用这个注解标注的代表过时的方法
  8.        //voiceThread.suspend();
  9.        try {
  10.            gameThread.join();
  11.         //InterruptedException:线程中断异常
  12.        } catch (InterruptedException e) {
  13.            e.printStackTrace();
  14.        }
  15.        System.out.println("main end--------");
  16.    }

  17.    /**
  18.     *
  19.     */
  20.    private static class GameThread extends Thread {
  21.        @Override
  22.        public void run() {
  23.            for (int i = 0; i < 10; i++) {
  24.                System.out.println("执行t1");
  25.            }
  26.        }
  27.    }

  28.    /**
  29.     *
  30.     */
  31.    private static class VoiceThread extends Thread {
  32.        @Override
  33.        public void run() {
  34.            for (int i = 0; i < 10; i++) {
  35.                System.out.println("执行t2");
  36.            }
  37.        }
  38.    }
  39. }

运行的结果如下:

join方法好比讲子线程加入到主线程去执行,join方法针对的是主线程。

举个例子:去下载一个大的文件,这个文件太大了,把它分成四个线程去下载。

每个线程去下载一部分,这四个线程不管谁先下载完都会去等待,然后最终把文件合并在一起,然后真正的下载到本地来。

五、创建线程的另一种方式:实现runnable接口

  1. public class TestTread2 {
  2.    public static void main(String[] args) {
  3.        //第一个参数是目标对象,第二个参数是线程的名字
  4.        Thread t1 = new Thread(new MyRunnable(), "AAAA");
  5.        Thread t2 = new Thread(new MyRunnable(), "BBBB");
  6.        t1.start();
  7.        t2.start();
  8.    }
  9.    private static class MyRunnable implements  Runnable{
  10.        @Override
  11.        public void run() {
  12.             for(int i=0;i<10;i++){
  13.                 System.out.println(Thread.currentThread().getName()+"执行Runnable接口");
  14.             }
  15.        }
  16.    }
  17. }

运行的结果如下:

sleep:阻塞线程,代码如下:

  1. public class TestTread2 {
  2.    public static void main(String[] args) {
  3.        Thread t1 = new Thread(new MyRunnable(), "AAAA");
  4.        Thread t2 = new Thread(new MyRunnable(), "BBBB");
  5.        t1.start();
  6.        t2.start();
  7.    }
  8.    private static class MyRunnable implements  Runnable{
  9.        @Override
  10.        public void run() {
  11.            for(int i=0;i<10;i++){
  12.                System.out.println(Thread.currentThread().getName()+"执行Runnable接口");
  13.                System.out.println("-------------------------");
  14.                try {
  15.                    Thread.sleep(2000);
  16.                } catch (InterruptedException e) {
  17.                    e.printStackTrace();
  18.                }
  19.                System.out.println("*****************************");
  20.                System.out.println(Thread.currentThread().getName()+"执行Runnable接口");
  21.            }
  22.        }
  23.    }
  24. }

运行的结果如下:

这时就会有线程的安全的问题。

  1. public class TestTread2 {
  2.    public static void main(String[] args) {
  3.        Thread t1 = new Thread(new MyRunnable(), "AAAA");
  4.        Thread t2 = new Thread(new MyRunnable(), "BBBB");
  5.        t1.start();
  6.        t2.start();
  7.    }
  8.    private static class MyRunnable implements  Runnable{
  9.        @Override
  10.        public void run() {
  11.            for(int i=0;i<10;i++){
  12.                System.out.println(Thread.currentThread().getName()+"执行Runnable接口");
  13.                try {
  14.                    Thread.sleep(2000);
  15.                } catch (InterruptedException e) {
  16.                    e.printStackTrace();
  17.                }
  18.                System.out.println(Thread.currentThread().getName()+"执行Runnable接口");
  19.            }
  20.        }
  21.    }
  22. }

这样的执行效果如下:睡眠2秒钟,等待其他线程来进入,然后醒来时继续执行,这就是好几个线程在同时执行。

六、线程的生命周期:

①、比如在上面的图中:在程序中有进入区,临界区,退出区,当一个线程进入到进入区的时候,获取到了时间片,然后进入到临界区执行一些操作。

②、在上面的图中,当两个线程,其中一个线程进来把张三改为李四,然后另一个线程进来的时候,拿到的值就不是张三了,所以说在临界区容易出现数据错误的问题。

③、退出区:就是死亡区,线程执行完。

如果有对象锁的话:

就是一个线程进来的时候获取到锁的,去进入到临界区,去执行相应的操作,执行完,再释放锁,然后其他线程去获取锁。

最基本的线程状态:

上面的图就是正常的执行过程。

下面的图是特殊的执行过程:


如果当调用wait方法的时候,会把线程放入到阻塞的队列中去。而上面的几个方法不会把线程放入到阻塞队列里面去,而wait之后会把锁给放开,让其他的线程去执行,wait方法是Object对象里面的方法,只要是对象的话,都有wait方法,wait方法是与synchnorized连用的,使用synchnorized关键字同步块的就会有放入到锁池中,当调用其他线程对象的notify和notiyall的时候会把阻塞队列中线程对象唤醒,notify:是唤醒阻塞队列中的一个线程对象,而notifyall是唤阻塞队列中的所有的线程对象。只有拿到锁的线程才会达到就绪的状态。

synchnorized:保证一个线程在一个时间点上在同一个时间片上去执行。

代码演示下线程的状态,代码如下:

  1. public class ThreadDemo {
  2.    public static void main(String[] args) {
  3.        StateThread  st=new StateThread();
  4.        st.start();
  5.    }

  6.    private static class StateThread extends Thread{
  7.        @Override
  8.        public void run() {
  9.            for(int i=0;i<1000000;i++){
  10.                System.out.println("执行线程内容");
  11.            }
  12.        }
  13.    }
  14. }

然后可以看到AAAA线程首先是runnable的状态,可执行的状态

当把线程阻塞的时候:这时就会有waiting的状态,阻塞的状态

总结:

1、我们看不到running状态,因为running状态是由系统来调度的,通过java虚拟机是观察不了的。我们能看到的是阻塞状态和就绪的状态。

2、死亡的状态也看不了,因为当调用join方法也好或者wait方法也好,必须人为的去唤醒它才可以。

3、不是每一个线程会无限制的去执行下去,只是会在有限的时间片去执行它,如果没有执行完仍旧会处于就绪的状态,不会让继续去执行。

相关文章
|
4月前
|
Java Linux API
线程的认识
线程的认识
|
10小时前
|
安全 Java
线程(二)
线程(二)
|
Java Linux 调度
03.关于线程你必须知道的8个问题(中)
大家好,我是王有志,欢迎来到《Java面试都问啥?》。我们书接上回,继续聊Java面试中关于线程的问题。
65 1
03.关于线程你必须知道的8个问题(中)
|
Java
线程理解
个人学习理解
72 0
|
传感器 存储 自动驾驶
(6)线程
(6)线程
92 0
|
Java 编译器 Linux
初识 线程
初识 线程
97 0
初识 线程
|
C++
C++ | C++线程
c++创建线程的方式不止一种。
113 0
|
Java 调度
线程小记
线程小记
|
Java
什么是线程
什么是线程
129 0