线程笔记1——多线程概述

来源:互联网 发布:请假流程数据库设计 编辑:程序博客网 时间:2024/06/06 21:05

 多线程

前言:传说,很多人的Java入门到放弃都是因为多线程。大多数初学者表示很难理解多线程的原理,老师也说每次多线程都会准备讲两遍或三遍,当时听到这些,我好方!!感觉随时做好了shi的准备。However,现在学完了多线程,觉得没有那么复杂,但确实会有的懵,要反复去领悟,多敲几次代码,书读百遍,其义自见嘛!那么,接下来就来看看我的多线程总结吧!!


一、什么是线程,线程和进程的联系、区别?

  1. 进程进程是指一个内存中运行中的应用程序。每个进程都有自己独立的一块内存空间,一个应用程序可以同时启动多个进程。比如在Windows系统中,一个运行的abc.exe就是一个进程。
  2. 线程:线程是指进程中的一个执行任务(控制单元)一个进程可以同时并发运行多个线程,如:多线程下载软件,多任务系统,该系统可以运行多个进程,一个进程也可以执行多个任务,一个进程可以包含多个线程。
  3. 进程和线程的关系:1)一个进程至少有一个线程,为了提高效率,可以在一个进程中开启多个执行任务,即多线程。2)多进程:操作系统中同时运行的多个程序。3)多线程:在同一个进程中同时运行的多个任务。
  4. 进程与线程的区别:1)进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。2)线程:堆空间是共享的,栈空间是独立的,线程消耗的资源也比进程小,相互之间可以影响的,又称为轻型进程或进程元。

二、一个Java程序中,线程有三个部分组成:虚拟的cpu代码数据。至少有2个线程,分别是主线程垃圾回收线程(后台线程)

三、线程的各种状态

1、创建状态:在程序中用构造方法创建了一个线程对象后,即是处于新建状态了,但还不可运行。Thread thread = new Thread()”。

2、就绪状态:新建线程对象后,调用该线程的start()方法就可以启动线程,但不是马上就执行,只是进入了线程池。

3、运行状态:当就绪状态的线程被调用并获得处理器资源时(通俗来讲,就是抢到CPU的执行权),线程就进入了运行状态。此时,自动调用该线程对象的run()方法。run()定义了该线程的操作和功能。

4、堵塞状态:由于一些原因失去CPU的执行权;在可执行的状态下,如果调用sleep()suspend()wait()等方法,线程也会进入堵塞状态。解除堵塞就进入就绪状态。

5、死亡状态:线程调用stop()方法时或run()方法执行结束后,就处于死亡状态,处于死亡状态的线程不具有继续运行的能力。

四、线程的创建

方式一:继承Thread

1、子类覆盖父类中的run方法,将线程运行的代码存放在run中。

2、建立子类对象的同时线程也被创建。

3、通过调用start方法开启线程

Demo:

public class ThreadDemo {public static void main(String[] args) {Test t1 = new Test("haoren");t1.start();Test t2 = new Test("huairen");t2.start();} }class Test extends Thread{private String name;public  Test(String name){this.name = name;}public void run(){for(int i=0;i<10;i++){System.out.println(name+":"+i);}}}

!注意:线程不能重复开启;千万不要调用run方法,如果调用run方法好比是对象调用方法,依然还是只有一个线程,并没有开启新的线程.

 

方式二:实现Runnable接口

1、子类覆盖接口中的run方法。

2、通过Thread类创建线程,并将实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数。

3Thread类对象调用start方法开启线程。

Demo

public class RunnableDemo {public static void main(String[] args) {Test t1 = new Test("haoren");Test t2 = new Test("hairen");Thread th1 = new Thread(t1);Thread th2 = new Thread(t2);th1.start();th2.start();}}class Test implements Runnable{private String name;public  Test(String name){this.name = name;}public void run(){for(int i=0;i<10;i++){System.out.println(name+":"+i);}}}


五、ThreadRunnable的关系:

1ThreadRunnable的子类

2、实际上ThreadRunable的关系和我们前边讲的代理设计模式很像,这里的Thread就是代理类。我们自己所实现的类才是real 3Runnable可以共享对象,实际Runnable每个线程内跑的是同一个对象

六、多线程的优势:

