黑马程序员——多线程——多线程概述,实现,控制与安全问题的解决

来源:互联网 发布:ios禁止软件联网 编辑:程序博客网 时间:2024/04/30 02:15

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一.多线程



1.多线程:一个应用程序有多条执行路径
A:概述

进程:当前正在执行的程序,代表一个应用程序在内存中的执行区域
线程:是依赖于进程的执行绪(执行路径/控制单元),是程序使用CPU的基本单位。
单线程:一个进程中,只有一个线程执行。
多线程:同一个进程中,多个线程执行。这多个线程共享该进程资源
(堆内存与方法区),单栈内存独立,即每一个线程占用一个栈

B:线程并行与线程并发

a:线程并行:正常的多线程执行就是线程并行。即逻辑上同一时间同时运行。

b:线程并发:由于线程抢占而不应出现的某一时刻的线程及相关数据状态。如并发修改异常的产生。

2.Java程序的运行原理及JVM的多线程

A:Java命令去启动JVM,JVM会启动一个进程,该进程会启动一个主线程。
B:JVM的启动是多线程的,因为它最低有两个线程启动了,主线程和垃圾回收线程。

3.多线程的实现方案
方式一:继承Thread类
a:自定义类MyThread继承Thread类。
b:重写run方法(),在这里面输出1-50的数据。
c:定义测试类MyThreadTest,在测试类中创建MyThread类的多个对象。
d:调用start()


代码体现:

继承Thread类

