1:继承Thread的方式
继承Thread
实现runnable接口
实现runnable接口更容易达到多继承的特性
线程的状态图
2:在多线程技术中,代码是顺序执行的吗? 不是
public class MyThread extends Thread {
@Override
public void run() {
super.run();
System.out.println("MyThread");
}
}
public class Run {
public static void main(String[] args) {
MyThread mythread = new MyThread();
mythread.start();
System.out.println("运行结束!");
}
}
程序输出:
运行结束!
MyThread
解释:在我们的Run类里面,其有一个主线程,当调用子线程的start方法的时候,即通知“线程规划器”此线程已经准备就绪,等待调用线程的run方法,这个过程其实就是让系统安排一个时间来调用Thread的run方法,也就是启动线程,使其具有异步执行效果。这里就带出一个线程的特性:线程具有随机特性。
问题一:这里会先打印MyThead,再打印 运行结束!吗? 不会
解释:Run类是主线程,其是向下执行的,子线程MyThread具有随机性,需要系统调用。举个例子
public class Run {
public static void main(String[] args) {
for(int i = 0 ; i < 5000 ; i++){
System.out.println("i=" + i);
}
System.out.println("上面的循环执行完了");
for(int j = 0 ; j < 5000 ; j++){
System.out.println("j=" + j);
}
System.out.println("执行完毕");
}
}
这里就需要等待第一个循环做完,才会打印“上面的循环执行完了”,然后等待第二个线程执行完,才会打印“执行完毕”
问题二:调用线程的start方法就是线程的执行顺序吗? 不是
解释:调用start方法只是让线程进入就绪状态,还需要系统分配时间片给线程让其进入运行状态,因此调用start方法的顺序不是线程的执行顺序
问题三:多次调用线程的start方法是否可以? 不可以
会抛出异常“IllegalThreadStateException”
3:实例变量与线程安全
自定义线程类中的实例变量针对其他线程可以有共享与不共享之分。
例如:
public class MyThread extends Thread {
private int count=5;
@Override
synchronized public void run() {
super.run();
while(count > 0){
count--;
System.out.println("由 "+this.currentThread().getName()+" 计算,count="+count);
}
}
}
实现非共享的方式:
MyThread mythreadA = new MyThread("A");
MyThread mythreadB = new MyThread("B");
MyThread mythreadB = new MyThread("C");
mythreadA.start();
mythreadB.start();
mythreadC.start();
此时每个线程都有各自的count变量,自己减少自己的count变量的值
实现共享的方式:
MyThread mythreadA = new MyThread();
Thread a = new Thread(mythreadA,"A");
Thread b = new Thread(mythreadB,"B");
Thread b = new Thread(mythreadC,"C");
a.start();
b.start();
c.start();
此时每一个线程共享一个count。但是这样的方式就产生了“非线程安全”的问题。
非线程安全是指:多个线程对同一个对象的同一个实例变量进行操作时,会出现值被更改,值不同步的问题。
解决“非线程安全”问题的方法之一:添加synchronize关键字
原因:多线程访问一个带有synchronize关键字的方法的时候,以排队的方式进行处理,当一个线程调用synchronize方法时,会尝试去拿这把锁,如果能够拿到,则可以执行,否则这个线程就会不断尝试拿这把锁,直到能够拿到为止。而且是多个线程同时去抢这把锁。
1):synchronize方法
也就是同步方法,它锁定的是调用这个同步方法对象。也就是说,当一个对象P1在不同的线程中执行这个同步方法时,它们之间会形成互斥,达到同步的效果。
当考虑阻塞时,一定要注意哪个对象正被用于锁定:1、调用同一个对象中非静态同步方法的线程将彼此阻塞。如果是不同对象,则每个线程有自己的对象的锁,线程间彼此互不干预。 2、调用同一个类中的静态同步方法的线程将彼此阻塞,它们都是锁定在相同的Class对象上。 3、静态同步方法和非静态同步方法将永远不会彼此阻塞,因为静态方法锁定在Class对象上,非静态方法锁定在该类的对象上。 4、对于同步代码块,要看清楚什么对象已经用于锁定(synchronized后面括号的内容)。在同一个对象上进行同步的线程将彼此阻塞,在不同对象上锁定的线程将永远不会彼此阻塞。
特例:println方法和i–表达式
public class MyThread extends Thread {
private int i = 5;
@Override
public void run() {
System.out.println("i=" + (i--) + " threadName="
+ Thread.currentThread().getName());
}
}
println()方法内部是同步的,即其内部实现了synchronize效果。但是上面的方法依旧会代码“非线程安全”问题,因为i–的操作是在进入println()方法前发生,因此有发生非线程安全的问题的概率。
4: 实例化Thread分析,线程的名字,Thread=0的来历
public class MyThread extends Thread {
@Override
public void run() {
}
}
MyThread myT = new MyThread ();
Thread t1 = new Thread(myT);
在这个过程中会调用Thread的构造函数
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
因此就产生了Thread-0,Thread-1…等
5: Thread.currentThread().getName() 和 this.getName()的分析
public class CountOperate extends Thread {
public CountOperate() {
System.out.println("CountOperate---begin");
System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName());
System.out.println("Thread.currentThread().isAlive()=" + Thread.currentThread().isAlive());
System.out.println("this.getName()=" + this.getName());
System.out.println("this.isAlive()=" + this.isAlive());
System.out.println("CountOperate---end");
}
@Override
public void run() {
System.out.println("run---begin");
System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName());
System.out.println("Thread.currentThread().isAlive()=" + Thread.currentThread().isAlive());
System.out.println("this.getName()=" + this.getName());
System.out.println("this.isAlive()=" + this.isAlive());
System.out.println("run---end");
}
}
输出为:
CountOperate---begin
Thread.currentThread().getName()=main
Thread.currentThread().isAlive()=true
this.getName()=Thread-0
this.isAlive()=false
CountOperate---end
run---begin
Thread.currentThread().getName()=A
Thread.currentThread().isAlive()=true
this.getName()=Thread-0
this.isAlive()=false
run---end
分析如下:Thread.currentThread()表示执行该处代码的线程的引用,而this表示当前类的引用。
因此在run方法中的this.getName()表示执行CountOperate类的getName方法,但是CountOperate没有getName方法,则其是执行Thread的getName方法。