一、使用Thread创建线并启动线程java.lang.Thread类是线程类,其每一个实例表示一个可以并发运行的线程。我们可以通过继承该类并重写run方法来定义一个具体的线程。其中重写run方法的目的是定义该线程要执行的逻辑。启动线程时调用线程的start()方法而非直接调用run()方法。start()方法会将当前线程纳入线程调度,使当前线程可以开始并发运行。当线程获取时间片段后会自动开始执行run方法中的逻辑。
package com.zc;
//模拟售票窗口.100张票.3个窗口卖.有线程安全问题
public class T03Window {
public static void main(String[] args){
Window window1=new Window();
Window window2=new Window();
Window window3=new Window();
window1.setName("窗口1");
window2.setName("窗口2");
window3.setName("窗口3");
window1.start();
window2.start();
window3.start();
}
}
class Window extends Thread{
static int ticket=100;
public void run(){
while(true){
if(ticket>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"售票号为
:"+ticket--);
}else{
break;
}
}
}
}
其中Window类继承Thread类,重写Thread类中的run方法,里面是具体的业务逻辑。通过调用start()方法启动线程。
Thread类中的run()方法
private Runnable target;
//原本的run()方法啥事也没干,所以创建线程时,重写该方法
@Override
public void run() {
if (target != null) {
target.run();
}
}
如果target不为null,则运行target对象的run()方法
Thread类中可以传入Runnable对象的构造方法
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
该方法会调用init方法,给target赋值
private void init(){
....
this.target = target;
...
}
此时run方法就会执行target.run();
二、所以第二种创建线程的方式为使用Runnable创建并启动线程
实现Runnable接口并重写run方法来定义线程体,然后在创建线程的时候将Runnable的实例传入并启动线程。
这样做的好处在于可以将线程与线程要执行的任务分离开减少耦合,同时java是单继承的,定义一个类实现Runnable接口这样的做法可以更好的去实现其他父类或接口。因为接口是多继承关系。
package com.zc;
//实现Runnable接口.创建多线程
public class T04Runnable {
public static void main(String[] args){
MyRunnable myRunnable=new MyRunnable();
//要想启动一个多线程.必须调用start()方法
//Thread类的构造方法中.有一个Thread(Runnable target)
//new Thread(..)对象的start()方法启动的是myRunnable对象的run()方法.底层源
码.Runnable()接口只有run()方法
//Thread类implements Runnable
new Thread(myRunnable).start();
Thread t1=new Thread(myRunnable);
t1.start();
Thread t2=new Thread(myRunnable);
t2.start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
for(int i=1;i<=100;i++){
if(i%2==0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
三、创建线程就以上两种方式,继承Thread类或实现Runnable接口。当然使用匿名内部类创建线程会使得代码更加简洁。
package com.zc;
//匿名内部类创建线程
public class T02InnerThread {
public static void main(String[] args){
//继承于Thread类的匿名内部类对象
new Thread(){
@Override
public void run(){
for(int i=1;i<=100;i++){
if(i%2==0){
System.out.println(Thread.currentThread().getName()+":"+
i);
}
}
}
}.start();
new Thread(){
public void run(){
for(int i=1;i<=100;i++){
if(i%2!=0){
System.out.println(Thread.currentThread().getName()+":"+
i);
}
}
}
}.start();
}
}
四、最后若既重写了Thread类中的run方法的同时又实现了Runnable接口中的run方法,哪个run方法会执行。
package com.zc.zx;
//输出结果为:"Thread中的run方法"
//new Thread(){};创建了一个Thread的匿名内部类,
//是Thread的子类,其中的run方法相当于重写了父类中的run方法,所以理应执行外面的run()
//如果没有重写run(),会调用父类Thread中的run方法,此时target不为null,则会执行
runnable里的run()
public class T02WhichRun {
public static void main(String[] args) {
new Thread(new Runnable(){
@Override
public void run() {//Runnable中的run方法
System.out.println("Runnable中的run方法");
}
}){
@Override
public void run() {//Thread中的run方法
System.out.println("Thread中的run方法");
}
}.start();
}
}