线程


1.程序 、进程和线程
程序: 静态代码,为了完成某种功能,使用某种计算机语言编写的指令集合;
进程:运行中的程序,被加载到内存中,分配资源
线程:进程可以进一步被细化为线程,就是一个具体的执行任务,是操作系统调度中最小的单位

线程和进程的关系:
每一个进程都有相应的线程;在执行程序时,实际上是执行相应的一系列线程,进程是资源分配的最小单位,线程是程序执行的最小单位;[一个进程可以包含多个线程,一个线程只能属于一个进程,线程不能脱离进程而独立,也就是说当进程被关闭时,线程也会被自动关闭,每一个进程中至少包含一个线程,也就是主线程,在主线程中开始执行程序,java的程序的入口main()方法就是在主线程中被执行的,在主线程中可以创建并启动其他线程, 一个进程内的所有线程共享该进程的内存资源。]

创建一个线程:
方式一:继承Thread类;
方式二:继承Runnable接口;
**继承方式和实现方式的区别与联系
区别: 继承Thread:线程代码存放在Thread子类run方法中。
实现Runnable:线程代码在接口的子类run方法中;
**
实现Runnable的好处
1.避免了单继承的局限性,提高了扩展性,
2.多个线程可以共享一个接口实现类的对象,非常适合多个相同的线程来处理同一份资源;

public class MyThread implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+i);}}
}
public class Test{public static void main(String[] args){MyThread myThread =new MyThread();//新建一个执行任务
myThread.start()//创建一个线程,并为线程指定执行任务
for(i=0;i<100;i++){System.out.println(i);}}}
package day17.demo3;
public class Test{//线程要执行的任务public static void main(String[] args) {MyThread myThread = new MyThread();myThread.start();//创建线程,并为线程指定执行任务for (int i = 0; i < 1000; i++) {System.out.println(i);}}
}
package day17.demo3;
public class MyThread extends Thread {public void run() {for (int i = 0; i < 1000; i++) {System.out.println("main" + i);}}}

Thread中的方法:

         Thread()                 创建一个新的线程Thread(String name)创建一个指定名字的线程

Thread类中的一些方法

     **void Start()                                             启动线程****final void setName(String name)            设置线程名称****final Striing getName()                            获得线程名称****final void setPriority(int newpriority)         设置线程优先级**线程优先级大的执行的优先权高,(并不是一直执行到完)**final int getPriority()                                 获得线程优先级**final void jion()                                               等待程序终止**也就是说在使用这个方法之后的程序需要等待这个进程完成(死亡)后才能执行**static Thread currentThtead()                     返回对当前执行对象的引用**Thread currentThread().getName()    可以获得当前正在执行的这个程序的名字;**static void sleep(long millis)              让线程休眠指定时间****Thread yield()                                线程让步(线程从运行态直接转为就绪态)**stop()                                                             停止程序**本质上不安全,已弃用
package day17.demo4;
public class MyThread  implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+i);}}
}
package day17.demo4;
public class Test1 {public static void main(String[] args) {MyThread myThread=new MyThread();//新建一个执行任务Thread thread1=new Thread(myThread,"thread1      ");//后面的参数为线程名称Thread thread2=new Thread(myThread,"thread2      ");//获得当前线程的线程名称;currentThread():获得当前线程;getName():获取线程名称;thread1.start();thread2.start();/** 时间片:给线程分配时间单位去执行;* 抢占式:高优先级的线程有更多的执行权,但也不是说低优先级的就没有机会;具体得看cup对进程的调度;* *///thread1.getPriority()获取thread1的线程优先级,一般为1-10;默认优先级为5thread1.setPriority(10);//设置线程优先级(高优先级的不一定先制性其优先权高,但不是一直执行完)thread2.setPriority(1);System.out.println("thread1的线程优先级为:"+thread1.getPriority());System.out.println("thread2的线程优先级为:"+thread2.getPriority());}
}

线程的优先级:

