黑马程序员 学习日志08 多线程

来源:互联网 发布:江苏盐城大数据产业园 编辑:程序博客网 时间:2024/04/28 12:48
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

多线程

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

线程:就是进程中的一个独立的控制单元。
线程在控制着进程的执行。

一个进程中至少有一个线程。

java VM启动时会有一个进程java.exe

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


创建线程的第一种方式:继承Thread类
步骤:
1.定义类继承Thread类
2.重写Thread类中的run()方法
3.调用线程的start()方法。
该方法有两个作用
1.启动线程 2.运行run()方法。

为什么要重写run()方法呢?
因为Thread类用于描述线程
该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run()方法。
<span style="font-size:24px;">//例主线程和Test线程同时运行class Test extends Thread{public void run(){for(int x = 0 ; x <100;x++){System.out.println(x);}}}class ThreadTest{public static void main(String[] args){Test t = new Test();t.start;for(int y = 0 ; y <100; y ++){System.out.println(y);}}}</span>

线程运行状态

线程被创建之后通过start()方法开始运行:
先进入阻塞状态:具备运行资格,但没有拿到CPU执行权。拿到执行权之后,开始运行run()方法
如果运行中调用了sleep()以及wait()方法,那么该线程进入冻结状态 直到sleep()时间结束或者被notify()方法唤醒。
如果:run()方法运行结束或者调用了stop()方法,该线程进入消亡状态。


通过Thread类的静态方法currentThread()可以获取当前线程对象。
getName()可以获取名称

设置线程名称:setName()或者构造函数


创建线程的第二种方式:实现Runnable接口
步骤:
1.定义类实现Runnable接口
2.覆盖Runnable接口中的run()方法。
3.通过Thread类建立线程对象。
4.将Runnable接口的子对象作为参数传递给Thread类的构造函数。
5.调用Thread类的start方法开启线程并调用Runnable接口子类的run()方法。
<span style="font-size:24px;">class Test implements Runnable{public void run(){for(int x = 0 ; x<100;x++){System.out.println(x);}}}class RunTest{public static void main(String[] args){Test t = new Test();Thread t1 = new Thread(t);t1.start();}}</span>



实现方式和继承方式有什么区别呢?
实现方式避免了单继承的局限性。
在定义线程时,推荐使用实现方式。

继承Thread方式:将线程代码存放在Thread类的run()方法中。
实现Runnable方式:将线程代码存放在Runnable接口子类的run()方法中


多线程运行安全问题

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

解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。

java对多线程安全问题提供了专业的解决方式。

就是同步代码块
synchronized(对象){
需要被同步的代码
}

对象如同锁。持有锁的线程可以在同步中执行。
没有持有锁的线程即使获得CPU的执行权,也进不去,因为没有锁
<span style="font-size:24px;">class Ticket implements Runnable{private int tick = 100;Object obj = new Object();public void run(){</span>
<span style="font-size:24px;"><span style="white-space:pre"></span>    <span style="white-space:pre"></span>while(true){<span style="white-space:pre"></span>synchronized(obj){<span style="white-space:pre"></span>if(tick>0){<span style="white-space:pre"></span>System.out.println(Thread.currentThread().</span>
<span style="font-size:24px;"><span style="white-space:pre"></span>getName()+"...."+tick--);<span style="white-space:pre"></span>}</span>
<span style="font-size:24px;"><span style="white-space:pre"></span>}}}}</span>

同步的前提:
1.必须要有两个或两个以上的线程。
2.必须多个线程使用同一个锁。

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

弊端:多个线程必须判断锁,比较占用资源。

如何找出需要同步的代码?
1.明确哪些代码是多线程运行代码。
2.明确共享数据。
3.明确多线程代码中哪些语句是操作共享数据的。


同步函数:
public synchronized void method(){
需要同步的代码
}
函数需要被对象调用,那么函数都有一个所属对象引用。就是this
所以同步函数用的锁是this。

如果同步函数被静态修饰后,使用的锁是什么呢?
静态进内存时,内存中没有本类对象,但一定有该类对应的字节码文件对象。
类名.class 该对象的类型是class

静态的同步方法使用的锁是该方法所在类的字节码文件对象:类名.class

死锁:同步嵌套统一发生死锁 程序卡住 例如:
<span style="font-size:24px;">public class Employee{public static void main(String[] args) {Thread t1 = new Thread(new Test(true));Thread t2 = new Thread(new Test(false));t1.start();t2.start();}}class Ob{static Object o = new Object();static Object c = new Object();}class Test implements Runnable{private boolean flag ;public Test(boolean flag){this.flag = flag;}public void run(){if(flag){while(true){synchronized(Ob.o){System.out.print("if a");synchronized(Ob.c){System.out.println("if b ");}}}}else{while(true){synchronized(Ob.c){System.out.println("else b");synchronized(Ob.o){System.out.println("else a");}}}}}}</span>




0 0
原创粉丝点击