目录
一.基础概念
程序: 是为完成特定的任务,用某种语言编写的一组指令的集合,即指一段静态的代码,静态对象
进程: 是程序的一次执行过程,运行中的程序。是一个动态的过程,有它自身的产生,存在,消亡的过程 ----生命周期
程序是静态的,进程是动态的
进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域
线程: 进程可进一步细化为线程,是一个程序内部的一条执行路径
若一个i进程同一时间并行执行多个线程,就是支持多线程的
线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
一个进程中的多个线程共享相同的内存单位/内存地址空间->他们从同一堆中分配的对象,可以访问相同的变量和对象。这就使得线程间通信更简便,高效。但多个线程操作共享的系统资源可能就会带来安全的隐患
单核cpu 和多核cpu
单核cpu:实际上是假的多线程,因为在一个时间单元内,也只能执行一个线程的任务
多核CPU: 才能更好的发挥多线程的效率
(Java应用程序Java.exe ,其实至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程,当然如果发生异常,会影响主线程。)
并行 :多个cpu同时执行多个任务。
并发:一个CPU同时执行多个任务。比如:秒杀,多个人做同一件事。
二.多线程创建方式
线程创建方式一:Thread
Thread类
1.创建一个继承于Thread类的子类
2.重写Thread类的run() -->将此线程执行的操作声明在run()中
3.创建Thread类的子类的对象
4.通过此对象调用start()
(启动当前线程,调用当前线程的run())
5.创建匿名子类
new Thread(){
@override
Public void run(){
}
}.start();
测试Thread的常用方法
1.start():启动当前线程;调用当前线程的方法run();
2.run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
3.currentThread():静态方法,返回当前执行代码的线程
4.getname():获取当前线程的名字
5.setname():设置当前线程的名字
6.yield():释放当前CPU执行权给高优先级线程
7.join():在线程a中调用线程b中的join(),此时线程a进入阻塞状态,直到线程完全执行完以后,线程a才结束阻塞状态
8.stop():已过时。当执行此方法,结束线程
9.sleep():静态方法,让当前线程睡眠指定时间毫秒。在指定的毫秒时间内,当前线程阻塞状态
10.isAlive():判断当前线程是否存活
线程的调度:
线程的优先级:
1.
MAX_PRIORITY: 10
MIN_PRIORITY: 1
NORM_PRIORITY: 5 -->默认优先级
2.如何获取和设置当前线程的优先级:
getPriority(): 获取当前线程的优先级
setPriority(int p):设置当前线程的优先级
说明:高优先级线程要抢占低优先级线程CPU的执行全,但是只是从概率上讲,不意味着有顺序
packagecom.example.thread;
class MyThread extends Thread{
@Override
public void run(){
for(int i=0;i<100;i++){
if(i%2==0){
System.out.println(i);
}
}
}
}
public class Thread Test{
public static void main(String[] args){
MyThread mathread=new MyThread();
mathread.start();
for(inti=0;i<100;i++)
System.out.println("hello");
}
}
案例,三个窗口卖票
//thread方式创建线程
packagecom.example.sailtext;
//存在线程安全,创建三个窗口卖票
public class WindowTest{
public static void main(String[] args){
Windowt1=new Window();
Windowt2=new Window();
Windowt3=new Window();
t1.setName("window-1");
t2.setName("window-2");
t3.setName("window-3");
t1.start();
t2.start();
t3.start();
}
}
class Window extends Thread{
private static int ticket=100;
@Override
public void run(){
while(true){
if(ticket>0){
System.out.println(getName()+":卖票,票号为:"+ticket);
ticket--;
}
else{
break;
}
}
}
}
线程创建方式二:Runnable
实现Runnable接口
1.创建一个实现Runnable接口的类
2.实现类去实现Runnable中的抽象方法 run();
3.创建实现类的对象
4.将此对象作为参数传递到Thread类中的构造器中,创建Thread类的对象
5.通过Thread类的对象调用 start();
package com.example.runnabletext;
class MThread implements Runnable{
@Override
public void run(){
for(int i=0;i<100;i++){
if(i%2==0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
public class ThreadTest{
public static void main(String[]args){
MThreadm1=new MThread();//MThread implements Runnable
Threadt1=new Thread(m1);//Thread(Runnabletarget)
t1.start();//此时调用的方法是根据thread里判断实现接口中的run方法
Threadt2=new Thread(m1);
t2.start();
}
}
案例,三个窗口卖票
package com.example.WindowTest1;
/*
三个窗口卖票
*/
public class WindowTest{
public static void main(String[]args){
Windowsw1=new Windows();
Threadt1=new Thread(w1);
Threadt2=new Thread(w1);
Threadt3=newT hread(w1);
t1.setName("window-1");
t2.setName("window-2");
t3.setName("window-3");
t1.start();
t2.start();
t3.start();
}
}
class Windows implements Runnable{
private int ticket=100;
@Override
public void run(){
while(true){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+":"+ticket);
}
else{
break;
}
ticket--;
}
}
}
线程创建方式三:Callable
Callable 比Runnable的强大之处
call方法可以有返回值
call方法可以抛出异常,被外面的操作捕获
Callable支持泛型
package com.example.callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//1.创建一个实现Callable接口的实现类
class NumThread implements Callable{
//2.实现call方法
@Override
public Object call() throws Exception{
int sum=0;
for(int i=1;i<100;i++){
if(i%2==0){
System.out.println(i);
sum+=i;
}
}
return sum;//自动装箱成Interger
}
}
public class ThreadNew{
public static void main(String[]args){
//3.创建Callable接口实现的对象
NumThread numThread=new NumThread();
//4.将此Callable接口实现类对象作为传递到FutureTask构造器中,创建FutureTask对象
FutureTask futureTask=new FutureTask(numThread);
//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()方法
Threadt1=newThread(futureTask);
t1.start();
try{
//6.获取callable中call的返回值
Objectsum=futureTask.get();//只是为了获取返回值
System.out.println(sum);
}catch(InterruptedExceptione){
e.printStackTrace();
}catch(ExecutionExceptione){
e.printStackTrace();
}
}
}
线程创建方式四:线程池
待完善
线程生命周期
线程同步
1.问题: 卖票过程中出现重票和错票---->线程安全问题
2.原因:当某个线程,操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票
3.解决:当一个线程在操作ticket中其他线程不能参与进来,直到线程a,操作完ticket 其他线程才开始操作ticket,这种情况即使线程出现阻塞,也不能改变
4.在Java中通过同步机制来解决线程安全的问题
方式一:同步代码块(synchornized)
synchronized(同步监视器){
需要被同步的代码(操作共享数据的代码,即为需要被同步的代码,共享数据:多个线程共同操作的变量。比如 :ticket ) 必须包含准确
}
同步监视器 俗称 锁。任何一个类的对象,都可以充当锁
要求:多个线程必须要共用同一把锁
补充: 继承Thread类多线程方式中,一般不可以用this 充当锁,可以考虑当前类
继承Runnable接口多线程方式中,一般使用this充当锁
packagecom.example.WindowTest1;
/*
三个窗口卖票 同步代码块解决runnable接口
*/
public class WindowTest{
public static void main(String[] args){
Windowsw1=new Windows();
Threadt1=new Thread(w1);
Threadt2=new Thread(w1);
Threadt3=new Thread(w1);
t1.setName("window-1");
t2.setName("window-2");
t3.setName("window-3");
t1.start();
t2.start();
t3.start();
}
}
class Windows implements Runnable{
private int ticket=100;
Object obj=new Object();
@Override
public void run(){
while(true){
synchronized(obj){ //或者this 表示当前对象
if(ticket>0){
System.out.println(Thread.currentThread().getName()+":"+ticket);
}else{
break;
}
ticket--;
}
}
}
}
package com.example.sailtext;
//存在线程安全,创建三个窗口卖票 同步代码块解决继承Thread 的多线程问题
public classWindowTest{
public static void main(String[]args){
Windowt1=new Window();
Windowt2=new Window();
Windowt3=new Window();
t1.setName("window-1");
t2.setName("window-2");
t3.setName("window-3");
t1.start();
t2.start();
t3.start();
}
}
class Window extends Thread{
private static int ticket=100;
Private static Object obj = new Object();
@Override
public void run(){
synchronized(obj){ //Window.class来代替
while(true){
if(ticket>0){
System.out.println(getName()+":卖票,票号为:"+ticket);
ticket--;
}
}
else{
break;
}
}
}
}
方式二:同步方法
1.runnable 接口多线程同步问题
将需要同步的代码块 包装成方法 比如show();
Private synchronized void show(){
//}
//锁是默认锁当前对象
2.Thread类继承多线程同步问题
Private static synchronized void show(){
//}
//锁是当前的类
方式三:Lock锁
private ReentrantLock lock = new ReentrantLock();
lock.lock();
lock.unlock();
面试题 synchronized Lock的异同
不同点:synchronized 机制在执行完相应的代码块后,自动的释放同步监视器
Lock手动启动 同步和手动结束同步
死锁的理解:
1.不同的线程分别占用分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
2.出现死锁的现象是所有线程出现阻塞
Sleep 与wait 的异同:
相同点:一旦执行,都可以使得当前线程进入阻塞状态
不同点:1.两个方法声明位置不同 Thread类中声明sleep ;Object 类中声明 wait
2.sleep可以在任何需要的场景,wait方法必须使用在同步代码块和方法中
3.若都用在同步代码块和方法中sleep方法不会释放锁,wait会释放锁。
wait()方法,一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器
notify() 方法,就会唤醒wait的一个现线程,若是多个就唤醒优先级高的
notify all()方法,就会唤醒所有被wait的线程
注意点
涉及到线程通信,此三个方法只能出现在同步代码块,或同步方法中