                 事实上,计算机只有一个cpu,各个线程轮流获得cpu的使用权,执行任务;优先级高的线程有更多的被cpu执行的机会;反之则亦然;优先级是用整数表示的,取值范围为1--10,在通常情况下,优先级默认为5;但可以通过final void setPriority(int newpriority)     final int getPriority()   两个方法来设置以及获得优先级;调度策略:时间片:给线程分配时间去执行;抢占式:高优先级的线程有更高的 执行权,(但不是说低优先级的程序就没有机会)        Thread类有三个静态常量来表示优先级        MAX_PRIORITTY:取值为10,表示最高的优先级;MIN_PRIORITY:取值为1,表示最低的优先级;NORM_PRIORITY;取值为5,表示默认的优先级;

线程的状态

线程在他的生命周期中会处于不同的状态;
线程的生命周期: 从生到死;

线程的各状态概念:
新建:当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
就绪:处于新建状态的线程被statr()后,将进入到线程队列等到cpu的时间片,此时其已经具备了运行的条件,只是没有被分配到cpu资源;
运行:当就绪的线程被调度并且得到了cpu的资源,便进入运行的状态,run()方法定义了线程的操作和功能;
阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出cpu并临时终止自己的执行,进入阻塞状态;
死亡:线程完成了它的全部工作或者被线程提前强制性的终止或出现异常导致结束;

线程的分类

Java中的线程主要分为两类,一类为用户线程,一类为守护线程;
用个比较通俗的比较就是说是守护线程就是用户线程的保姆,一般情况下只有当用户线程全部结束时,时守护线程才能停止;
守护线程的作用:是为其他线程运行提供便利服务,守护线程最典型的应用就是GC(垃圾回收器),它就是一个很称职的守护者;
用户线程与守护者线程两者之间几乎没有区别,唯一不同的之处就是虚拟机的离开,如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了,因为没有了被守护者,守护者线程也就没有工作可以做了,也就没有继续运行程序的必要了,

      注意:设置线程为守护线程必须在启动线程之前,否则可能会抛出一个IllegalThreadStateException异常。

多线程的概念

多线程是指程序中包含多个执行单元,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
何时需要多线程

     1.程序需要同时执行两个或多个任务。2.程序需要实现一些需要等待的任务时,如用户输出、文件读写操作或者网络操作以及搜索等等;3.需要一些后台运行的程序时。

多线程的优点

      提高程序的响应;提高cpu的利用率;改善程序结构,将复杂任务分为多个线程,独立运行;

多线程的缺点

            线程也是程序,所以线程需要占用内存,线程越多占用的内存也越多(升级硬件设备来解决)多线程需要协调和管理,所以需要cpu时刻跟踪线程;线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题;

线程的同步

并发与并行

 并行:多个cpu同时执行多个任务,比如,多个人同时做不同的事;并发:一个cpu(采用时间片)同时执行多个任务,比如:秒杀,抢票,多个人做同一件事;

多线程同步

多个线程同时读写同一份共享资源时,可能会引起冲突,所以引入线程"同步"机制,即各线程间要有先来后到;

同步就是排队加锁:

几个线程之间要排队,一个个对共享资源进行操作,而不是同时进行操作;
为了保证数据在方法中被访问时的正确性,在访问时加入锁机制;

模拟买票

      两个窗口分别售票,票数为十张;分别使用继承Thread类和实现Runnable接口两种方式实现

确保一个时间点只有一个线程访问共享资源,可以给共享资源加一把锁,哪个线程获取了这把锁,才有权力访问该共享资源。

同步监视器

          同步监视器可以是任何对象,必须唯一,保证多个线程获得的是同一个对象(锁)

同步监视器的执行过程

