进程:系统可以进行独立调配并且不可分割的独立单元
线程:进程的一个独立单元
进程有多个任务,每个任务就是一个线程
多线程程序:如果一个程序有多条执行路径,则该程序为多线程程序
多线程特点:线程之间抢占CPU执行权
多线程具有随机性
面试题:JVM是多线程程序吗?至少有几条线程..
jvm是多线程的,
至少有2条线程...
有主线程,main..执行这些代码,能够被Jvm识别
在执行一些程序的时候,一些对象Jvm释放掉,原因,
它开启了垃圾回收线程,里面GC:垃圾回收器(回收一些没有更多引用的对象或者变量...)
并行和并发(高并发:MyBatis --->IBatis:半自动化)
并行:逻辑上的同时,指的是同一个时间段内
并发:物理上的同时,指的是同一个时间点
Java提供了一个类:Thread类
实现多线程程序方式一的步骤:
1)将类声明为 Thread 的子类
2)该子类应重写 Thread 类的 run 方法
3)在主线程进行该自定义的线程类的对象的创建
Thread 类提供了一些方法
public final void setName(String name):给线程起名称
public final String getName() :获取线程名称
public final void setDaemon(boolean on) :true时,表示为守护线程
说明: 将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。(守护线程不会立即结束掉,它会执行一段时间在结束掉)该方法必须在启动线程前调用。
跟线程优先级相关的方法:
public final int getPriority()返回线程的优先级。
public final void setPriority(int newPriority)更改线程的优先级
线程存在一个默认优先级
public static final int MAX_PRIORITY 10 最大优先级
public static final int MIN_PRIORITY 1 最小优先级
public static final int NORM_PRIORITY 5 默认优先级
其他常用方法:
public final void join():等待该线程终止
public static void sleep(long millis):线程睡眠 指定是时间毫秒值
public final void stop() ;强迫线程停止执行。 不会执行了
public void interrupt()中断线程。 表示中断线程的一种状态
public static void yield()暂停当前正在执行的线程对象,并执行其他线程
Wait()和sleep()的区别:
wait(): wait()调用的,立即释放锁 (同步锁/Lock锁)
sleep():线程睡眠,调用不会释放锁
继承extends的方式创建多线程:
package org.westos_06;
public class MyThread extends Thread {
@Override
public void run() {
for(int x = 0 ; x <100 ; x ++) {
System.out.println(getName()+":"+x);
}
}
}
package org.westos_06;
public class ThreadDemo {
public static void main(String[] args) {
//创建三个子线程
MyThread t1 = new MyThread() ;
MyThread t2 = new MyThread() ;
MyThread t3 = new MyThread() ;
t1.start();
t2.start();
t3.start();
}
}
注意:
Run()方法封装被执行的代码,直接调用则是普通方法的调用
Start()方法启动线程,并由jvm自动调用run()方法
实现多线程程序的第二种方式:
1)自定义一个类,实现Runnable接口
2)实现接口中的run方法,对耗时的代码进行操作
3)然后在主线程中创建该了对象,将该类对象做为一个资源类,创建Threadd类的对象,将刚才的资源类作为参数进行传递
实现runnable接口的方法示例:
package org.westos_07;
//自定义类实现接口,实现run方法
public class MyThread implements Runnable {
@Override
public void run() {
for(int x= 0; x <100 ; x ++) {
// System.out.println(getName()+":"+x);
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
package org.westos_07;
public class ThreadDemo {
public static void main(String[] args) {
//创建当前类对象
MyThread my =new MyThread() ;
//实现多线程
//public Thread(Runnable target,String name)
Thread t1 = new Thread(my, "线程1") ;
Thread t2 = new Thread(my, "线程2") ;
//启动线程
t1.start();
t2.start();
}
}
校验一个多线程程序是否有安全问题的隐患的前提条件:
1)当前程序是否是多线程环境
2)是否有共享数据
3)是否有多条语句对共享数据进行操作
解决安全问题:
1)多线程环境 不能解决
2)对共享数据进行优化 不能解决
3)解决将多条语句对共享数据这一环进行解决
解决方案:就是将多条语句对共享数据操作的代码,用一个代码包起来---->代码--->同步代码块
同步机制:
格式:
synchronized(锁对象){
针对多条语句对共享数据操作代码;
}
注意:
锁对象:肯定一个对象,随便创建一个对象(匿名对象)
锁对象:多个线程必须使用同一把锁
同步的优点:解决多线程的安全问题
同步的弊端:当线程很多时,每个线程都会去判断同步上的锁,消耗资源,降低了运行效率。并且如果出现了同步嵌套,就会出现死锁问题
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构
可以使用Lock锁进行具体的锁定操作类 提供了具体的实现类:ReentrantLock
使用方式:
void lock()加锁
void unlock()释放锁
死锁问题:两个或两个以上的线程,在执行的过程中出现互相等待的情况
什么叫死锁问题?代码说明
package org.westos_08;
public class MyLock {
//两个锁对象分别是objA 和objB
public static final Object objA = new Object() ;
public static final Object objB = new Object() ;
}
package org.westos_08;
public class DieLock extends Thread {
//声明一个成语变量
private boolean flag ;
public DieLock(boolean flag) {
this.flag = flag ;
}
//重写run方法
@Override
public void run() {
if(flag) {
synchronized (MyLock.objA) {
System.out.println("if ObjA");
synchronized (MyLock.objB) {
System.out.println("if objB");
}
}
}else {
synchronized (MyLock.objB) {
System.out.println("else objB");
synchronized (MyLock.objA) {
System.out.println("else objA");
}
}
}
}
}
package org.westos_08;
public class DieLockDemo {
public static void main(String[] args) {
//创建线程了对象
DieLock dl1 = new DieLock(true) ;
DieLock dl2 = new DieLock(false) ;
//启动线程
dl1.start();
dl2.start();
}
}
解决死锁问题:
保证生产者线程和消费者线程针对同一个对象进行操作的!
具体操作:在外部创建一个资源类,将这个资源类对象对象通过构造方法传入到各个线程中
等待唤醒机制:
通过锁对象调用的方法(object类):
Wait():等待
Notify():唤醒单个线程
Notify():唤醒所有线程
生产者—消费者问题:
A. 生产者线程抢占CPU执行权,如果本身没有数据则生产数据,如果本身有数据则等待消费者线程消费
B. 消费者线程抢占CPU执行权,如果已有数据则消费数据,如果没有数据则等待生产者生产数据
代码说明:
package org.westos_11;
//生产者线程
public class SetThread implements Runnable {
private Student s ;
public SetThread(Student s) {
this.s = s ;
}
//定义一个变量
private int x = 0 ;
@Override
public void run() {
//设置学生数据
// Student s = new Student() ;
while(true) {
synchronized (s) {
//判断有没有数据的情况
if(s.flag) {
try {
s.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(x%2 ==0) {
s.name = "高圆圆" ; //高圆圆---27
s.age = 27 ;
}else {
s.name = "张杨";
//张杨
s.age = 28 ;
}
x++ ;
//如果有数据了,更改flag值
s.flag = true ;//有数据了
//通知t2线程消费数据,唤醒
s.notify(); //唤醒t2线程,唤醒之后t1,t2都互相抢占
}
}
}
}
package org.westos_11;
//消费者线程
public class GetThread implements Runnable {
private Student s ;
public GetThread(Student s) {
this.s = s ;
}
@Override
public void run() {
//输出该学生数据
// Student s = new Student() ;
while(true) {
synchronized (s) {
//如果本身消费者有数据
if(!s.flag) {
try {
s.wait();//和网络编程中TCP编程里面的accept() 都属于阻塞式方法
//消费线程等待,等待该线程先输出这些数据(立即释放锁对象)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(s.name +"----"+s.age);//高圆圆---27
//如果没有数据类,
s.flag = false ;
//通知t1线程,赶紧产生数据
s.notify(); //唤醒单个线程
}
//张杨---27
}
}
}
线程组:表示一个线程的集合。此外,线程组也可以包含其他线程组
public final ThreadGroup getThreadGroup()返回该线程所属的线程组
public final String getName():返回线程组的名称
所有的线程它默认的线程组名称:main(主线程)
线程池:多个线程执行完毕,它会重新回到线程池中,等待被利用,不会变成垃圾!
和线程池有关的类
类 Executors: 一种工厂类
方法:
和线程池的创建有关系
public static ExecutorService newFixedThreadPool(int nThreads)创建一个可重用固定线程数的线程池
ExecutorService:可以执行异步任务
创建一个线程池,执行接口中的方法
提交:Future<?> submit(Runnable task)
* <T> Future<T> submit(Callable<T> task)提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future
Future:接口
Future 表示异步计算的结果
线程池调用完毕可以关闭的
void shutdown():关闭之前,会提交刚才的任务
多线程实现方式第三种方式:
前提:自定义类实现Callable接口
1)创建线程池对象: Executors 里面的那个方法,返回的是ExecutorsService
2) 然后调用ExecutorsService里面的提交任务的方法:
<T> Future<T> submit(Callable<T> task)提交一个返回值的任务用于执行
3) 关闭线程池
代码举例:
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
//定义个变量
private int number ;
public MyCallable(int number) {
this.number = number;
}
@Override
public Integer call() throws Exception {
//定义最终结果变量
int sum = 0 ;
for(int x =1 ; x <=number ; x ++) {
sum +=x ;
}
return sum;
}
}
package org.westos_15;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
*分别计算每个线程的求和!
*
*/
public class ExecutorsTest {
public static void main(String[] args) throws InterruptedException,
ExecutionException {
//创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2) ;
//提交任务
Future<Integer> f1 = pool.submit(new MyCallable(100)) ;
Future<Integer> f2 = pool.submit(new MyCallable(200)) ;
//V get():获取结果
Integer i1 = f1.get() ;
Integer i2 = f2.get() ;
System.out.println(i1);
System.out.println(i2);
//关闭线程池
pool.shutdown();
}
}
线程的生命周期图解:
