传智播客风清扬视频-------线程简介

想了解线程,必须先了解进程,因为线程是依赖进程存在的。

什么是进程?
进程就是正在运行的程序;是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。

多进程有什么意义?
单进程的计算机只能做一件事情,而我们现在的计算机可以做多件事情。
举例:一边玩游戏,一边听音乐等
现在的计算机都支持多进程的,可以在一个时间段内执行多个任务。并且,可以提高CPU使用率。

什么是线程?
在同一个进程内又可以执行多个任务,而每一个任务可以看成是一个线程。
线程:是程序的执行单元,执行路径。是程序使用CPU的最基本单位。
单线程:如果程序只有一条执行路径。
多线程:如果程序有多条执行路径。

多线程有什么意义呢?
多线程的存在,不是提高程序的执行速度。其实是为了提高应用程序的使用率。
程序的执行其实都是在抢CPU的资源,CPU的执行权。
多个进程是在抢这个资源,而其中的某一个进程如果执行路径比较多,就会有更高的机率抢到CPU的执行权。
我们不敢保证哪一个线程能够在哪个时刻抢到,所以线程执行有随机性。


并行和并发的区别:
并行:是逻辑上同时发生,指在某一个时间内同时运行多个程序。
并发:是物理上同时发生,指在某一个时间点同时运行多个程序。

Java程序的运行原理:
由java命令启动JVM,JVM启动就相当于启动一个进程。
接着有该进程创建了一个主线程去调main方法。


方法1:继承Thread实现线程
步骤:
A:自定义类MyThread继承Thread类
B:MyThread类里面重写run()
C:创建对象
D:启动线程

该类为什么重写run()方法? 不是类中所有代码都需要被线程执行的。而这个时候,为了区分哪些代码能够被线程执行,Java提供了run方法来包含被线程执行的代码。


public class MyThread extends Thread {

@Override
public void run() {
// 一般来说,被线程执行的代码肯定是比较耗时的。
// 所以我们用循环改进
for (int x = 0; x < 200; x++) {
System.out.println(x);
}
}

}

public class MyThreadDemo {
public static void main(String[] args) {
// 创建线程对象
// MyThread my = new MyThread();
// // 启动线程
// my.run();
// my.run();
// 调用run()方法为什么是单线程的呢?
// 因为run()方法直接调用其实就相当于普通的方法调用,所以你看到的是单线程的效果
// 要想看到多线程的效果,就必须说说另一个方法:start()
// 面试题:run()和start()的区别?
// run():仅仅是封装被线程执行的代码,直接调用是普通方法
// start():首先启动了线程,然后再由jvm去调用该线程的run()方法。
// MyThread my = new MyThread();
// my.start();
// // IllegalThreadStateException:非法的线程状态异常
// // 为什么呢?因为这个相当于是my线程被调用了两次。而不是两个线程启动。
// my.start();

// 创建两个线程对象
MyThread my1 = new MyThread();
MyThread my2 = new MyThread();

my1.start();
my2.start();
}
}



获取线程对象的名称:
public final String getName() :获取线程对象的名称

设置线程对象的名称:
public final void setName()

针对不是Thread类的子类中如何获取线程对象名称
public static Thread currentThread() : 返回当前正在执行的线程对象
Thread.currentThread().getName()


public class MyThreadDemo {
public static void main(String[] args) {
// 创建线程对象
//无参构造+setXxx()
// MyThread my1 = new MyThread();
// MyThread my2 = new MyThread();
// //调用方法设置名称
// my1.setName("林青霞");
// my2.setName("刘意");
// my1.start();
// my2.start();

//带参构造方法给线程起名字
// MyThread my1 = new MyThread("林青霞");
// MyThread my2 = new MyThread("刘意");
// my1.start();
// my2.start();

//我要获取main方法所在的线程对象的名称,该怎么办呢?
//遇到这种情况,Thread类提供了一个很好玩的方法:
//public static Thread currentThread():返回当前正在执行的线程对象
System.out.println(Thread.currentThread().getName());
}
}


public final void setDaemon(boolean on)将线程设置为守护线程或用户线程
当正在运行的线程都是守护线程时,Java虚拟机退出, 该方法必须在启动前调用



public class ThreadDaemon extends Thread {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(getName() + ":" + x);
}
}
}

public class ThreadDaemonDemo {
public static void main(String[] args) {
ThreadDaemon td1 = new ThreadDaemon();
ThreadDaemon td2 = new ThreadDaemon();

td1.setName("关羽");
td2.setName("张飞");

// 设置守护线程
td1.setDaemon(true);
td2.setDaemon(true);

td1.start();
td2.start();

Thread.currentThread().setName("刘备");
for (int x = 0; x < 5; x++) {
System.out.println(Thread.currentThread().getName() + ":" + x);
}
}
}