              1.第一个线程访问,锁定同步监视器,执行其中的代码。2.第二个线程访问,发现同步监视器被锁定,无法访问。3.第一个线程访问完毕,解锁同步监视器。4.第二个线程访问,发现同步监视器没有锁,然后锁定并访问。

一个线程持有锁会导致其他所有需要此锁的线程挂起;在多线程竞争下,加锁与释放锁会导致比较多的上下文切换和调度延时,引起性能问题。

synchronized

案例:
模拟两个窗口同时出售100张票
代码如下:(继承Thread类 给代码块加同步锁)

package Day18.demo1;
public class SPThread extends  Thread {static int num = 100;//定义一个票数static   Object obj = new Object();//保证只有一个@Overridepublic void run() {while (true) {//synchronized同步锁就是一个对象,但要求只有一个synchronized (obj) {if (num > 0) {try {Thread.sleep(100);//让线程休眠100ms;} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "出票" + num);num--;} else {break;}}//同步代码块,执行完成后,同步对象(锁)会自动释放}}
}
package Day18.demo1;
public class Test {public static void main(String[] args) throws InterruptedException {SPThread ck1=new SPThread();ck1.setName("窗口一");SPThread ck2=new SPThread();ck2.setName("窗口二");ck1.start();ck2.start();}
}


同样的案例:
模拟两个窗口同时出售100张票
代码如下:(继承Thread类 给方法加同步锁)

package Day18.demo1;
public class SPThread1 extends Thread{/*将方法上同步锁*/
static int num=100;@Overridepublic void run() {while (true){if (num>0){try {print();} catch (InterruptedException e) {e.printStackTrace();}}else {break;}}}public  synchronized static void print() throws InterruptedException {Thread.sleep(500);if (num>0){System.out.println(Thread.currentThread().getName()+num);num--;}}
}
package Day18.demo1;import Day18.demo1.SPThread1;public class Test1 {public static void main(String[] args) {SPThread1 t1=new SPThread1();t1.setName("窗口一");SPThread1 t2=new SPThread1();t2.setName("窗口二");t1.start();t2.start();}
}


同样下面用Runnable接口来实现:
代码如下:(实现Runnable接口, 代码块加同步锁)

package Day18.demo2;
public class SpThread implements Runnable {int num = 100;//定义票数@Overridepublic void run() {while (true) {synchronized (this) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}if (num > 0) {System.out.println(Thread.currentThread().getName() + num);num--;} else {break;}}}}}
package Day18.demo2;
public class Test {public static void main(String[] args) {SpThread spThread=new SpThread();//创建一个任务Thread t1=new Thread(spThread,"窗口一");Thread t2=new Thread(spThread,"窗口二");t1.start();t2.start();}
}


代码如下:(实现Runnable接口, 给方法加同步锁)

package Day18.demo2;public class SpThread1 implements Runnable{int num=100;@Overridepublic void run() {while (true){if (num>0){try {print();} catch (InterruptedException e) {e.printStackTrace();}}else {break;}}}public  synchronized void print() throws InterruptedException {Thread.sleep(200);if (num>0){System.out.println(Thread.currentThread().getName()+num);num--;}}
}
package Day18.demo2;
public class Test1 {public static void main(String[] args) {SpThread1 spThread1=new SpThread1();Thread t1=new Thread(spThread1,"窗口一");Thread t2=new Thread(spThread1,"窗口二");t1.start();t2.start();}
}

Lock(锁)

Lock

       从JDK5.0开始,java提供了更强大的线程同步机制--通过显示定义同步锁对象来实现同步。同步锁使用Lock对象充当。Java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁,释放锁。

同样的案例:模拟两个窗口出票
代码如下:

package Day18.demo3;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockDemo implements Runnable{Lock lock=new ReentrantLock();int num=100;@Overridepublic void run() {while (true){if (num>0){try {print();} catch (InterruptedException e) {e.printStackTrace();}}else {break;}}}public void print() throws InterruptedException {lock.lock();Thread.sleep(100);if (num>0){System.out.println(Thread.currentThread().getName()+num);num--;lock.unlock();}}
}
package Day18.demo3;
public class Test {public static void main(String[] args) {LockDemo lockDemo=new LockDemo();Thread ck1=new Thread(lockDemo,"窗口一");Thread ck2=new Thread(lockDemo,"窗口二");ck1.start();ck2.start();}
}


Lock方法只能给代码块加同步锁,不能给方法加,这是与synchronized的区别之一,还有一个区别就是syschronized是隐式的,Lock是显示的。

线程死锁

死锁:

         不用的线程分别占用对方需要的同步资源不放弃,都在等对方放弃自己需要的同步资源,于是就形成了线程的死锁。出现死锁后,不会出现异常,不会出现提示,所有线程都将处于阻塞状态,无法继续下去。例如;一个M国人和一张中国人一起吃饭正常情况为: M国人一刀一叉               中国人:两根筷子特殊情况为:M国人一刀一根筷子          中国人;一叉一根筷子当遇到特殊情况时,两人都不放弃对方需要的同步资源,就会一直等待下去,也就是线程死锁。**设计程序时要考虑清楚锁的顺序,尽量减少嵌套的加锁交互数量。**

制造一个死锁:
代码如下:

package Day18.demo4;
public class SiSuo extends Thread {static Object objA = new Object();static Object objB = new Object();boolean flag;public SiSuo(boolean flag) {this.flag = flag;}@Overridepublic void run() {if (flag) {synchronized (objA) {System.out.println("ifOBJA");synchronized (objB) {System.out.println("ifobjB");}}} else {synchronized (objB) {System.out.println("elseOBJA");synchronized (objA) {System.out.println("elseobjA");}}}}
}
package Day18.demo4;
public class Test {public static void main(String[] args) throws InterruptedException {SiSuo si=new SiSuo(true);SiSuo s2=new SiSuo(false);si.start();s2.start();}
}

产生死锁一直等待;

案例:两个线程交替打印1-100之间的数字
代码如下:

package Day18.demo5;
public class PrintNumDemo extends Thread {static int num = 0;static Object object = new Object();@Overridepublic void run() {while (true) {synchronized (object) {object.notify();//唤醒等待的进程,由于已经有线程持有锁了,于是不能进入到同步代码块try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}if (num < 100) {num++;System.out.println(Thread.currentThread().getName() + ":" + num);} else {break;}try {object.wait();//线程等待,释放锁} catch (InterruptedException e) {e.printStackTrace();}}}}
}
package Day18.demo5;
public class Test {public static void main(String[] args) {PrintNumDemo t1=new PrintNumDemo();t1.setName("线程1:");PrintNumDemo t2=new PrintNumDemo();t2.setName("线程2:");t1.setPriority(1);//设置优先级t2.setPriority(10);//将线程2的优先级设置更高,让线程2优先执行的概率更大t1.start();t2.start();}
}

线程通信

线程通讯指的是多个线程通过消息传递实现相互牵制,相互调度,即进程间的相互作用。
设计到三个方法:

