一、线程相关概念
1.程序:就是我们写的代码
2.进程:运行时候的程序,在程序运行的时候会吃掉一定CPU和内存
3.线程:线程是由进程创建的,是一个进程的实体,一个进程可以有多个线程
4.单线程:同一时刻,只有一个线程在运行
5.多线程:同一时刻,多个线程在运行
6.并发:同一时刻,多个任务交替执行,由于交替的时间比较快,所以可以看作是同时执行的
7.并行:同一时刻,多个任务同时进行
//获取自己电脑cpu的个数 Runtime runtime=Runtime.getRuntime(); int cpu=runtime.availableProcessors();
二、线程的基本使用
有两种方法:1.继承Thread,重写run()方法 2.实现Runnable接口,然后重写run()方法
1.第一种方式的代码
public class thread01 {
public static void main(String[] args) throws InterruptedException {
Cat cat=new Cat();
cat.start(); //线程开启没有这个的话线程无法执行
System.out.println("主线程的名字"+Thread.currentThread().getName());
for(int i=0;i<50;i++){
System.out.println("主线程"+i);
Thread.sleep(1000);
}
}
}
class Cat extends Thread{
int sum=0;
@Override
public void run(){
while(true){
System.out.println("我是小猫咪 "+"我的线程名字是:"+currentThread().getName());
sum++;
try {
Thread.sleep(1000); //每隔一秒
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if(sum==60){
break;
}
}
}
}
返回线程名字的代码:Thread.currentThread().getName(),主线程的名字就是main
当运行程序的时候,主线程和Cat的线程是交替执行的,当点击run的时候进程就开启了一个主线程,然后主线程开了一个Cat线程,主线程结束但是子线程没有结束会导致整个进行还会在运行
执行jconsole是用来监视和管理控制台的
输入jconsole来看主线程和Thread-0线程的运行情况,发现在主线程执行完毕之后Thread-0还在运行
当Thread-0执行完毕时,该进程结束链接断开
那么问题来了!!为什么cat要调用的是start()方法而不是run()方法呢,在程序中我们来调用一下run方法看一下输出发现输出都是run()方法中的我是小猫咪,线程名字也改变了,并且没有输出“主线程”这几个字
答案:因为直接调用run()方法就相当于是一个普通的方法,没有真正的线程启动,等调用的方法执行完毕之后才是向下继续执行,这个就是串行化了,不是多线程了
run方法什么时候执行取决于cpu,jvm来调用start0(),start0()是真正实现多线程的效果的,start0()是本地方法栈
2.第二种方式的代码
实现Runnable接口
由来:因为Java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时再用继承Thread类方法来创建线程显然不可能了,所以就通过实现Runnable接口来创建线程
代码:
//通过实现接口来开发线程
public class thread02 {
public static void main(String[] args) {
Dog dog = new Dog();
//这里的dog没有start,但是不能调用run方法,run方法就是一个普通的方法
//创建了Thread对象,把dog对象,放入到Thread
Thread thread=new Thread(dog);
thread.start();
}
}
class Dog implements Runnable{
int sum=0;
@Override
public void run() {//就是一个普通的方法
while(true){
System.out.println("小狗汪汪叫。。。"+(++sum)+" "+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if(sum==10){
break;
}
}
}
}
那么为什么把dog对象放到Thread里面就可以实现线程创建和线程开启了呢?
答:这里使用了设计模式【静态代理】,下面用代码来模拟一下,伪代码模拟,主要是懂得里面的思想
首先是tiger对象实现了Runnable的方法,然后创建类ThreadProxy 将tiger对象放到ThreadProxy里面,因为在我们自己模拟的这个ThreadProxy里面有一个构造方法,将穿的tiger值赋给了里面的tagert,然后判断target,target不等于空就往下调用就可以调用到start方法了,
这个就是线程的代码
//通过实现接口来开发线程
public class thread02 {
public static void main(String[] args) {
Dog dog = new Dog();
//这里的dog没有start,但是不能调用run方法,run方法就是一个普通的方法
//创建了Thread对象,把dog对象,放入到Thread
// Thread thread=new Thread(dog);
// thread.start();
//这里的ThreadProxy是自己创建的,来模拟这个过程
ThreadProxy proxy=new ThreadProxy(dog);
proxy.start();
}
}
//模拟了一个Thread
class ThreadProxy implements Runnable{//把proxy看作thread类来看作
//比如就是外卖,自己懒得拿,叫个外卖
private Runnable target=null;
@Override
public void run() {
if(target!=null){
target.run();
//这里的dog传进来之后呢,运行的这个方法就是绑定的dog类型,运行的是下面dog类中的run方法(妙哉妙哉)
//这里就是涉及到代理,在上面我不是直接调用run方法执行,而是通过thread线程里面的代码来调用run方法
//所以说在继承了Runnable必须要实现run这个方法,因为里面的target要调用
}
}
public ThreadProxy(Runnable target){
this.target=target;
}
public void start(){
start0();//真正实现多线程的方法
}
public void start0(){
run();
}
}
class Dog implements Runnable{
int sum=0;
@Override
public void run() {//就是一个普通的方法
while(true){
System.out.println("小狗汪汪叫。。。"+(++sum)+" "+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if(sum==10){
break;
}
}
}
}
3.多线程执行
这里用实现Runnable接口的方式来写代码,两个线程交替执行
package com.heaboy.thread;
public class thread03 {
public static void main(String[] args) {
T1 t1 = new T1();
T2 t2 = new T2();
Thread thread = new Thread(t1);
Thread thread1 = new Thread(t2);
thread.start();
thread1.start();
}
}
class T1 implements Runnable{
int sum=0;
@Override
public void run() {
//每个一秒输出helloworld,输出10次
while(true){
System.out.println("hello world1"+" "+(++sum));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if(sum==10){
break;
}
}
}
}
class T2 implements Runnable{
int sum=0;
@Override
public void run() {
//每个一秒输出helloworld,输出10次
while(true){
System.out.println("hello world2"+" "+(++sum));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if(sum==10){
break;
}
}
}
}
那么这个要如何理解?
可以想象是孙悟空用猴毛变小猴子,通过这样多线程的方式,让程序运行的效率更高