Java多线程
在学习多线程之前我们需要了解一些概念。
并发(Concurrent):指两个或多个事件在同一时间段内发生。(CPU每个小时间片执行一个线程,多个操作快速切换执行)。
并行(Parallel):指两个或多个事件在同一时刻发生。(两个线程互不抢占 CPU 资源,可以同时进行)
进程:指一个内存中运行的程序,每个进程都有一个独立的运行空间。
线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程至少有一个线程,可以有多个线程。
线程的调度:分为分时调度(轮流,平均分配CPU时间),抢占式调度(优先级高的线程使用CPU)。
(一)创建线程类
第一种创建线程的方式(Thread类):
Java使用Thread类来代表线程,所有的线程对象必须是Thread类或其子类的实例。每个线程的作用就是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码。通过继承Thread类来创建并启动多线程的步骤如下:
1.定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
2.创建Thread子类的实例,即创建了线程对象。
3.调用线程对象的start()方法来启动该线程。
public class MyThread extendsThread{
@Overridepublic voidrun() {for (int i = 0; i <20 ; i++) {
System.out.println("run:"+i);
}
}
}
public classDemo1 {public static voidmain(String[] args) {
MyThread mt= newMyThread();
mt.start();for (int i = 0; i <20 ; i++) {
System.out.println("main:"+i);//main主线程
}
}
}
*java属于抢占式调度,哪个优先级高执行哪个,同一优先级,随机选择一个执行。
上述代码多线程原理:
1.JVM首先执行main方法通向CPU的路径,这个路径叫做main线程,主线程。CPU用过这个线程,这个路径可以执行main方法。
2.new MyThread()方法在main线程内开辟一条通向CPU的新路径(即新线程),用来执行run()方法。
3.程序执行结果,即随机打印。
两个线程,一个main线程,一个新线程一起抢夺CPU的执行权(执行时间)谁抢到了谁就执行相应的代码。
多线程的内存图解:
start()方法会开辟新的栈空间,如果只是调用run()方法,只是在原来的空间中进行压栈操作而已。
关于Thread方法中的常用方法:
构造方法:
•public Thread():分配一个新的线程对象。
•public Thread(String name):分配一个指定名字的新的线程对象。
•public Thread(Runnable target):分配一个带有指定目标(任务)的新的线程对象。
•public Thread(Runnable target,String name):分配一个带有指定目标的新的线程对象。
常用方法:
•public String getNmae():获取当前线程名称。
•public void start():导致此线程开始执行;java虚拟机调用此线程的run方法。
•public void run():此线程要执行的任务在此处定义代码。
•public static void sleep(long mills):当前正在执行的线程以指定的毫秒数暂停。(暂时停止执行)
•public static Thread currentThread():返回当前正在执行的线程的引用。(Thread.currentThread.getNname()返回当前线程名称。)
设置线程名字的两种方式:
1.使用Thread中的方法setName(name)
void setName(String name)去改变线程的名称,使之与参数name相同。
2.创建一个带参数的构造方法,参数传递线程的名称;调用父类的带参构造函方法,把线程名称传递给父类,让父类给子线程起一个名字Thread(String name)分配新的Thread 对象。
public class MyThread extendsThread{publicMyThread(){
}publicMyThread(String name){super(name);
}
@Overridepublic voidrun() {
System.out.println(Thread.currentThread());
System.out.println(Thread.currentThread().getName());
}
}
public classDemo1 {public static voidmain(String[] args) {
MyThread thr= newMyThread();
thr.setName("哆啦A梦");//使用setName方法直接去设置线程的名字
thr.start();new MyThread("蜡笔小新").start();
System.out.println(Thread.currentThread().getName());//显示当前线程的名字
}
}
第二种创建线程的方式(Runnable):
使用Runnable接口来创建线程。方法如下:
1.定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法同样是该线程的执行体。
2.创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
3.调用对象的start()方法来启动线程。
public class RunnableImpl implementsRunnable {
@Overridepublic voidrun() {for (int i = 0; i <20 ; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
public classDemo {public static voidmain(String[] args) {
RunnableImpl imp= newRunnableImpl();
Thread t= new Thread(imp,"哆啦A梦");
t.start();for (int i = 0; i <20 ; i++) {try{
t.sleep(1000);//调用sleep方法,使线程暂停1秒。
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
实现Runnable接口比Thread类所具有的优势:
1.适合多个相同的程序代码去共享一个资源。
2.避免了单继承的局限性,可以去继承其他类,而不单单是Thread类
3.可扩展性高,降低了程序的耦合性(解耦)
解耦:就是创建Runnable接口实现类并完成调用这一过程,把设置线程任务和开启新线程实现了分离
实现类中,重写了Run方法:用来设置线程任务。
创建Thread类对象。调用start方法只需传递不同的实现类(run方法任务不同)的类的对象。
第三种创建线程的方式(匿名类):
匿名内部类的作用:简化代码
把子类继承父类,重写父类的方法,创建子类对象合成一步写完。
把实现类实现类接口,重写接口的方法,创建实现类对象合成一步完成。
格式:
new 父类/接口(){
重复父类/接口中的方法
}
public classDemo {public static voidmain(String[] args) {newThread() {//重写run方法
@Overridepublic voidrun() {for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}.start();
}
}
//接口匿名类,第一种方法
Runnable r = newRunnable() {
@Overridepublic voidrun() {for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+ "黑马");
}
}
};newThread(r).start();//接口匿名类,第二种,更简便
new Thread(newRunnable() {
@Overridepublic voidrun() {for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+ "你好");
}
}
}).start();
关于线程安全,有时间在更新吧~