             .wait()     一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。.notify()   一旦执行此方法,就会唤醒被.wait()阻塞的方法,如果有多个线程被阻塞,就最先唤醒优先级大的哪一个。.notifyAll()  一旦执行此方法,就会唤醒全部被wait()阻塞的进程。## 特别说明:这三个方法必须使用在同步代码块或同步方法中这三个方法定义在java.lang.Object类中

经典案例:生产者消费者问题

                 生产者将产品放在柜台,而消费者从柜台处取走产品,生产者一次只能生产固定的数量,产品(例如一个),这时候柜台中不能再放产品,此时生产者应该停止生产等待消费者拿走产品,此时生产者唤醒消费者来取走产品,消费者拿走产品后,唤醒生产者,消费者开始等待。

代码如下;

package Day18.demo6;
/*柜台,只有一个    */
public class Countdemo {int num=0;//生产//同步对象是this就是同一个Countdemo对象public synchronized void add(){if (num==0){num++;System.out.println("生产者线程生产了一个产品放在了柜台上");this.notify();//此时唤醒消费者}else {try {this.wait();//此时已有产品如果此时生产者线程进来了就wait()生产者线程} catch (InterruptedException e) {e.printStackTrace();}}}//消费public synchronized void sub(){if (num==1){num--;System.out.println("消费者线程从柜台上消费了一个产品");this.notify();//此时唤醒生产者}else {try {this.wait();//此时没有产品,如果此时消费者进来就外套()消费者线程} catch (InterruptedException e) {e.printStackTrace();}}}
}
package Day18.demo6;
//消费者线程
public class XFZdemo extends Thread{Countdemo countdemo;public  XFZdemo(Countdemo countdemo) {this.countdemo=countdemo;}@Overridepublic void run() {while (true){try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}countdemo.sub();}}
}
package Day18.demo6;
//生产者线程
public class SCZdemo extends Thread{Countdemo countdemo;public SCZdemo(Countdemo countdemo) {this.countdemo = countdemo;}@Overridepublic void run() {while (true){try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}countdemo.add();}}
}
package Day18.demo6;
public class Test {public static void main(String[] args) {Countdemo c=new Countdemo();XFZdemo xfz=new XFZdemo(c);SCZdemo scz=new SCZdemo(c);xfz.start();scz.start();}
}

新增创建线程的方式

实现Callable接口与使用Runnable相比,Callable功能更加强大一些

         相比run()方法,可以有返回值。方法可以抛出异常。支持泛型的返回值。据要借助FutureTask类,获取返回结果。

接受任务

         FutureTask<Integer>futureTask=new FutureTask(任务);创建线程Thread t=new Thread(futureTask);t.start();Integer res=futureTask.get()  获得线程call方法的返回值

案例:

package Day18.demo7;
import java.util.concurrent.Callable;
public class CallableDemo  implements Callable <Integer>{@Overridepublic Integer call() throws Exception {int num=0;for (int i = 0; i < 11; i++) {num+=i;}return num;}
}
package Day18.demo7;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test {public static void main(String[] args) throws ExecutionException, InterruptedException {Callable c=new CallableDemo();FutureTask<Integer> futureTask=new FutureTask<>(c);Thread t=new Thread(futureTask);t.start();Integer res=futureTask.get();System.out.println(res);}
}

dya19第十章线程相关推荐

  1. 《操作系统真象还原》第十章 ---- 线程打印尚未成功 仍需此章锁机制完善努力 在前往最终章的路上激流勇进

    文章目录 专栏博客链接 相关查阅博客链接 本书中错误勘误 部分缩写熟知 闲聊时刻 稍加修改的main.c 看锁理论介绍时 说点想聊的 修改增添thread.c 编写thread_block函数 编写t ...