<span style="font-family:FangSong_GB2312;font-size:18px;"><strong>package cn.blog2;public class myThread extends Thread {public myThread() {super();}// 继承父类的有参构造public myThread(String name) {super(name);}// 重写run()方法public void run() {for (int i = 0; i < 50; i++) {System.out.println(Thread.currentThread().getName() + "我是线程我被执行了"+ i);}}}</strong></span>


测试类

<span style="font-family:FangSong_GB2312;font-size:18px;"><strong>package cn.blog2;public class Test_Thread {public static void main(String[] args) {// 创建线程对象myThread thread = new myThread("我是线程1号");myThread thread2 = new myThread("我是线程2号");myThread thread3 = new myThread("我是线程3号");myThread thread4 = new myThread("我是线程4号");// 开启线程thread.start();thread2.start();thread3.start();thread4.start();}}</strong></span>


方式二:实现Runnable接口
a:自定义类MyRunnable实现Runnable接口。
b:重写run方法(),在这里面输出1-50的数据。
c:定义测试类MyThreadTest,在测试类中创建MyRunnable类的一个对象。
d:在测试类创建Thread类的多个对象,并把MyRunnable类的一个对象作为构造参数传递。
e:调用start()


代码体现:

实现Runnable接口

<span style="font-family:FangSong_GB2312;font-size:18px;"><strong>package cn.blog2;public class myRunnable implements Runnable {// 重写run()方法public void run() {for (int i = 0; i < 50; i++) {System.out.println(Thread.currentThread().getName() + "我是线程我被执行了"+ i);}}}</strong></span>


测试类

<span style="font-family:FangSong_GB2312;font-size:18px;"><strong>package cn.blog2;public class Test_Runnable {public static void main(String[] args) {myRunnable mr = new myRunnable();Thread t1 = new Thread(mr,"我是线程一号");Thread t2 = new Thread(mr,"我是线程二号");Thread t3 = new Thread(mr,"我是线程三号");Thread t4 = new Thread(mr,"我是线程四号");t1.start();t2.start();t3.start();t4.start();}}</strong></span>


方式一与方式二的区别:


方式一:a.当类去描述事物,事物中有属性和行为。如果行为中有部分代码需要被多线程所执行,
同时还在操作属性。就需要该类继承Thread类,产生该类的对象作为线程对象。
可是这样做会导致每一个对象中都存储一份属性数据。无法在多个线程中共享该数据。
加上静态,虽然实现了共享但是生命周期过长。
b.如果一个类明确了自己的父类,那么它就不可以再继承Thread。因为java不允许类的多继承。


方式二:a.将线程与运行的业务逻辑分离,可以让多个线程共享业务逻辑中的数据。
b.可以让业务类不再继承Thread而专注于业务继承其他类,避免了单继承的局限性。




4.线程的调度和优先级问题

A:线程的调度

a:分时调度
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片。

b:抢占式调度
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个。
(线程随机性)Java使用的为抢占调度模型。

B:获取和设置线程优先级

a:默认线程优先级是5
b:线程优先级范围是1-10


5.线程的方法

A:主要方法:

获取当前线程名称:
public final String getName()

设置当前线程名称:
public final void setName(String name)

返回对当前正在执行的线程对象的引用:
public static Thread currentThread()

如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法
否则,该方法不执行任何操作并返回。
public void run()

使该线程开始执行,Java虚拟机调用该线程的run方法
public void start()


B:控制方法:

线程优先级(priority)
获取优先级:public final int getPriority()
设置优先级:public final void setPriority(int newPriority)

线程礼让(暂停)(yield)
public static void yield()


线程加入(join)
public final void join() throws InterruptedException

守护线程(setDaemon)
public final void setDaemon(boolean on)


线程睡眠(sleep)
public static void sleep(long millis) throws InterruptedException


线程中断(interrupt)
public void interrupt()

` 6.线程的生命周期



7.多线程安全问题的原因

A:是否有多线程环境
B:是否有共享数据
C:是否有多条语句操作共享数据


8.同步解决线程安全问题

A:同步代码块

synchronized(对象) {
需要被同步的代码;
}
这里的锁对象可以是任意对象。

B:同步方法

把同步加在方法上。
这里的锁对象是this

C:静态同步方法

把同步加在方法上。
这里的锁对象是当前类的字节码文件对象 .class


9.卖票案例:实现多线程并解决线程安全问题

案例1:卖票多线程不同步

线程目标不同步类

<span style="font-family:FangSong_GB2312;font-size:18px;"><strong>package cn.blog2;//定义线程执行目标类实现Runnable接口public class Tickets implements Runnable {//定义票的总数为100张,多个线程共享int number = 100;//重写run()方法,定义卖票逻辑public void run() {while (true) {//处理异常try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}//如果票数是正的,说明有票,就卖票if (number > 0) {//每卖一张票就将票数减1System.out.println(Thread.currentThread().getName() + "正在卖第"+ number + "号票");number--;}}}}</strong></span>


测试类

<span style="font-family:FangSong_GB2312;font-size:18px;"><strong>package cn.blog2;public class Test_Tickets {//线程不同步卖票public static void main(String[] args) {//创建线程目标类对象Tickets tickets = new Tickets();//创建线程对象Thread thread1 = new Thread(tickets,"卖票窗口一号");Thread thread2 = new Thread(tickets,"卖票窗口二号");Thread thread3 = new Thread(tickets,"卖票窗口三号");Thread thread4 = new Thread(tickets,"卖票窗口四号");//线程开始执行thread1.start();thread2.start();thread3.start();thread4.start();}}</strong></span>


案例2:卖票多线程使用代码块同步

线程目标同步类

<span style="font-family:FangSong_GB2312;font-size:18px;"><strong>package cn.blog2;//定义线程执行目标类实现Runnable接口public class TicketsSynchronized implements Runnable {// 定义票的总数为100张,多个线程共享int number = 100;// 定义锁对象Object object = new Object();// 重写run()方法,定义卖票逻辑synchronized加入同步代码块public void run() {while (true) {// 处理异常synchronized (object) {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}// 如果票数是正的,说明有票,就卖票if (number > 0) {// 每卖一张票就将票数减1System.out.println(Thread.currentThread().getName()+ "正在卖第" + number-- + "号票");}}}}}</strong></span>


测试类

<span style="font-family:FangSong_GB2312;font-size:18px;"><strong>package cn.blog2;public class Test_TicketsSynchronized {public static void main(String[] args) {// 创建线程目标类对象TicketsSynchronized tickets = new TicketsSynchronized();// 创建线程对象Thread thread1 = new Thread(tickets, "卖票窗口一号");Thread thread2 = new Thread(tickets, "卖票窗口二号");Thread thread3 = new Thread(tickets, "卖票窗口三号");Thread thread4 = new Thread(tickets, "卖票窗口四号");// 线程开始执行thread1.start();thread2.start();thread3.start();thread4.start();}}</strong></span>


案例3:卖票多线程使用同步方法同步

线程目标同步类

<span style="font-family:FangSong_GB2312;font-size:18px;"><strong>package cn.blog2;//定义线程执行目标类实现Runnable接口public class TicketsSynchronized2 implements Runnable {// 定义票的总数为100张,多个线程共享int number = 100;// 定义变量x记录奇偶数int x = 0;// 重写run()方法,定义卖票逻辑synchronized加入同步代码块public void run() {while (true) {// 处理异常if (x % 2 == 0) {// 使用同步方法之后,锁对象就是this,不在是任意对象// 假如是静态同步方法,锁对象就是其所在类类本身也就是.classsynchronized (this) {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}// 如果票数是正的,说明有票,就卖票if (number > 0) {// 每卖一张票就将票数减1System.out.println(Thread.currentThread().getName()+ "正在卖第" + number-- + "号票");}}} else {method();}x++;}}// 定义同步方法在返回值前面加同步关键字synchronizedpublic synchronized void method() {// 方法里面是卖票的逻辑try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}// 如果票数是正的,说明有票,就卖票if (number > 0) {// 每卖一张票就将票数减1System.out.println(Thread.currentThread().getName() + "正在卖第"+ number-- + "号票");}}}</strong></span>


测试类

<span style="font-family:FangSong_GB2312;font-size:18px;"><strong>package cn.blog2;public class Test_TicketsSynchronized2 {public static void main(String[] args) {// 创建线程目标类对象TicketsSynchronized2 tickets = new TicketsSynchronized2();// 创建线程对象Thread thread1 = new Thread(tickets, "卖票窗口一号");Thread thread2 = new Thread(tickets, "卖票窗口二号");Thread thread3 = new Thread(tickets, "卖票窗口三号");Thread thread4 = new Thread(tickets, "卖票窗口四号");// 线程开始执行thread1.start();thread2.start();thread3.start();thread4.start();}}</strong></span>


0 0
原创粉丝点击