黑马程序员-多线程(一)

来源:互联网 发布:淘宝排名优化 编辑:程序博客网 时间:2024/05/24 06:50
------- android培训、java培训、期待与您交流! ----------

多线程的概念:

进程:是一个正在执行中的程序

每一个进程都有一个执行顺序,该顺序是一个执行路径或者叫一个控 制单元

线程:就是进程中的一个独立的控制单元

线程在控制着进程的执行,一个进程中至少有一个线程

例如:

Java虚拟机启动的时候会有一个进程java.exe.

该进程中至少一个线程负责java程序的执行。而且这个线程运行的代码 存在于main方法中。该线程称之为主线程。

扩展:其实更细节说明jvmjvm启动不止一个线程,还有负责垃圾回收 机制的线程。

 

多线程存在的意义:对程序进行优化

 

线程的创建方式:

创建线程方式1:

通过对api的查找,java已经提供了对线程这类事物的描述。就Thread 类。

创建线程的第一种方式:继承Thread类。

步骤:

1,定义类继承Thread

2,复写Thread类中的run方法。

目的:将自定义代码存储在run方法。让线程运行。

3,调用线程的start方法,

该方法两个作用:启动线程,调用run方法。

如下代码:

class Demo extends Thread{public void run(){for(int x=0; x<60; x++)System.out.println("demo run----"+x);}}class ThreadDemo {public static void main(String[] args) {//for(int x=0; x<4000; x++)//System.out.println("Hello World!"); Demo d = new Demo();//创建好一个线程。//d.start();//开启线程并执行该线程的run方法。d.run();//仅仅是对象调用方法。而线程创建了,并没有运行。 for(int x=0; x<60; x++)System.out.println("Hello World!--"+x);}}

发现每次运行结果都不同,因为多个线程都获取cpu的执行权,cpu执行到谁,谁就运行,明确一点,在某一时刻,只能有一个程序在运行(多核除外),cpu在做着快速切换,已达到看上去是同时运行的效果

 

我们可以把多线程的运行行为形象的看做是在互相抢夺cpu的执行权

这就是多线程的一个特性:随机性,谁抢到谁执行,至于执行多长由cpu说了算.

 

为什么要覆盖run方法呢?

Thread类用于描述线程

该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法.也就是说Thread类中的run方法是用于存储线程要运行的代码

 

 

线程练习1:创建两个线程,和主线程交替运行

代码如下:

class ThreadA extends Thread  //创建线程A{//private String name;public ThreadA(String name) {this.name=name;super(name);}public void run(){for(int x=0;x<100;x++)System.out.println(name+"====="+x);}}public class ThreadTest1 {public static void main(String[] args) {ThreadA a1=new ThreadA("A线程运行");ThreadA a2=new ThreadA("B线程运行");a1.start();a2.start();for(int x=0;x<100;x++)System.out.println("主线程运行======"+x);}}

线程的四种状态+一种特殊状态:

如下图:


创建线程方式2: 实现Runable接口

步骤:

1.定义类实现Runnable接口

2.覆盖Runnable接口中的run方法。

将线程要运行的代码存放在该run方法中。

3.通过Thread类建立线程对象。

4.Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。

为什么要将Runnable接口的子类对象传递给Thread的构造函数?

因为,自定义的run方法所属的对象是Runnable接口的子类对象,

所以要让线程去指定指定对象的run方法。就必须明确该run方法所属 对象。

5.调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。

 

两种方式的区别:

继承Thread:线程代码存放在Thread子类run方法中.

实现Runnable:线程代码存放在接口的子类的run方法中

 

 

多线程的安全问题:

如下代码:

/* * 需求:简单的卖票程序,多个窗口同时卖票 * */class Ticket1 implements Runnable{private int tick =100;private boolean flag=true;public void run(){while(flag){if(tick>0){try {Thread.sleep(20);/*如果这里把线程sleep后悔打印出-1,-2等错票,多线程的运行出现了安全问题 */} catch (Exception e) {// TODO: handle exception}System.out.println(Thread.currentThread().getName()+": 要票的赶紧的,还剩:"+tick--+"张票了");}else {System.out.println(Thread.currentThread().getName()+": 票卖完了");flag=false;}}}}public class ThreadDemo3 {public static void main(String[] args) {Ticket1 t=new Ticket1();Thread t1=new Thread(t);Thread t2=new Thread(t);Thread t3=new Thread(t);Thread t4=new Thread(t);t1.start();t2.start();t3.start();t4.start();}}

通过分析,发现打印出0,-1,-2等错票.

多线程的运行出现了安全问题

问题的原因:

当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完的时候另一个线程参与进来执行,导致共享数据的错误.

 

解决办法:

对多条操作共享数据的语句,只有让一个线程都执行完的时候,其他线程才能执行,在执行过程中不允许其他线程执行

 

Java对于多线程的安全问题提供了专业的解决方式,就是同步代码块:

Synchronized(对象)

{

需要被同步的代码;

}

对象如同锁,持有锁的线程可以在同步中执行,

没有持有锁的线程即使获取cpu的执行权也进不去,因为没有锁

就如同火车上的卫生间------很经典

同步的前提:

1.必须要有两个或者两个以上的线程.

2.必须是多个线程使用同一个锁.

3.必须保证同步中只有一个线程在运行.

 

好处:解决了多线程的安全问题

弊端:多消耗了资源,因为每次都要判断同步中师父有其他线程


同步函数:

把synchronized放在函数上作为修饰符后,那么该函数就是同步函数

同步函数用的是哪一个锁呢?

函数需要被对象调用。那么函数都有一个所属对象引用。就是this。

所以同步函数使用的锁是this。

 

通过该程序进行验证:

使用两个线程来买票。

一个线程在同步代码块中。

一个线程在同步函数中。

都在执行卖票动作。

如下代码:

class Ticket implements Runnable{private  int tick = 100;Object obj = new Object();boolean flag = true;public  void run(){if(flag){while(true){synchronized(this){if(tick>0){try{Thread.sleep(10);}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);}}}}elsewhile(true)show();}public synchronized void show()//this{if(tick>0){try{Thread.sleep(10);}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);}}} class  ThisLockDemo{public static void main(String[] args) {Ticket t = new Ticket();Thread t1 = new Thread(t);Thread t2 = new Thread(t);t1.start();try{Thread.sleep(10);}catch(Exception e){}t.flag = false;t2.start();}}

死锁:

同步中嵌套同步,而锁却不同

如下代码:

class DieLock implements Runnable{private boolean flag;public DieLock(boolean flag) {this.flag=flag;}public void run(){if (flag) {synchronized (MyLock.locka) {System.out.println("if=====locka");synchronized (MyLock.lockb) {System.out.println("if=====lockb");}}}else {synchronized (MyLock.lockb) {System.out.println("else=====lockb");synchronized (MyLock.locka) {System.out.println("else=====locka");}}}}}class MyLock{static Object locka=new Object();static Object lockb=new Object();}public class DieLockDemo {public static void main(String[] args) {Thread t1=new Thread(new DieLock(true));Thread t2=new Thread(new DieLock(false));t1.start();t2.start();}}



0 0