  2. Netscape Mozilla源代码指南

    为什么80%的码农都做不了架构师?>>>    Netscape Mozilla源代码指南 序 第一部分 从头开 第一章 Mozilla和Netscape开源计划 介绍Mozilla ...

  3. Java开发快速上手

    Java开发快速上手 前言 1.我的大学 2.对初学者的建议 3.大牛的三大特点 4.与他人的差距 第一章 了解Java开发语言 前言 基础常识 1.1 什么是Java 1.1.1 跨平台性 1.2 ...

  4. 使用 TCP / IP 套接字(Sockets)

    使用TCP / IP 套接字(Sockets) TCP/IP 套接字提供了跨网络的低层控制.TCP/IP套接字是两台计算机之间的逻辑连接,通过它,计算机能够在任何时间发送或接收数据:这个连接一直保持, ...

  5. 李兴华课程 java学习笔记

    学习笔记 第一章 Java Java的特点 1.java语言足够简单,正因为足够简单,所有才能让人们有更多的发挥空间 2.java是一门面向对象的编程语言 3.java是为数不多的多线程编程语言 4. ...

  6. Review of Software Construction

    ソフトウェア構築の復習 @1.0 ver. 文章目录 ソフトウェア構築の復習 @1.0 ver. @[toc] 第一章 第一节 软件构造的多维视图 Buildtime概述 Runtime概述 第二节 ...

  7. 成功入职字节跳动的小姐姐告诉你,Android面试吃透这一篇就没有拿不到的offer!

    文章目录 写在前面 来,发车了! 1. 战略定位:Android面试都会问些什么? 2. 运筹帷幄:我需要形成什么样的知识体系? 3. 披襟斩将:我需要掌握多少知识? 4. 锦上添花:面试过程中适用的 ...

  8. 《J2SE 回炉再造16》-------溺水狗

    第十章 线程 1. 提纲 2. 线程的基本概念 进程是一个静态的概念,严格意义上讲并不能执行,我们所说的进程执行指的是进程里的主线程(main()方法)开始执行了 3. 线程的创建和启动 只要可以使用 ...

  9. 《操作系统真象还原》从零开始自制操作系统 全流程记录

    文章目录 前引 章节博客链接 实现源码链接 前引 这本<操作系统真象还原>里面一共有十五个章节 大约760页 这些系列博客也是我在做完哈工大操作系统Lab之后 觉得还是有些朦朦胧胧 毅然决 ...

最新文章

  1. SAP WM LT42创建TO,报错-No entry in Table 329S (NM1 B)-
  2. 机器人 陆梅东_机器人知识与实践比赛获奖 - 上海徐汇区青少年活动中心
  3. angular 指令渲染_Angular JS指令有后期渲染回调吗?
  4. JAVA NIO概述(一):I/O模型
  5. redux异步action_React躬行记(12)——Redux中间件
  6. linux服务器搭建教程c,Linux服务器上搭建web项目环境
  7. Docker学习总结(42)——Docker Compose 入门学习
  8. 魔法值(【CCF】NOI Online能力测试3 提高组)
  9. zabbix添加表达式
  10. WebHubBot 网络爬虫
  11. 计算机网络考研知识点总结,2020计算机专业考研的计算机网络部分知识点
  12. 两台电脑之间实现串口通信
  13. excel表格拆分多个表如何操作
  14. 【window】解决word,excel,PowerPoint 等office图标不显示问题
  15. 【软件工程习题(含参考答案)】软件系统分析-五道题
  16. String.matches()的用法
  17. 计算机绘图c2是什么,Cad里c2表示(cad中C1(C2)这样的符号是什么意思)
  18. AltiumDesigner 19软件安装步骤
  19. 【Go实现】实践GoF的23种设计模式:命令模式
  20. JavaJDK下载安装与环境配置(Windows 10 超详细的图文版教程 )

热门文章

  1. 西游记中孙悟空有哪些技能?
  2. Java数组之一维数值数组之成绩统计
  3. 2018.11.14 Chopin’s
  4. XiaoHu日志 9/27~10/18
  5. 方便又高效,这几款远程办公软件值得学习
  6. c语言c 下载文件,如何在C/C++中使用WinHTTP下载文件?
  7. 导入maven项目,报错Cannot read lifecycle mapping metadata for artifact org.apache.maven.plugins:mav
  8. myBatis中实用技巧
  9. week11——实验(月模拟题3:CSP201609 03)
  10. 小米2s刷原生安卓_小米2S升级安卓5.0原生ROM下载刷机教程