public final void join() ; 等待该线程终止后再调用其它线程

public class ThreadJoinDemo {
public static void main(String[] args) {
ThreadJoin tj1 = new ThreadJoin();
ThreadJoin tj2 = new ThreadJoin();
ThreadJoin tj3 = new ThreadJoin();

tj1.setName("李渊");
tj2.setName("李世民");
tj3.setName("李元霸");

tj1.start();
try {
tj1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}

tj2.start();
tj3.start();
}
}


设置线程对象优先级
public final int getPriority() :返回线程对象优先级
public final void setPriority(int ne) : 设置线程优先级

注意:
线程默认优先级是5
线程优先级的范围是 : 1-10
线程优先级高仅表示线程获取的CPU时间片的几率高,需要在次数比较多,或多次运行时效果才比较显著。

IllegalArgumentException:非法参数异常。
抛出的异常表明向方法传递了一个不合法或不正确的参数。

public class ThreadPriority extends Thread {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(getName() + ":" + x);
}
}
}


public class ThreadPriorityDemo {
public static void main(String[] args) {
ThreadPriority tp1 = new ThreadPriority();
ThreadPriority tp2 = new ThreadPriority();
ThreadPriority tp3 = new ThreadPriority();

tp1.setName("东方不败");
tp2.setName("岳不群");
tp3.setName("林平之");

// 获取默认优先级
// System.out.println(tp1.getPriority());
// System.out.println(tp2.getPriority());
// System.out.println(tp3.getPriority());

// 设置线程优先级
// tp1.setPriority(100000);

//设置正确的线程优先级
tp1.setPriority(10);
tp2.setPriority(1);

tp1.start();
tp2.start();
tp3.start();
}
}


public static void sleep() 线程休眠


public class ThreadSleep extends Thread {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(getName() + ":" + x + ",日期:" + new Date());
// 睡眠
// 困了,我稍微休息1秒钟
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

public class ThreadSleepDemo {
public static void main(String[] args) {
ThreadSleep ts1 = new ThreadSleep();
ThreadSleep ts2 = new ThreadSleep();
ThreadSleep ts3 = new ThreadSleep();

ts1.setName("林青霞");
ts2.setName("林志玲");
ts3.setName("林志颖");

ts1.start();
ts2.start();
ts3.start();
}
}


线程中断
public final void stop() : 让线程停止,过时了, 超过一定时间,直接结束进程
public final void interrupt() : 中断线程,把线程的状态终止,并抛出一个InterruptedException。


public class ThreadStop extends Thread {
@Override
public void run() {
System.out.println("开始执行:" + new Date());

// 我要休息10秒钟,亲,不要打扰我哦
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// e.printStackTrace();
System.out.println("线程被终止了");
}

System.out.println("结束执行:" + new Date());
}
}

public class ThreadStopDemo {
public static void main(String[] args) {
ThreadStop ts = new ThreadStop();
ts.start();

// 你超过三秒不醒过来,
try {
Thread.sleep(3000);
// ts.stop(); // 结束线程
ts.interrupt(); //抛出异常,将线程状态终止
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}


public static void yield() : 暂停当前正在执行的线程,让CPU去执行其它线程
让多个线程执行更和谐

public class ThreadYield extends Thread {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(getName() + ":" + x);
Thread.yield();
}
}
}

public class ThreadYieldDemo {
public static void main(String[] args) {
ThreadYield ty1 = new ThreadYield();
ThreadYield ty2 = new ThreadYield();

ty1.setName("林青霞");
ty2.setName("刘意");

ty1.start();
ty2.start();
}
}



方法二: 实现Runnable接口
步骤 : A:自定义MyRunnable实现Runnable接口
B:重写run()方法
C:创建MyRunnable类的对象
D:创建Thread类的对象,并把c步骤的对象作为构造参数传递



public class MyRunnable implements Runnable {

@Override
public void run() {
for (int x = 0; x < 100; x++) {
// 由于实现接口的方式就不能直接使用Thread类的方法了,但是可以间接的使用
System.out.println(Thread.currentThread().getName() + ":" + x);
}
}

}

public class MyRunnableDemo {
public static void main(String[] args) {
// 创建MyRunnable类的对象
MyRunnable my = new MyRunnable();

// 创建Thread类的对象,并把C步骤的对象作为构造参数传递
// Thread(Runnable target)
// Thread t1 = new Thread(my);
// Thread t2 = new Thread(my);
// t1.setName("林青霞");
// t2.setName("刘意");

// Thread(Runnable target, String name)
Thread t1 = new Thread(my, "林青霞");
Thread t2 = new Thread(my, "刘意");

t1.start();
t2.start();
}
}




