<pre name="code" class="html">
------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
多线程
我们学习的是多线程,那么要学习多线程,我们首先要学习线程.而线程是依赖于进程的,所有我们需要学习进程.1. 什么是进程?
通过任务管理器,可以查看到进程,所谓的进程就是正在执行的程序.
2. 多进程的意义?
如果计算机是单进程,那么指定一次执行一个程序.而我们现在的计算机都是多进程. 那么也就是说我们可以一边玩游戏一边听音乐.
提高CPU的使用率.
我们一边 玩游戏一边听音乐.存在两个进程,那么这个两个进程是同时执行的吗?同时: 指的是在一个时间点
不是同时执行的,因此CPU在一个时间点上只能执行一个任务,而我们看到的像是同时执行,其实是这样子的.是CPU在这个两个进程之间
进行高效的切换.
3. 什么是线程?
一个应用程序可以执行多个任务,而每一个任务就是一个线程.
4. 多线程的意义?
多线程的意义不是提高程序的运行效率,而是提高程序的使用率.
如何理解这句话呢?
我们程序在执行的时候其实都是在抢占CPU的时间片(CPU的执行权). 如果一个应用程序只有一个任务,而另一个应用程序有多个任务,那么那个应用
抢占到CPU的执行权的概率大呢?是多个任务的应用程序抢到的概率比较大.多个任务的应用程序一定就可以抢占到CPU的执行权吗?不一定,所以多线程的执行具有随机性.
并发和并行的区别:
并发: 就是在一个时间点上
并行: 就是在一个时间段上
实现多线程的两个方式:
第一种方式:
步骤:
a: 创建一个类,然后让这个类继承Thread
b: 重写run方法
c: 创建该类的对象
d: 启动线程类
public class ThreadDemo {
public static void main(String[] args) {
// 创建对象
MyThread t1 = new MyThread() ;
MyThread t2 = new MyThread() ;
// 启动线程
/**
* 不能使用run来启动线程,下边的代码其实只是在调用run方法
* 启动线程: 需要使用start方法
*/
// t1.run() ;
// t1.run() ;
// public void start()使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
// 启动线程
t1.start() ;
t2.start() ;
// java.lang.IllegalThreadStateException
// 线程的启动只能是一次,不能进行多次启动
// t1.start() ;
}
}
package cn.itcast_test02;
/**
* 为什么要重写run方法?
* 我们现在的这个类是不是可以有多个方法? 可以存在多个方法.
* 而我们的多个方法中的代码都是需要被线程执行的吗?不是的.
* 如果我们想让某一段代码被线程执行,那么我们就把这个代码写在run方法 .
* run方法中写的代码就是需要被线程执行的代码.
*
* run方法中的代码的特点:
* 写的代码都是比较耗时的代码
*/
public class MyThread extends Thread {
@Override
public void run() {
for(int x = 0 ; x < 100 ; x++){
System.out.println(x);
}
}
}
如何来获取线程的名称以及给线程设置名称:
public final String getName(): 返回该线程的名称。
public final void setName(String name)改变线程名称,使之与参数 name 相同。
public class ThreadDemo {
public static void main(String[] args) {
// 创建对象
// MyThread t1 = new MyThread() ;
// MyThread t2 = new MyThread() ;
// 使用构造方法给线程设置名称
MyThread t1 = new MyThread("刘亦菲") ;
MyThread t2 = new MyThread("公孙瓒") ;
// 设置线程名称
// t1.setName("刘亦菲") ;
// t2.setName("公孙瓒") ;
//
// // 启动线程
t1.start() ;
t2.start() ;
/**
* 获取主线程的名称: 如果我们可以获取当当前正在执行的线程,我们就可以获取到对应的名称
* 如何获取当当前正在执行的线程呢? 通过Thread类中的方法
* public static Thread currentThread()返回对当前正在执行的线程对象的引用。
*/
// Thread.currentThread().setName("主线程") ;
// System.out.println(Thread.currentThread().getName()) ; // main
}
}
package cn.itcast_test03;
public class MyThread extends Thread {
public MyThread(){}
public MyThread(String name){
super(name) ;
}
@Override
public void run() {
for(int x = 0 ; x < 200 ; x++){
System.out.println(getName() + "----" + x);
}
}
}
实现线程的第二种方式:
步骤:
a: 创建一个类,然后让这个类去实现Runnable接口
b: 复写run方法
c: 创建定义的类的对象
d: 创建Thread类的对象,然后把C中的对象作为参数传递进来
e: 启动线程
public class ThreadDemo {
public static void main(String[] args) {
// 创建MyThread的对象
MyThread my = new MyThread() ;
// 创建Thread类的对象,然后把C中的对象作为参数传递进来
// Thread t1 = new Thread(my) ;
// Thread t2 = new Thread(my) ;
/**
* 给线程设置名称
*/
Thread t1 = new Thread(my , "张三") ;
Thread t2 = new Thread(my , "李四") ;
//启动线程
t1.start() ;
t2.start() ;
}
}
package cn.itcast_test10;
public class MyThread implements Runnable {
@Override
public void run() {
for(int x = 0 ; x < 200 ; x++){
System.out.println(Thread.currentThread().getName() + "----" + x);
}
}
}
这两种方式: 第二种方式相对比较友好一点点.因为其解决了继承带来的局限性
线程的调度以及线程的优先级:
调度模型:
分时调度模型: 给每一个线程分配指定的时间,来完成线程的执行
抢占式调度模型: 优先执行优先级高的线程,如果多个线程的优先级相同,随机执行一个
而我们的java语言采用的就是抢占式调度模型
线程的优先级:
如何来获取线程的优先级:
public final int getPriority() 返回线程的优先级。
如何给线程设置优先级:
public final void setPriority(int newPriority):更改线程的优先级。
线程的优先级存在一个范围: 这个范围是1-10
如果我们没有给线程设置优先级那么使用的就是默认的优先级: 5
线程控制
线程休眠:
public static void sleep(long millis) throws InterruptedException: 参数表示的意思就是休眠的时间
线程加入:
public final void join(): 等待该线程终止
线程礼让:
public static void yield()暂停当前正在执行的线程对象,并执行其他线程。
yield这个暂停线程的时间太短了,这时候别的线程有可能没有抢占到CPU的执行权,这时候该线程醒了,那么可以继续在和别的线程在抢占CPU的执行权
线程守护:
public final void setDaemon(boolean on)将该线程标记为守护线程或用户线程。
线程中断:
public final void stop(): 终止线程
public void interrupt(): 中断线程(这个翻译不太好),查看API可得当线程调用wait(),sleep(long time)方法的时候处于阻塞状态,可以通过这个方法清除阻塞
我们线程中产生问题的标准:
a: 是否是多线程环境
b: 是否存在共享数据
c: 是否有多条语句操作共享数据
如何来解决这个问题呢?
我们可以让一个线程执行操作共享数据的代码,然后其他的线程处于等待状态.
如何让一个线程执行这段代码的同时,其他线程处于等待状态呢?
等待唤醒机制
需要使用同步代码块:
格式:
synchronized(对象){
需要被同步的代码 ;
}
同步代码块保证同步的作用的关键是这个对象,其实这个对象可以被看做成一把锁.有的书上把这个对象称之为监视器
这个同步代码块中的对象要保证多个线程使用的是一个
同步代码块的好处: 提高了数据的安全性
同步代码块的弊端: 每一个线程进来以后都需要判断同步锁,所有对应的效率比较低
public class ThreadTest {
<span style="white-space:pre"> </span>public static void main(String[] args) {
// 创建SellTickets对象
SellTickets st = new SellTickets() ;
// 创建Thread对象
Thread t1 = new Thread(st , "窗口1") ;
Thread t2 = new Thread(st , "窗口2") ;
Thread t3 = new Thread(st , "窗口3") ;
// 启动线程
t1.start() ;
t2.start() ;
t3.start() ;
}
package cn.itcast_test14;
public class SellTickets implements Runnable {
/**
* 定义票数
*/
<span style="white-space:pre"> </span>private static int tickets = 100 ;
<span style="white-space:pre"> </span>private static Object obj = new Object() ;
@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--) + "张票");
}
}
}
<span style="white-space:pre"> </span>}
}
同步代码块以及同步方法和静态同步方法的锁对象?
同步代码块的锁是, 是任意的对象
同步方法的锁对象到底是谁呢? 是this
静态同步方法的锁对象到底是谁呢? 是当前类的字节码文件对象
package cn.itcast_test15;
/*
* 同步代码块的锁是,是任意的对象
* 同步方法的锁对象到底是谁呢? 是this
* 静态同步方法的锁对象到底是谁呢? 是当前类的字节码文件对象
*/
public class ThreadTest {
<span style="white-space:pre"> </span>public static void main(String[] args) {
// 创建SellTickets对象
SellTickets st = new SellTickets() ;
// 创建Thread对象
Thread t1 = new Thread(st , "窗口1") ;
<span style="white-space:pre"> </span>Thread t2 = new Thread(st , "窗口2") ;
Thread t3 = new Thread(st , "窗口3") ;
// 启动线程
t1.start() ;
t2.start() ;
t3.start() ;
}
}
package cn.itcast_test15;
public class SellTickets implements Runnable {
/**
* 定义票数
*/
private static int tickets = 100 ;
private static Object obj = new Object() ;
<span style="white-space:pre"> </span>private int n = 0 ;
@Override
public void run() {
while(true){
if(n % 2 == 0){
synchronized(SellTickets.class){
if(tickets > 0){
try {
Thread.sleep(100) ;
} catch (InterruptedException e) {
<span style="white-space:pre"> </span> e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "出售第" + (tickets--) + "张票");
}
}
}else {
sellTicket() ;
}
n++ ;
}
}
// public void sellTicket() {
// synchronized(obj){
// if(tickets > 0){
// try {
// Thread.sleep(100) ;
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName() + "出售第" + (tickets--) + "张票");
// }
// }
// }
/**
* 同步方法
*/
// public synchronized void sellTicket() {
// if(tickets > 0){
// try {
// Thread.sleep(100) ;
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// <span style="white-space:pre"> </span>System.out.println(Thread.currentThread().getName() + "出售第" + (tickets--) + "张票");
// }
// }
public static synchronized void sellTicket() {
if(tickets > 0){
try {
Thread.sleep(100) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "出售第" + (tickets--) + "张票");
}
}
}
死锁
package cn.itcast_test02;
/**
* 死锁: 是多个线程在抢占CPU的资源的时候,出现了相互等待的状态就叫死锁
* 举例:
* 中国人和美国人吃饭使用的餐具
*
* 中国人使用的是筷子
* 美国人使用的是刀和叉子
*
* 中国人拿了美国人的刀
* 美国人拿了中国人的一个筷子
*
public class ThreadDemo {
public static void main(String[] args) {
// 创建线程对象
<span style="white-space:pre"> </span>MyThread t1 = new MyThread(true) ;
MyThread t2 = new MyThread(false) ;
// 启动线程
t1.start() ;
t2.start() ;
}
}
public class MyThread extends Thread {
private boolean flag ;
public MyThread(boolean flag){
this.flag = flag ;
}
@Override
public void run() {
if(flag){
synchronized(MyLock.objA){
System.out.println("true....objA......");
<span style="white-space:pre"> </span> synchronized(MyLock.objB){
System.out.println("true....objB.....");
}
}
}else {
synchronized(MyLock.objB){
System.out.println("false....objB......");
synchronized(MyLock.objA){
System.out.println("false....objA.....");
}
}
}
}
}
线程的五种状态
线程的生命周期: