进程
- (1)进程的本质就是程序
- (2)程序安装在硬盘上,此时不是进程
- (3)程序加载在内存中,且正在运行,此时是进程
线程介绍
- (1) 进程:是正在运行的应用程序
线程:是进程中的执行单元,每一个线程可以去执行一个任 务 - (2) 多线程程序同时可以执行多个任务
- (3)单线程程序同时只能执行一个任务
并发与并行
多线程程序的实现
- (1)多线程实现步骤
1.创建一个类,继承Thread
2.重写Thread类中的方法
3.在测试类中创建Thread的子类对象
4.调用start启动线程 - (2)当通过一个线程调用start方法后,会做两件事情
1.这个线程会启动
2.这个线程会自动执行自己的run方法
//1:创建Thread子类
class Thread1 extends Thread{
@Override
public void run() {
//2: 重写run方法,方法内的代码需要cpu时间执行
//存放代码
for (int i = 0; i < 100; i++) {
System.out.println("循环1 i = "+i);
}
}
}
class Thread2 extends Thread{
@Override
public void run() {
for (int j = 0; j < 100; j++) {
System.out.println("循环2 j = "+j);
}
}
}
public class Demo01 {
public static void main(String[] args) {
//3:创建线程对象
Thread1 t1 = new Thread1();
Thread2 t2 = new Thread2();
//4 必须使用start来启动线程
t1.start();//将run里面先放进新的线程中,再抢cpu时间,再执行
t2.start();
}
}
main线程介绍
- (1)main线程
主线程 - (2)什么时候产生main线程
当程序启动时,JVM会创建一个main线程,并执行程序中的main方法 - (3)在java中有一个类表示线程,这个类叫做Thread
public class Demo02 {
public static void main(String[] args) {
//JVM先找到main,将main方法内的代码放到mainThread去执行
for (int i = 0; i < 10; i++) {
System.out.println("循环:"+i);
}
}
}
getName与setName
- (1)构造方法
Thread(String name):线程传递一个名字 - (2)设值与取值方法
void setName(String name) 给线程设置名字
String getName();获取线程的名字
class Thread1 extends Thread{
public Thread1(String name) {
super(name);//super()调用父类的构造方法
}
@Override
public void run() {
//2: 重写run方法,方法内的代码需要cpu时间执行
//调用setName修改线程的名称
//super.setName("线程1号");
System.out.println(super.getName());//获取线程的名称
//存放代码
for (int i = 0; i < 100; i++) {
System.out.println("循环1 i = "+i);
}
}
}
获取当前线程
- (1)
static
Thread currentThread() 获取正在执行的线程对象 - (2)Thread t = Thread.currentThread();//返回一个Thread
如果是在主线程执行,则返回主线程
否则返回子线程
休眠
- (1)static void sleep(long mills) 线程休眠指定的毫秒
1 秒=1000毫秒 - (2)//休眠5秒
Thread.sleep(5*1000);
public class Demo03Clock {
public static void main(String[] args) throws InterruptedException {
SimpleDateFormat format = new SimpleDateFormat("yyyy-mm-dd hh:MM:ss");
while (true) { //等等1秒
Thread.sleep(1 * 1000);
//打印时间
System.out.println(format.format(new Date()));
}
}
}
Thread的第二种实现方式
- (1)创建线程的方式有两种
1.子类的方式:继承Thread 重写run
2.实现类的方式:实现Runnable 再把实现类对象给Thread对象
- (2)Runnable接口
实现类一般叫做线程任务类,因为里面定义的是线程要执行的任 - (3)
JVM只有看到Thread才知道是线程
- (4)实现步骤
1.定义类,然后实现Runnable接口
2.重写这个接口中的run方法
,在run方法中定义线程要执行的任务
3.在测试类中创建Runnable实现类对象
4.创建Thread对象
,并且在构造方法位置传递Runnable实现类对象
5.调用线程的start方法
,开启线程,线程会执行对应的run方法
//1 实现类的方式:Runnable
class Task1 implements Runnable{
@Override
public void run() {
//2:执行代码
for (int i = 0; i < 100; i++) {
System.out.println("循环1 i = "+i);
}
}
}
class Task2 implements Runnable{
@Override
public void run() {
for (int j = 0; j < 100; j++) {
System.out.println("循环2 j = "+j);
}
}
}
public class Demo04 {
public static void main(String[] args) {
//JVM只看Thread
Task1 task1 = new Task1();
Task2 task2 = new Task2();
//为后续线程池做准备
Thread t1 = new Thread(task1);//调用Thread构造方法 Thread(Runnable r)
Thread t2 = new Thread(task2);
t1.start();
t2.start();
}
}
第二种实现方式的好处***
- (1)第二种方式更好
1.实现接口的方式解决了java类中类与类之间
单继承的局限性
2.Runnable 接口中只有一个 run方法,没有start,setName,getName,这些方法,在Runnable接口中只需要关注线程要执行的任务,这样的话,功能更加的纯粹,更加符合设计模式中的单一职责原则
3.降低耦合性
4.第二种方式可以更加简便的实现多个线程之间的数据共享
,线程池
同步代码块解决线程安全问题
- (1)解决线程安全问题的关键是 多线程写共享数据时,让线程排队
- (2)可以使用同步关键字或者锁
- (3)分析上厕所
JDK1.5之后的Lock锁
- (1)在jdk1.5之后,提供了Lock接口,这个Lock接口表示的是锁,支持手动的获取锁以及手动的释放锁
Lock是一个接口
,如果要用需要使用它的实现类
我们可以使用实现类 ReentrantLock
- (2)
void lock()
:手动的获取锁
void unlock()
:手动的释放锁 - (3)同步方法和同步代码块的好处
1.同步方法:语法简洁
2.同步代码块:更加灵活
//加锁
lock.lock();
//如果票数> 0 ,退出
if (tikets > 0) {
tikets--;
System.out.println("还剩余" + tikets + "张");
}else{
break;
}
//释放锁
lock.unlock();