案例:模拟售票

版本1 : 继承Thread类

public class SellTicket extends Thread {

// 定义100张票
// private int tickets = 100;
// 为了让多个线程对象共享这100张票,我们其实应该用静态修饰
private static int tickets = 100;

@Override
public void run() {
// 定义100张票
// 每个线程进来都会走这里,这样的话,每个线程对象相当于买的是自己的那100张票,这不合理,所以应该定义到外面
// int tickets = 100;

// 是为了模拟一直有票
while (true) {
if (tickets > 0) {
System.out.println(getName() + "正在出售第" + (tickets--) + "张票");
}
}
}
}

public class SellTicketDemo {
public static void main(String[] args) {
// 创建三个线程对象
SellTicket st1 = new SellTicket();
SellTicket st2 = new SellTicket();
SellTicket st3 = new SellTicket();

// 给线程对象起名字
st1.setName("窗口1");
st2.setName("窗口2");
st3.setName("窗口3");

// 启动线程
st1.start();
st2.start();
st3.start();
}
}


版本二: 实现Runnable接口

public class SellTicket implements Runnable {
// 定义100张票
private int tickets = 100;

@Override
public void run() {
while (true) {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售第"
+ (tickets--) + "张票");
}
}
}
}

public class SellTicketDemo {
public static void main(String[] args) {
// 创建资源对象
SellTicket st = new SellTicket();

// 创建三个线程对象
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");

// 启动线程
t1.start();
t2.start();
t3.start();
}
}



版本三:通过sleep() 暴露出问题

public class SellTicket implements Runnable {
// 定义100张票
private int tickets = 100;

// @Override
// public void run() {
// while (true) {
// // t1,t2,t3三个线程
// // 这一次的tickets = 100;
// if (tickets > 0) {
// // 为了模拟更真实的场景,我们稍作休息
// try {
// Thread.sleep(100); // t1就稍作休息,t2就稍作休息
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//
// System.out.println(Thread.currentThread().getName() + "正在出售第"
// + (tickets--) + "张票");
// // 理想状态:
// // 窗口1正在出售第100张票
// // 窗口2正在出售第99张票
// // 但是呢?
// // CPU的每一次执行必须是一个原子性(最简单基本的)的操作。
// // 先记录以前的值
// // 接着把ticket--
// // 然后输出以前的值(t2来了)
// // ticket的值就变成了99
// // 窗口1正在出售第100张票
// // 窗口2正在出售第100张票
//
// }
// }
// }

@Override
public void run() {
while (true) {
// t1,t2,t3三个线程
// 这一次的tickets = 1;
if (tickets > 0) {
// 为了模拟更真实的场景,我们稍作休息
try {
Thread.sleep(100); //t1进来了并休息,t2进来了并休息,t3进来了并休息,
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println(Thread.currentThread().getName() + "正在出售第"
+ (tickets--) + "张票");
//窗口1正在出售第1张票,tickets=0
//窗口2正在出售第0张票,tickets=-1
//窗口3正在出售第-1张票,tickets=-2
}
}
}
}

/*
* 实现Runnable接口的方式实现
*
* 通过加入延迟后,就产生了连个问题:
* A:相同的票卖了多次
* CPU的一次操作必须是原子性的
* B:出现了负数票
* 随机性和延迟导致的
*/
public class SellTicketDemo {
public static void main(String[] args) {
// 创建资源对象
SellTicket st = new SellTicket();

// 创建三个线程对象
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");

// 启动线程
t1.start();
t2.start();
t3.start();
}
}



如何解决线程安全问题?
分析哪些原因导致问题:
A: 是否是多线程环境
B: 是否共享数据
C: 是否有多条语句操作共享数据

解决思路:
把多条语句操作共享数据的代码给包成一个整体,让某一个线程在执行的时候,别的线程不能进入。
同步代码块:
synchronized(对象){
需要同步的代码
}
注意: 同步可以解决安全问题的根本原因就在那个对象上,该对象如同锁的功能。
多个线程必须是同一把锁。

版本四:使用synchronized 同步解决问题

public class SellTicket implements Runnable {
// 定义100张票
private int tickets = 100;
//创建锁对象
private Object obj = new Object();

// @Override
// public void run() {
// while (true) {
// synchronized(new Object()){
// if (tickets > 0) {
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName() + "正在出售第"
// + (tickets--) + "张票");
// }
// }
// }
// }

@Override
public void run() {
while (true) {
synchronized (obj) {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "正在出售第" + (tickets--) + "张票");
}
}
}
}
}

public class SellTicketDemo {
public static void main(String[] args) {
// 创建资源对象
SellTicket st = new SellTicket();

// 创建三个线程对象
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");

// 启动线程
t1.start();
t2.start();
t3.start();
}
}



同步的好处: 可以解决多线程的安全问题
同步的弊端: 当线程相当多时,因为每个线程都会去判断同步上的锁,
这是很耗资源的,无形中降低了程序运行的效率。


A:同步代码块的锁对象是: 任意对象
B:同步方法的格式及锁对象:
把同步方法关键字加在方法上
同步方法的锁对象是this
C:静态方法及代码块是: 类的字节码对象


public class SellTicket implements Runnable {

// 定义100张票
private static int tickets = 100;

// 定义同一把锁
private Object obj = new Object();
private Demo d = new Demo();

private int x = 0;

//同步代码块用obj做锁
// @Override
// public void run() {
// while (true) {
// synchronized (obj) {
// if (tickets > 0) {
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()
// + "正在出售第" + (tickets--) + "张票 ");
// }
// }
// }
// }

//同步代码块用任意对象做锁
// @Override
// public void run() {
// while (true) {
// synchronized (d) {
// if (tickets > 0) {
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()
// + "正在出售第" + (tickets--) + "张票 ");
// }
// }
// }
// }

@Override
public void run() {
while (true) {
if(x%2==0){
synchronized (SellTicket.class) {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "正在出售第" + (tickets--) + "张票 ");
}
}
}else {
// synchronized (d) {
// if (tickets > 0) {
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()
// + "正在出售第" + (tickets--) + "张票 ");
// }
// }

sellTicket();

}
x++;
}
}

// private void sellTicket() {
// synchronized (d) {
// if (tickets > 0) {
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()
// + "正在出售第" + (tickets--) + "张票 ");
// }
// }
// }

//如果一个方法一进去就看到了代码被同步了,那么我就再想能不能把这个同步加在方法上呢?
// private synchronized void sellTicket() {
// if (tickets > 0) {
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()
// + "正在出售第" + (tickets--) + "张票 ");
// }
// }

private static synchronized void sellTicket() {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "正在出售第" + (tickets--) + "张票 ");
}
}
}

class Demo {
}


public class SellTicketDemo {
public static void main(String[] args) {
// 创建资源对象
SellTicket st = new SellTicket();

// 创建三个线程对象
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");

// 启动线程
t1.start();
t2.start();
t3.start();
}
}


将集合变成线程安全

public class ThreadDemo {
public static void main(String[] args) {
// 线程安全的类
StringBuffer sb = new StringBuffer();
Vector<String> v = new Vector<String>();
Hashtable<String, String> h = new Hashtable<String, String>();

// Vector是线程安全的时候才去考虑使用的,但是我还说过即使要安全,我也不用你
// 那么到底用谁呢?
// public static <T> List<T> synchronizedList(List<T> list)
List<String> list1 = new ArrayList<String>();// 线程不安全
List<String> list2 = Collections
.synchronizedList(new ArrayList<String>()); // 线程安全
}
}



总结:

(1)多线程: 一个应用程序多条执行路径
进程: 正在执行的应用程序
线程: 进程的执行单元,执行路径
单线程: 一个应用程序只有一条执行路径
多线程: 一个应用程序有多条执行路径

多进程的意义: 提高CPU的使用率
多线程的意义: 提高应用程序的使用率

(2)Java程序的运行原理及JVM的启动是多线程的吗?
A:JAVA命令去启动JVM,JVM就会启动一个进程,该进程会启动主线程。
B:JVM的启动是多线程的,因为最低有两个线程启动主线程和垃圾回收线程

(3)多线程实现方案
A:继承Thread类
B:实现Runnable接口
(4)线程的调度和优先级问题
A:线程的调度 a:分时调度 b:抢占式调度(java采用该种)
B:获取和设置线程优先级 a默认是5 b范围是1-10
(5)线程的控制
A:休眠线程 B:加入线程 C:礼让线程 D:后台线程 E:终止线程
(6)线程的生命周期
A:新建 B:就绪 C:运行 D:阻塞 E:死亡
(7)多线程安全问题的原因
A:是否有多线程环境
B:是否有共享数据
C:是否有多条语句操作共享数据
(8)同步解决线程安全问题
A:同步代码块
synchronized(对象){
需要被同步的代码
}
这里的锁对象可以是任意对象
B:同步方法
把同步加在方法上
这里的锁对象是this
C:静态同步方法
把同步加在方法上
这里的锁对象是当前类的字节码文件对象

(9)回顾以前的线程安全的类
A:StringBuffer
B:Vector
C:Hashtable
D:如何把一个线程不安全的集合类变成一个线程安全的集合类
用Collections工具类的方法即可。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值