一:进程与线程的区别:
1.线程:程序中单独顺序的控制流,线程本身依靠程序进行运行,线程 是程序中的顺序控制流,只能使用分配给程序的资源和环境。
2.进程:执行中的程序一个程序可以包含一个或多个线程一个进程自少 要包含一个线程。
3.单线程:程序中只存在一个线程,实际上主方法就是一个主线程。
4.多线程:多线程是在一个程序(进程)中运行多个任务(线程),多线程的目的是更好的使用CPU资源。
5、线程运行的原理
- 分时调度
所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。
- 抢占式调度
优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
二、并行与并发
- 并行:多个CPU实例或者多台机器同时执行一段处理逻辑,是真正的同时运行。
- 并发:通过CPU调度算法,让用户看上去在同时执行,实际上从CPU操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用资源往往产生瓶颈,我们会用TPS(Transactions Per Second,每秒传输的事物处理个数)或者QPS(Query Per Second,每秒查询率)来反映这个系统的处理能力。
从下面的图例可以看出并行(Parallel)与并发(Concurrent)的区别。简单来说,并行就是多个线程同时访问多个对象,可以理解为多个用户同时使用多个服务资源。并发就是多个线程同时访问一个对象,可以理解为多个用户同时使用同一个服务资源。
三、线程的实现
<1>在Java中,线程的实现有2种方法:
- 继承Thread类
- 实现Runnable接口
1、继承Thread 类, Thread类是在Java.lang包中定义的,一个类只要继承了Thread类,此类就称为多线程实现类,继承Thread类必须重写run()方法。
定义的格式:
class 类名称 extends Thread{//继承Thread类
public void run(){//覆写Thread类中的run()方法,此方法是线程的主体
//线程主体
}
}
2、实现Runnable接口
定义格式:
class 类名称 implements Runnable{
属性……;
方法……;
public void run(){//覆写Runnable中的run()方法
//线程主体
}
}
<2>线程的启动方法
- 要正确的启动线程,是不能直接调用run()方法的,而应该调用从Thread类中继承而来的start()方法。
class MyThread extends Thread{
private String name;
public MyThread(String name){
this.name=name;
}
public void run(){
for(int i=0;i<10;i++){
System.out.println("name"+"运行,i="+i);
}
}
}
public class ThreadDemo{
public static void main(String args[]){
MyThread mt1=new MyThread("线程A");
MyThread mt2=new MyThread("线程B");
mt1.start();//启动多线程
mt2.start();//启动多线程
}
}
四、线程的状态(生命周期)
- 线程共有5种状态
- 新建(New)状态:当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值。
- 就绪(Runnable)状态:当线程对象调用了start()方法之后,该线程处于就绪状态。Java虚拟机会为其创建方法调用栈和程序计数器,等待cpu调度运行。
- 运行(Running)状态:如果处于就绪状态的线程获得了CPU资源,开始执行run()方法的线程执行体,则该线程处于运行状态。
- 阻塞(Blocked)状态:当处于运行状态的线程失去所占用资源之后,便进入阻塞状态。
- 死亡(Dead)状态:线程被销毁。
- 解释:yield()礼让
各种状态一目了然,值得一提的是"blocked"这个状态;线程在Running的过程中可能会遇到阻塞(Blocked)情况
- 调用join()和sleep()方法,sleep()时间结束或被打断,join()中断,IO完成都会回到Runnable状态,等待JVM的调度。
- 调用wait(),使该线程处于等待池(wait blocked pool),直到notify()/notifyAll(),线程被唤醒被放到锁定池(lock blocked pool ),释放同步锁使线程回到可运行状态(Runnable)。
- 对Running状态的线程加同步锁(Synchronized)使其进入(lock blocked pool ),同步锁被释放进入可运行状态(Runnable)。
- 此外,在runnable状态的线程是处于被调度的线程,此时的调度顺序是不一定的。Thread类中的yield方法可以让一个running状态的线程转入runnable。
五、线程的常用方法
1.取得线程名字
getName()
2.取得当前线程对象
currentThread()
3.判断线程是否启动
isAlive()
4.线程的强行运行
join()
5.线程的休眠
sleep()
6.中断线程,当一个线程运行时另一个线程可以直接通过interrupt()方法中断其运行。
interrupt()
六、线程的优先级别
1-MIN_PRIORITY
10-MAX_PRIORITY
5-NORM_PRIORITY
如果什么都不设置默认值是5
七、同步与死锁
- 同步,即两个或两个以上的线程需要共享对同一对数据的存取。
什么时候需要使用同步:在资源共享的时候需要
1.同步代码块
在代码块上加"synchronized"关键字,则此代码块就称为同步代码块。
2.同步代码块格式:
synchronized(同步对象){
需要同步的代码块;
}
3.同步方法:
除了代码块可以同步,方法也是可以同步的
4.方法同步格式:
synchronized void 方法名称(){}
5.死锁:所谓死锁: 是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
比如:大学生找工作要找高薪的而企业要有经验才给高薪,如果双方标准都不降低就无法继
一和二代码:
代码一run方法:
package Java线程;
public class MyThread extends Thread {
private String name;
//创建构造方法进行传参
public MyThread(String name){
this.name=name;
}
//重写Run方法
public void run(){
//为了能看到用for循环做打印
for(int i=0;i<1000;i++){
System.out.println(name+":"+i);
}
//super.run();
}
}
代码一run方法:
package Java线程;
//调用线程的主方法
public class ThreadDemo{
public static void main(String [] args){
//实例化线程A B启动线程
MyThread t1=new MyThread("线程A:");
MyThread t2=new MyThread("线程B:");
//线程的启动是通过start()方法启动的
t1.start();
t2.start();
}
}
代码二:Runnable接口
package 线程2;
//第二种方法通过实现Runnable接口来实现线程
public class MyRunnable implements Runnable{
private String name;
//构造方法传递参数标识当前线程
public MyRunnable(String name){
this.name=name;
}
//Runnable接口也要重写run方法
public void run() {
for(int i=0;i<1000;i++){
System.out.println(name+":"+i);
}
}
}
代码二:runnable接口
package 线程2;
public class ThreadDemo2 {
public static void main(String[] args) {
//实现MyRunnable
MyRunnable r1=new MyRunnable("线程A:");//A是一个name 标识当前的线程
MyRunnable r2=new MyRunnable("线程B:");
//runnable 接口不存在启动方法要通过线程启动
Thread t1=new Thread(r1);//r1 r2为传递进来的Runnable 对象
Thread t2=new Thread(r2);
t1.start();
t2.start();
}
}
代码三同步与死锁:
package Java线程;
//线程的同步与死锁
class MyThreadDemo implements Runnable{
private int ticket=5;
public void run() {
for(int i=0;i<10;i++){
//调用tell方法
tell();
//为了资源能够共享使用线程同步(第一种方法)
/*synchronized (this) {
if(ticket>0){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("车票:"+ticket--);
*/
}
}
//第二种使用线程同步方法来实现线程同步实现资源共享
public synchronized void tell(){
if(ticket>0){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("车票:"+ticket--);
}
}
}
public class ThreadDemo05 {
//主方法
public static void main(String[] args) {
MyThreadDemo m=new MyThreadDemo();
Thread t1=new Thread(m);
Thread t2=new Thread(m);
Thread t3=new Thread(m);
t1.start();
t2.start();
t3.start();
}
}