基本的概念
线程与进程直接的区别
首先,进程是系统资源分配的基本单位,一个进程对应一个程序。而线程是处理器任务调度和执行的基本单位。还存在资源开销、包含关系、内存分配、影响关系、执行过程等区别。
同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源相互独立。
而在java中线程之间方法区与堆内的数据是共享的,而栈中的资源是相互独立的。每一个单独的线程都会创建一个单独的栈空间。
也就是说假设启动十个线程,会有十个栈空间,每个栈与每个栈之间,互不干扰,各自执行各自的,这就是对线程并发
之所以会有多线程的机制,就是为了提高程序的执行效率。
但是一般在单核cpu的电脑上是不能真的进行多线程并发,只是进程之间切换的速度很快,如同早期的电影院使用的胶卷一般,人的感知中是连续不断的画面,但是实际中只是不停地切换画面而已。
线程的生命周期
创建->就绪->运行->阻塞->结束
注意:在java程序中使用了多线程机制后,main方法结束,意味着主线程结束,代表主栈空了,但是其他栈(线程)可能还在压栈弹栈。
线程的三种创建的方式
线程的创建方式有三种,但是我目前处于学生阶段第三种方法还是处于了解阶段
前两种方法都是通过继承Thread抽象类或实现Runable接口,然后通过在主方法中创建实例对象.start或.run的方式来创建线程。
主要区别就是通过继承Thread类的方式只能继承一个父类,相比于可以多个实现的接口来说,具有一定的局限性
第一种通过继承Thread抽像类以及重写run()方法创建线程
public class Thread_test02 {
public static void main(String[] args) {
mySecondThread me = new mySecondThread();
me.start();//测试支线程
myFirstThread me1 = new myFirstThread();
me1.run();
for (int i = 0; i < 200; i++) {
System.out.println("我在测试主线程");//测试主线程
}
}
}
class mySecondThread extends Thread{
public void run(){
for (int i = 0; i < 200; i++) {
System.out.println("我在测试不是主线程的线程");
}
}
}
另一种是通过实现Runable接口来实现线程的创建
package Thread;
//通过实现Runable接口创建线程
public class Thread_test01 {
public static void main(String[] args) {
myFirstThread me = new myFirstThread();
me.run();
}
}
class myFirstThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("这是我创建的第"+i+"个线程");
}
}
}
创建多个线程
package Thread;
//通过实现Runable接口创建多个线程
public class Thread_test03 {
public static void main(String[] args) {
Runable_test runable_test = new Runable_test();
new Thread(runable_test,"第一个线程").start();
new Thread(runable_test,"第二个线程").start();
new Thread(runable_test,"第三个线程").start();
//该创建方法为静态代理,只是传入对象然后实现Thread类
}
}
class Runable_test implements Runnable{
@Override
public void run() {
System.out.println("创建的线程");
}
}
拓展
在Runable接口中我们打开其源码,我们可以看到接口只有一个run()方法,我们可以通过匿名内部类或者lamdbm语句的形式来创建线程
package java.lang;
@FunctionalInterface
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
而通过Thread抽象类的部分源码我们可以看出,我们可以通过设定其中的priority在设置线程优先级,daemon来设置是否为守护线程,用过name来设置线程名字。还有就是该类的类对象是通过contextClassLoader来加载的。
private volatile String name;
private int priority;
private Thread threadQ;
private long eetop;
/* Whether or not to single_step this thread. */
private boolean single_step;
/* Whether or not the thread is a daemon thread. */
private boolean daemon = false;
/* JVM state */
private boolean stillborn = false;
/* What will be run. */
private Runnable target;
/* The group of this thread */
private ThreadGroup group;
/* The context ClassLoader for this thread */
private ClassLoader contextClassLoader;
线程的启动
线程的启动有两种方法一个是run()方法与start()方法,但是这两种方法有很大差别。
首先,run()方法不会启动的线程,不会创建一个新的线程。run方法是单线程的。
而start()方法是开辟一个新的栈内存(栈空间),然后该方法就结束了,并且该线程就启动成功了,之后线程自动调用run方法来运行该线程
(run方法在分支栈的栈底部,start方法在主栈的栈底部,run方法与start方法是平等的
线程的阻塞
线程的阻塞主要使用的是jion()方法该方法也成为插队方法
package Thread;
public class Thread_testjion implements Runnable{
@Override
public void run() {
for (int i = 0; i < 255; i++) {
System.out.println("线程中的贵族降临!");
}
}
public static void main(String[] args) throws InterruptedException {
Thread_testjion ts = new Thread_testjion();
Thread thread = new Thread(ts);
thread.start();
for (int i = 0; i < 299; i++) {
if(i==200){
thread.join();//在主线程运行至200是,支线程插队,而此时主线程就会被阻塞
}
System.out.println("我是主线程!");
}
}
}
线程的等待
线程的等待主要使用的是sleep()方法
package Thread;
//有此例子了解sleep方法,并且初步了解线程同步问题
public class Thread_test04 {
public static void main(String[] args) {
ticket t = new ticket();
new Thread(t,"它").start();
new Thread(t,"他").start();
new Thread(t,"她").start();
}
}
class ticket implements Runnable{
private int ticketNum = 10;
@Override
public void run() {
while (true){
if (ticketNum<=0){
break;
}
try {
Thread.sleep(1000);//睡眠等待1s,如果注释改行代码会使得全部票一下被全部人抢完
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了第"+ticketNum--+"票");
}
}
}
线程优先级
线程的优先级为1到10级。线程的优先级也高代表该线程进入内存并且获得cpu的计算资源的机会就越大,但是不一定优先处理,只是一个机会的概念,其次java中线程创建的默认优先级为5。我们也可以通过setPriorty()方法来设定优先级。
package Thread;
public class Thread_testPriority implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("现在是"+Thread.currentThread().getName()+"在跑");
}
}
public static void main(String[] args) {
Thread_testPriority myPriorty = new Thread_testPriority();
Thread t1 = new Thread(myPriorty);
Thread t2 = new Thread(myPriorty);
Thread t3 = new Thread(myPriorty);
Thread t4 = new Thread(myPriorty);
Thread t5 = new Thread(myPriorty);
t1.start();
t2.setPriority(5);
t2.start();
t3.setPriority(1);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY);
t4.start();
t5.setPriority(4);
t5.start();
for (int i = 0; i < 10; i++) {
System.out.println("主线程默认优先级为5");
}
}
}
线程的停止
我们在线程还没有执行完之前停止线程我们有很多方法,有destroy方法但是该方法已经被舍弃了,因为该方法会产生很多的问题。我们一般停止线程的方法是设立一个标志位,当标志位为真时该线程继续执行,该标志位为假时该线程停止。
package Thread;
//学会使用通过信号的方式停止线程
public class Thread_teststop {
}
class testStop implements Runnable{
@Override
public void run() {
int i = 0 ;
while (flag){
System.out.println(i++);
}
}
boolean flag = true;//标志位
public void stop() {//中止线程的方法
this.flag=false;
}
public static void main(String[] args) {
testStop ts = new testStop();
new Thread(ts).start();
for (int i = 0; i < 2000; i++) {
System.out.println("main"+i);
if (i==800){
ts.stop();
System.out.println("支线程停止了");
}
}
}
}
线程的礼让
sleep方法与yield方法的区别:sleep()方法会给其他线程运行的机会,不考虑其他线程的优先级,因此会给较低优先级线程一个运行的机会;yield()方法只会给相同优先级或者更高优先级的线程一个运行的机会。
当线程执行了sleep(long millis)方法,将转到阻塞状态,参数millis指定睡眠时间;当线程执行了yield()方法,将转到就绪状态。
线程的礼让是线程在运行态时将线程重新回归到就绪态,就是主动让出cpu的计算资源,但是线程的礼让不一定成功,因为线程在礼让后还是可以去抢占cpu资源的,所以礼让的结果不一定成功。
package Thread;
//yield方法不一定成功,由cpu调度
public class Thread_testyield implements Runnable {
@Override
public void run() {
for (int i = 0; i < 4; i++) {
System.out.println("这是" + Thread.currentThread().getName() + "在跑");
Thread.yield();//礼让不一定成功
}
}
public static void main(String[] args) {
Thread_testyield y = new Thread_testyield();
new Thread(y, "礼貌的男人").start();
new Thread(y, "礼貌的女人").start();
new Thread(y, "礼貌的小孩").start();
new Thread(y, "礼貌的老人").start();
}
}
线程的状态
线程的状态对应的是线程的五种不同的生命周期
package Thread;
public class Thread_teststate implements Runnable {
@Override
public void run() {
for (int i = 0; i < 28; i++) {
System.out.println("现在在跑支线程");
}
}
public static void main(String[] args) throws InterruptedException {
Thread_teststate tt = new Thread_teststate();
Thread thread = new Thread(tt);
System.out.println(thread.getState());
thread.start();
System.out.println(thread.getState());
while (thread.getState()!=Thread.State.TERMINATED){
thread.sleep(100);
System.out.println(thread.getState());
}
}
}