目录
思维导图
一、进程
进程:正在运行的程序
是系统惊醒资源分配和调用的独立单位
每个进程都有他自己的内存空间和系统资源
二、线程
线程概念:是进程中的单个顺序控制流,是一条执行路径
单线程:一个进程如果只有一条执行路径,成为单线程程序
多线程:一个进程如果有多条执行路径,成为多线程程序
1、多线程的实现方式一
1.1、实现步骤
(1)定义一个MyThread类继承自Thread类
(2)在MyThread类中重写run方法;
(3)创建MyThread类的对象
(4)启动线程
//定义一个MyThread类继承自Thread类
public class MyThread extends Thread {
@Override
//在MyThread类中重写run方法;
public void run() {
for (int i=0;i<100;i++){
System.out.println(i);
}
}
}
============================================================================
public class Demo2 {
public static void main(String[] args) {
//创建MyThread类的对象
MyThread my1=new MyThread();
MyThread my2=new MyThread();
//启动线程
// my1.run();//只调用run方法并不能实线多线程
// my2.run();
my1.start();
my2.start();
}
}
问题:
为什么要重写run()方法?
因为run()是用来封装被线程执行的代码
run()和start()的区别?
run():封装线程执行的代码,直接调用,相当于普通方法调用
start():启动线程;然后由Java虚拟机调用run()方法
1.2、 设置和获取线程名称
Thread类中设置和获取线程名称的方法
setName(String name):将此线程的名称为参数name
getName(String name):返回此线程名称,默认Thread-0、Thread-1.......
//定义一个MyThread类继承自Thread类
public class MyThread extends Thread {
public MyThread(){}
public MyThread(String name){
super(name);
}
@Override
//在MyThread类中重写run方法;
public void run() {
for (int i=0;i<100;i++){
//getName(String name):返回此线程名称
System.out.println(getName()+":"+i);
}
}
}
=============================================================================
public class Demo2 {
public static void main(String[] args) {
//方法一
//创建MyThread类的对象(无参构造)
MyThread my1=new MyThread();
MyThread my2=new MyThread();
//setName(String name):将此线程的名称为参数name
my1.setName("q");
my2.setName("w");
//方法二
//创建MyThread类的对象(有参构造)
MyThread my3=new MyThread("e");
MyThread my4=new MyThread("t");
//启动线程
my1.start();
my2.start();
//currentThread()返回当前正在执行的的线程对象的引用
System.out.println(Thread.currentThread().getName());
}
}
1.3、线程调度
线程的两种调度模式
分时调度模型:所有线程轮流使用CPU使用权,平均分配每个线程CPU的时间片
抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取CPU时间片相对多一些。
线程优先级默认值是5,范围是1-10
线程优先级高仅仅表示线程获取CPU时间片的几率高,但是要在此时较多或多次运行时才能显现效果。
Java使用的是抢占式调度模型
System.out.println(my1.getPriority());//获取线程优先级
my1.setPriority(10);//设置线程优先级
System.out.println(Thread.MIN_PRIORITY);//线程最低优先级
System.out.println(Thread.MAX_PRIORITY);//线程最高优先级
1.4、线程控制
static void sleep(long milis):使用当前正在执行的线程停留(暂停执行)指定的毫秒数
void join( ):其他线程必须等待这个线程死亡才能执行
void setDaemon(boolean on):将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出
//定义一个MyThread类继承自Thread类
public class MyThread extends Thread {
public MyThread(){}
public MyThread(String name){
super(name);
}
@Override
//在MyThread类中重写run方法;
public void run() {
for (int i=0;i<100;i++){
//getName(String name):返回此线程名称
System.out.println(getName()+":"+i);
try {
//使用当前正在执行的线程停留(暂停执行)指定的毫秒数
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
=============================================================================
public class Demo2 {
public static void main(String[] args) throws InterruptedException {
//创建MyThread类的对象(无参构造)
MyThread my1=new MyThread();
MyThread my2=new MyThread();
//setName(String name):将此线程的名称为参数name
my1.setName("q");
my2.setName("w");
//设置主线程
Thread.currentThread().setName("e");
//主线程功能
for (int i=0; i<10;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
//设置守护线程
my1.setDaemon(true);
my2.setDaemon(true);
// 启动线程
my1.start();
//其他线程需等待my1执行结束才能开始执行
my1.join();
my2.start();
}
}
1.5、线程生命周期
2、线程实现方法二
2.1、实现步骤:实现Runnable接口
(1)定义一个MyRunnable实现Runnable接口
(2)在MyRunnable类中重写run()方法
(3)创建Runnable类的对象
(4)创建Thread类的对象,把MyRunnable对象作为构造方法的参数
(5)启动线程
//定义一个MyRunnable类实现Runnable接口
public class MyRunnable implements Runnable{
@Override
//在MyThread类中重写run方法;
public void run() {
for (int i=0;i<100;i++){
//Thread.currentThread().getName(String name):返回此线程名称
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
==============================================================================
public class Demo2 {
public static void main(String[] args) throws InterruptedException {
//创建MyRunnable类的对象
MyRunnable my=new MyRunnable();
//创建Thread类的对象,把MyRunnable对象作为构造方法的参数(默认线程名)
Thread t1=new Thread();
Thread t2=new Thread();
//Thread(Runnable target,String name) :创建Thread对象时给线程命名
Thread t3=new Thread(my,"a");
Thread t4=new Thread(my,"b");
//启动线程
t1.start();
t2.start();
}
}
相比继承Thread类,实现Runnable接口的好处?
避免了Java单继承的局限性
适合多个相同程序的代码去处理同一个资源的情况,把线程和程序代码、数据有效分离,较好的体现了面向对象的设计思想
3、线程同步
3.1、同步代码块
格式:
synchronized(任意对象){
多条语句操作共享数据的代码
}
synchronized(任意对象):相当于给代码块加锁,任意对象可以看成是一把锁
同步的好处和弊端
好处:解决了多线程的数据安全问题
弊端:当线程很多时,因为每个线程都会去判断同步上的锁,会降低程序的运行效率
3.2、同步方法
同步方法:把synchronized关键字加到方法上
格式:
修饰符 synchronized 返回值类型 方法名(方法参数){}
public synchronized void methoed (){}
同步方法的锁是this
同步静态方法:把synchronized关键字加到静态方法上
格式:
修饰符 static synchronized 返回值类型 方法名(方法参数){}
public static synchronized void methoed (){}
同步静态方法的锁是类名.class的到类的字节码对象
4、线程安全的类
StringBuffer
线程安全,可变的字符序列
JDK5后被StringBuilder替代,通常应该使用StringBuilder类,因为他支持所有相同的操作,单他更快,因为他不执行同步
Vector
改进了List接口,会被同步,如果不需要线程安全的实现,建议使用ArrayList代替
Hashtable
该类实现了 一个哈希表,将键映射到值,任何非null对象都可以作为键或者值
实现map接口,会被同步,如果不需要线程安全的实现,建议使用HashMap代替
5、Lock锁
Lock锁提供比使用synchronized 方法和语句可以获得更广范的锁定操作
Lock中提供获得锁和释放锁的方法
void lock(): 获得锁
viod unlock(): 释放锁
Lock是接口不能被实例化,采用他的实现类ReentrantLock来实例化
ReentrantLock的构造方法
ReentrantLock():创建一个ReentrantLock的实例
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//定义一个MyRunnable类实现Runnable接口
public class MyRunnable implements Runnable{
private Lock lock= new ReentrantLock();
private int i;
@Override
//在MyThread类中重写run方法;
public void run() {
while (true){
try{
lock.lock();//获得锁
if (i>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}}finally {
lock.unlock();//释放锁
}
}}
}
三、生产者消费者模式
1、生产者消费者模式概述
该模式包含两类线程
(1)生产者线程用于生产数据
(2)消费者线程用于消费数据
为了解耦生产者与消费者的关系,通常会采用共享的数据区域
为了体现生产和消费过程中的等待和唤醒,Java提供了等待和唤醒方法
void wait():使当前线程等待,直到另一个线程调用该对象的notify()方法或者notifyAll()方法
void notify(): 唤醒正在等待对象监视器的单个线程
void notifyAll():唤醒正在等待对象监视器的所有线程
private int milk;
private boolean state=false;
public synchronized void put(int milk) throws InterruptedException {
//有则等待
if (state){
wait();
}
//无则生产
this.milk=milk;
//生产后改变状态
state =true;
//唤醒其他线程
notifyAll();
}