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

 多线程

前言:传说,很多人的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 block
e.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方法中调用该新的方法即可.

 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值