进程和线程
进程和线程是什么?二者的区别是什么?
实现线程的方法?
线程中常用的方法?
线程中有哪些状态?
线程调度问题?
线程安全问题?
Object类中的wait和notify方法?
生产者和消费者模式
进程和线程及区别
进程是一个应用程序或软件
线程是进程的一个执行单元。
一个进程可以有多个线程。一个线程对应一个栈,栈与栈之间相
互独立。
实现线程的方法
1.编写一个类,直接继承java.lang.Thread,重写run方法。
public class ThreadTest01 {
public static void main(String[] args) {
//这里的方法是main方法,属于主线程,在主栈中运行
MyThread myThread = new MyThread();
//启动线程
//start方法的作用是启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束
//这段代码是为了开辟一个新的占空间,只要新的栈空间开辟出来,start方法就结束啦,线程就启动成功了。
//run方法是在分支的栈底部,main方法在主栈的底部。run和main是平级的。
//注意:直接调用run方法不会启动线程,不会分配新的分支栈。
myThread.start();
//这里的代码还是在主栈中运行
for(int i = 0;i<1000;i++){
System.out.println("主线程---"+i);
}
}
}
class MyThread extends Thread{
public void run(){
//编写程序,这段程序运行在分支栈中
for(int i = 0; i < 1000; i++){
System.out.println("分支线程---"+i);
}
}
}
代码分析:创建线程对象然后调用线程对象的start方法才能开启
线程
start方法的作用:启动一个分支线程,在JVM中开辟一个新的栈
空间,这段代码任务完成之后,瞬间就结束。
注意:直接调用run方法不会启动线程,不会分配新的分支栈。
2.编写一个类,实现java.lang.Runnable接口,实现run方法。
public class ThreadTest02 {
public static void main(String[] args) {
//创建一个可运行的对象
MyRunnable myRunnable = new MyRunnable();
//将可运行的对象封装成一个线程对象
Thread thread = new Thread(myRunnable);
thread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("主线程"+i);
}
}
}
//这不是一个线程类,是一个可运行的接口
class MyRunnable implements Runnable{
@Override
public void run() {
for(int i = 0;i<1000;i++) {
System.out.println("分支线程" + i);
}
}
}
线程中常用的方法
1.currentThread()方法:用于获取当前线程对象,是一个静态方法
Thread currentthread = Thread.currentThread();
System.out.println(currentthread);
2.getName()方法:用于获取线程的名字
setName()方法:用于修改线程的名字
//MyThread7是一个线程类
MyThread7 myThread2 = new MyThread7();
myThread2.setName("tttt");
System.out.println( myThread2.getName());
myThread2.start();
注意:当线程没有设置名字时,默认的名字是Thread-0,Thread-
1等等
3.sleep()方法:是静态方法,参数是毫秒。作用是让当前线程进
入休眠,进入阻塞状态,放弃占有的CPU时间片,让给其他线程使
用
public static void main(String[] args) {
//让主线程进入休眠,睡眠5秒
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//5秒中后输出
System.out.println("hello world!");
}
线程中的5中状态
状态分为5中:新建状态,就绪状态,运行状态,阻塞状态,死亡
状态
新建状态:刚new出来的线程对象为新建对象
就绪状态:调用start方法后线程处于就绪状态。就绪状态又称为
可运行状态,表示当前线程具有抢夺CPU时间片的权利。当一个线
程抢夺到CPU时间片后,就开始执行run方法。
运行状态:调用run方法后线程处于运行状态。当之前占用的CPU
时间片用完之后,会重新回到就绪状态继续抢夺时间片,当再次
抢到CPU时间片后会重新进入run方法接着上次的代码继续执行。
注意:线程之间的运行是多个线程之间交替进行的。
死亡状态:当run执行结束后线程进入死亡状态。
阻塞状态:遇到阻塞事件后,就进入阻塞状态。比如sleep方法
或者接收用户键盘输入。阻塞状态的线程会放弃之前占用的CPU时
间片。需要再次回到就绪状态才能抢夺CPU时间片。
线程调度问题介绍
线程调度模型:
1.抢占式调度模型:哪个线程的优先级比较高,抢到CPU时
间片的概率就高一些。java采用的就是抢占式调度模型。
2.均分式调度模型:平均分配CPU时间片,每个线程占有的
CPU时间片的时间长度一样。
线程调度相关的方法:
setPriority(int newPriority):设置线程的优先级
getPriority( ):获取线程的优先级
补充:最低优先级是1,默认优先级是5,最高优先级是10;
优先级高的可能会获得更多的CPU时间片。
static void yield():暂停当前正执行的线程对象,并执
行其他线程。执行该方法后不会进入阻塞状态。而是使得
当前线程从运行状态回到就绪状态。
void join():合并线程,受阻后进入另一个线程,直到线
程结束才进行原先的进程
注意:sleep方法和join方法会进入阻塞状态。
线程安全问题介绍
在开发过程中,我们的项目都是运行在服务器当中的,而服务器
已经将线程的定义,线程的创建,线程的启动等都已经实现了,
这些代码我们都不需要编写。最重要的是编写的程序需要放到一
个多线程的环境下运行,因此需要关注的是这些数据在多线程并
发的环境下是否是安全的。
什么时候会存在安全问题?
1.多线程并发 2.有共享数据 3.共享的数据有修改行为。
比如我们日常的ATM取款。
线程编程模式:
1.异步编程模型:线程1和 线程2各自执行各自的,谁也不
需要等谁,这种模型称为异步编程模型
2.同步编程模型:线程1和线程2,在线程1执行的时候,必
须等待线程2线程执行结束,或者说,线程2执行的时
候,必须等待线程1执行结束。两个线程之间发生了等待
关系。效率较低,但是数据安全。
线程安全问题解决思路:线程排队执行,不能并发,称为线程同
步执行。在此过程中会牺牲一定的效率。
线程同步机制的语法:synchronized(共享对象){ 线程同步代码
块 },小括号内的数据是相当关键的,这个必须是多线程共享的数
据( )内写什么要看我们想让哪些线程同步。也就是共享对象。
Synchronized有两种写法:
1.在实例方法上使用Synchronized,此时共享对象一定是
this。并且同步代码块是整个方法体.一个对象一个锁。
2.在静态方法上使用Synchronized,表示找类锁,类锁永
远只有一把。
补充:synchronized会让程序的执行效率降低,用户体验不好,
系统用户吞吐量(并发量)降低,用户体验差。
因此我们尽量做到以下几点:
1.尽量使用局部变量代替实例变量和静态变量
2.如果必须是实例变量,那么可以考虑创建多个对象,
这样实例变量的内存就不共享了。
3.如果不能使用局部变量,对象也不能创建多个,这个
时候只能选择synchronized了,线程同步机制。
Object类中的wait和notify方法
第一:wait方法和notify方法不是线程的对象,是java中任何一
个java对象都有的方法,因为这两个方式是Object类中
自带的。wait方法和notify方法不是通过线程对象调用
的。
第二:wait方法的作用:Object obj = new Object( );
o.wait( );表示让正在o对象上活动的线程进入等待状
态,无限期的等待,直到被唤醒。
第三:notify方法是唤醒等待的线程。
生产者和消费者模式
public class ThreadTest06 {
public static void main(String[] args) {
//创建一个仓库对象,共享的
List list = new ArrayList();
//创建两个线程对象
Thread t1 = new Thread(new Product(list));
Thread t2 = new Thread(new Consumer(list));
t1.setName("生成线程");
t2.setName("消费线程");
t1.start();
t2.start();
}
}
//生成线程
class Product implements Runnable{
private List list;
public Product(List list){
this.list = list;
}
@Override
public void run() {
//一直生成(使用死循环模拟一直生产)
while(true){
synchronized (list){
if(list.size()> 0){
//当前线程进入等待状态
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//程序执行到这,说明list集合是空的,可以生成
Object obj = new Object();
list.add(obj);
System.out.println(Thread.currentThread().getName()+"----->"+obj);
//唤醒消费线程进行消费
list.notify();
}
}
}
}
//消费线程
class Consumer implements Runnable{
private List list;
public Consumer(List list){
this.list = list;
}
@Override
public void run() {
//一直消费
while (true){
synchronized (list){
if(list.size() == 0){
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//程序执行到这里,说明list集合内有元素,可以进行消费
Object obj = list.remove(0);
System.out.println(Thread.currentThread().getName()+"--->"+obj);
list.notify();
}
}
}
}