1、进程之前不能共享内存,而线程之间共享内存(堆内存)则很简单。

2、系统创建进程时需要为该进程重新分配系统资源,创建线程则代价小很多,因此实现多任务并发时,多线程效率更高.

3Java语言本身内置多线程功能的支持,而不是单纯第作为底层系统的调度方式,从而简化了多线程编程.

4、多线程是为了同步完成多项任务,不是为了提供程序运行效率,而是通过提高资源使用效率来提高系统的效率。

 

七、补充知识:

宽带带宽是以位(bit)计算,而下载速度是以字节(Byte)计算,1字节(Byte)等于8位(bit),

所以1024kb/s是代表上网带宽为1024千位(1M),而下载速度需要1024千位/秒(1024kb/s)带宽除以8,得出128千字节/秒(128KB/s)。

八、线程同步[synchronized](重难点):

1、同步的特点

前提:同步需要两个或者两个以上的线程多个线程使用的是同一个锁。未满足这两个条件,不能称其为同步。

弊端:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率

2线程的安全问题

(1)原因多个线程访问出现延迟线程随机性 。(一般出现在在多个线程有数据共享,并且run中的语句不止只有一句)

(2)解决方案

a、同步代码块

语法:

synchronized(对象)

{

     需要同步操作的代码

}

Demo:(同步的经典案例--卖票)

public class TicketDemo {public static void main(String[] args) {Person2 p1 = new Person2("haoren");Thread t1 = new Thread(p1);Thread t2 = new Thread(p1);t1.start();t2.start(); } }class Person2 implements Runnable {private String name;private int tickets = 5;public Person2(String name) {this.name = name;}Object object1 = new Object();public void run() {for (int i = 0; i < 5; i++) {synchronized (object1) {//object1就是传说中的锁,要同步必须使用同一个锁if (tickets > 0) {try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(tickets--);}}} }}


 

同步锁:

为了保证每个线程都能正常执行原子操作,Java引入了线程同步机制.

同步监听对象/同步锁/同步监听器/互斥锁

对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.

Java程序运行使用任何对象作为同步监听对象,但是一般的,我们把当前并发访问的共同资源作为同步监听对象.

!注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着.

b同步方法

语法:在函数上加上synchronized修饰符即可

同步锁是谁:

      对于非static方法,同步锁就是this.  

      对于static方法,我们使用当前方法所在类的字节码对象(Apple2.class).

同步函数的锁是this,而同步代码块的锁可以是任意对象

Demo:

public class Tickets1 {public static void main(String[] args) {/* * GetTickets gt1 = new GetTickets(); GetTickets gt2 = new GetTickets(); * GetTickets gt3 = new GetTickets(); gt1.setName("窗口一"); * gt2.setName("窗口二"); gt3.setName("窗口三"); gt1.start(); gt2.start(); * gt3.start(); */GetTickets2 gt = new GetTickets2();Thread th1 = new Thread(gt, "窗口一");Thread th2 = new Thread(gt, "窗口二");Thread th3 = new Thread(gt, "窗口三");th1.start();try {Thread.sleep(500);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}gt.flag = true;th2.start();th3.start();} } class GetTickets2 implements Runnable { private int tickets = 10;boolean flag = false;Object ob = new Object();public void run() {if (flag) {for (int i = 0; i < 10; i++) {//synchronized (ob) {//如果用ob就无法同步synchronized (this) {if (tickets > 0) {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+ "卖出" + (tickets--) + "号票"+":同步代码块");}}}} else {for (int i = 0; i < 10; i++) {function();}}}public synchronized void function() {if (tickets > 0) {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "卖出"+ (tickets--) + "号票"+":同步函数");}}}/* * class GetTickets extends Thread{ //private static int tickets = 10; private * int tickets = 10; public void run(){ * * for (int i = 0; i < 10; i++) { if(tickets>0){ * System.out.println(Thread.currentThread().getName()+"卖出"+(tickets--)+"号票"); } * } } } */


c锁机制

!注意:不要使用synchronized修饰run方法,修饰之后,某一个线程就执行完了所有的功能.好比是多个线程出现串行.

解决方案:把需要同步操作的代码定义在一个新的方法中,并且该方法使用synchronized修饰,再在run方法中调用该新的方法即可.

 



原创粉丝点击