java多线程示例记录

直接继承Thread类

【代码】

package thread;
public class MultiThread1 extends Thread{
public void run(){
for(int i=0; i<7; i++){
System.out.println("name:"+this.getName()+" i:"+ i+"   ");
}
}
public static void main(String[] args) {
MultiThread1 tA = new MultiThread1();
tA.setName("AA");
MultiThread1 tB = new MultiThread1();
tB.setName("BB");
tA.run();
tB.run();
}
}

【运行结果】

name:AA i:0   

name:AA i:1   

name:AA i:2   

name:AA i:3   

name:AA i:4   

name:AA i:5   

name:AA i:6   

name:BB i:0   

name:BB i:1   

name:BB i:2   

name:BB i:3   

name:BB i:4   

name:BB i:5   

name:BB i:6   

顺序执行的,说明调用方法不对,应该用start(),而不是run();

【代码】

package thread;
public class MultiThread1 extends Thread{
public void run(){
for(int i=0; i<7; i++){
System.out.println("name:"+this.getName()+" i:"+ i+"   ");
}
}
public static void main(String[] args) {
MultiThread1 tA = new MultiThread1();
tA.setName("AA");
MultiThread1 tB = new MultiThread1();
tB.setName("BB");
tA.start();
tB.start();
}
}

【运行结果】

name:BB i:0   

name:BB i:1   

name:AA i:0   

name:BB i:2   

name:AA i:1   

name:AA i:2   

name:AA i:3   

name:AA i:4   

name:AA i:5   

name:AA i:6   

name:BB i:3   

name:BB i:4   

name:BB i:5   

name:BB i:6   

其实,start()方法内部调用了run()方法的。
当启动一个线程 的时候,是需要系统资源的支持,表现在代码中就是说 要进行初始化多线程的环境。然后再调用run()方法进行运行。
当单单调用run()方法的时候,系统并没有初始化多线程的环境,还是在一个线程中运行的。

start()的源代码:

public synchronized void start() {
        /**
     * This method is not invoked for the main method thread or "system"
     * group threads created/set up by the VM. Any new functionality added
     * to this method in the future may have to also be added to the VM.
     *
     * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0 || this != me)
            throw new IllegalThreadStateException();
        group.add(this);
        start0();
        if (stopBeforeStart) {
        stop0(throwableFromStop);
    }
}
private native void start0();
start0()。并且这个这个方法用了native关键字,次关键字表示调用本地操作系统的函数。因为多线程的实现需要本地操作系统的支持。


实现Runnable接口

【代码】

package thread;
public class MyRunnable implements Runnable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0; i<10; i++){
System.out.println("name:"+this.getName()+" i:"+ i+"   ");
}
}
public static void main(String[] args) {
MyRunnable mA = new MyRunnable();
mA.setName("AA");
MyRunnable mB = new MyRunnable();
mB.setName("BB");
Thread t1 = new Thread(mA);
Thread t2 = new Thread(mB);
t1.start();
t2.start();
}
}

【运行结果】

name:AA i:0   

name:AA i:1   

name:AA i:2   

name:BB i:0   

name:AA i:3   

name:BB i:1   

name:AA i:4   

name:BB i:2   

name:AA i:5   

name:BB i:3   

name:AA i:6   

name:BB i:4   

name:AA i:7   

name:BB i:5   

name:BB i:6   

name:BB i:7   

name:BB i:8   

name:BB i:9   

name:AA i:8   

name:AA i:9   


Thread也是实现的Runnable接口

class Thread implements Runnable {
    //…
public void run() {
        if (target != null) {
             target.run();
        }
        }
}







ThreadRunnable的区别:

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。


继承Thread 不能共享资源

【代码】

package thread;
/**
 * 继承Thread类,不能资源共享
 * */
class Thread2 extends Thread {
private int count = 5;
    public void run() {
        for (int i = 0; i < 7; i++) {
            if (count > 0) {
                System.out.println("count= " + count--);
            }
        }
    }
    public static void main(String[] args) {
    Thread2 h1 = new Thread2();
    Thread2 h2 = new Thread2();
    Thread2 h3 = new Thread2();
        h1.start();
        h2.start();
        h3.start();
    }
 
    
}


【运行结果】

count= 5

count= 5

count= 5

count= 4

count= 4

count= 3

count= 4

count= 2

count= 3

count= 1

count= 3

count= 2

count= 2

count= 1

count= 1


实现Runnable可以共享资源

【代码】

package thread;
class Thread3 implements Runnable{
 
    private int ticket = 5;  //5张票
    public void  run() {
        for (int i=0; i<=20; i++) {
            if (this.ticket > 0) {
                System.out.println(Thread.currentThread().getName()+ "正在卖票"+this.ticket--);
            }
        }
    }
    public static void main(String[] args) {
    Thread3 my = new Thread3();
        new Thread(my, "1号窗口").start();
        new Thread(my, "2号窗口").start();
        new Thread(my, "3号窗口").start();
}
}

【运行结果】

1号窗口正在卖票5

3号窗口正在卖票3

2号窗口正在卖票4

3号窗口正在卖票1

1号窗口正在卖票2


总结一下吧:

实现Runnable接口比继承Thread类所具有的优势:

1):适合多个相同的程序代码的线程去处理同一个资源

2):可以避免java中的单继承的限制

3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。



提醒一下大家:main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。

 

java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实习在就是在操作系统中启动了一个进程。


isAlive判断线程是否启动

【代码】

MultiThread1 tA = new MultiThread1();
tA.setName("AA");
System.out.println("启动前 isAlive :"+ tA.isAlive());
tA.start();
System.out.println("启动后 isAlive :"+ tA.isAlive());

【运行结果】

启动前 isAlive :false

启动后 isAlive :true



线程的强制执行

【代码】

package thread;
public class MyRunnable implements Runnable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0; i<6; i++){
System.out.println("name:"+this.getName()+" i:"+ i+"   ");
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable mA = new MyRunnable();
mA.setName("AA");
Thread t1 = new Thread(mA);
t1.start();
for(int i = 1; i<50; i++){
System.out.println("main 线程: "+i);
if(i == 3){
t1.join();
}
}
}
}

【运行结果】

main 线程: 1

main 线程: 2

main 线程: 3

name:AA i:0   

name:AA i:1   

name:AA i:2   

name:AA i:3   

name:AA i:4   

name:AA i:5   

main 线程: 4

main 线程: 5

main 线程: 6

main 线程: 7

。。。




线程的休眠

【代码】

package thread;
public class MyRunnable implements Runnable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0; i<3; i++){
System.out.println("name:"+this.getName()+" i:"+ i+"   ");
if(i == 1){
try {
Thread.currentThread().sleep(2000);
System.out.println("睡眠2秒--------");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable mA = new MyRunnable();
mA.setName("AA");
Thread t1 = new Thread(mA);
t1.start();
}
}

【运行结果】

name:AA i:0   

name:AA i:1   

睡眠2秒--------

name:AA i:2  


睡眠2秒打印之前等待了2秒



线程中断

【代码】

package thread;
public class MyRunnable implements Runnable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("线程开始");
try {
Thread.currentThread().sleep(10000);
System.out.println("线程完成休眠");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("线程休眠被打断!");
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable mA = new MyRunnable();
mA.setName("AA");
Thread t1 = new Thread(mA);
t1.start();
Thread.currentThread().sleep(2000);
t1.interrupt();//2秒后终止线程
}
}

【运行结果】

线程开始

线程休眠被打断!

java.lang.InterruptedException: sleep interrupted

at java.lang.Thread.sleep(Native Method)

at thread.MyRunnable.run(MyRunnable.java:17)

at java.lang.Thread.run(Thread.java:745)



java程序中,只要前台有一个线程在运行,整个java程序进程不会消失,所以此时可以设置一个后台线程,这样即使java进程小时了,此后台线程依然能够继续运行。

【代码】

package thread;
public class MyRunnable implements Runnable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void run() {
while(true){
System.out.println(Thread.currentThread().getName() + "在运行");
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable mA = new MyRunnable();
mA.setName("AA");
Thread t1 = new Thread(mA);
t1.setName("AA");
t1.start();
}
}

【运行结果】

AA在运行

AA在运行

AA在运行

AA在运行

AA在运行

。。。

死循环,一直打印




线程的优先级

【代码】

package thread;
public class MyRunnable implements Runnable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
MyRunnable(String name){
this.name = name;
}
@Override
public void run() {
        for(int i=0;i<5;++i){
            System.out.println(Thread.currentThread().getName()+"运行"+i);
        }
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new MyRunnable("Thread1"));
t1.setName("AA");
Thread t2 = new Thread(new MyRunnable("Thread2"));
t2.setName("BB");
Thread t3 = new Thread(new MyRunnable("Thread3"));
t3.setName("CC");
t2.setPriority(1);
t2.setPriority(10);
t3.setPriority(5);
t1.start();
t2.start();
t3.start();
}
}


【运行结果】

BB运行0

BB运行1

BB运行2

BB运行3

BB运行4

AA运行0

AA运行1

AA运行2

CC运行0

CC运行1

CC运行2

CC运行3

CC运行4

AA运行3

AA运行4


不要误以为优先级越高就先执行。谁先执行还是取决于谁先去的CPU的资源、

 

另外,主线程的优先级是5.



线程的礼让  yield()

该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。

【代码】

package thread;
public class MyRunnable implements Runnable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
MyRunnable(String name){
this.name = name;
}
@Override
public void run() {
        for(int i=0;i<5;++i){
            System.out.println(Thread.currentThread().getName()+"运行"+i);
            if(i == 2 && Thread.currentThread().getName().equals("BB")){
            Thread.currentThread().yield();
            System.out.println("线程礼让");
            }
        }
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new MyRunnable("Thread1"));
t1.setName("AA");
Thread t2 = new Thread(new MyRunnable("Thread2"));
t2.setName("BB");
t2.setPriority(6);
t1.setPriority(6);
t1.start();
t2.start();
}
}

【运行结果】

BB运行0

BB运行1

BB运行2

AA运行0

线程礼让

BB运行3

AA运行1

AA运行2

AA运行3

AA运行4

BB运行4



同步和死锁

对于卖票系统,会有如下问题

【代码】

package thread;
public class MyRunnable implements Runnable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
MyRunnable(String name){
this.name = name;
}
private int count = 5;
@Override
public void run() {
        for(int i=1;i<10;i++){
        if(count > 0){
        try {
        Thread.sleep(500);
        } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        }
        System.out.println("剩余票数: "+ count--);
        }
        }
}
public static void main(String[] args) throws InterruptedException {
MyRunnable m = new MyRunnable("Thread1");
System.out.println(m.count);
Thread t1 = new Thread(m);
t1.setName("AA");
Thread t2 = new Thread(m);
t2.setName("BB");
Thread t3 = new Thread(m);
t3.setName("CC");
t1.start();
t2.start();
t3.start();
}
}

【运行结果】

5

剩余票数: 5

剩余票数: 5

剩余票数: 5

剩余票数: 4

剩余票数: 3

剩余票数: 2

剩余票数: 1

剩余票数: 0

剩余票数: -1

这里出现了-1,显然这个是错的。,应该票数不能为负值。

如果想解决这种问题,就需要使用同步。所谓同步就是在统一时间段中只有有一个线程运行,

其他的线程必须等到这个线程结束之后才能继续执行。

【使用线程同步解决问题】

采用同步的话,可以使用同步代码块和同步方法两种来完成。

 

【同步代码块】:

语法格式:

synchronized(同步对象){

 //需要同步的代码

}

但是一般都把当前对象this作为同步对象。

【代码】

package thread;
public class MyRunnable implements Runnable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
MyRunnable(String name){
this.name = name;
}
private int count = 5;
@Override
public void run() {
        for(int i=1;i<10;i++){
        synchronized (this) {
        if(count > 0){
        try {
        Thread.sleep(500);
        } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        }
        System.out.println("剩余票数: "+ count--);
        }
}
        }
}
public static void main(String[] args) throws InterruptedException {
MyRunnable m = new MyRunnable("Thread1");
System.out.println(m.count);
Thread t1 = new Thread(m);
t1.setName("AA");
Thread t2 = new Thread(m);
t2.setName("BB");
Thread t3 = new Thread(m);
t3.setName("CC");
t1.start();
t2.start();
t3.start();
}
}

【运行结果】

5

剩余票数: 5

剩余票数: 4

剩余票数: 3

剩余票数: 2

剩余票数: 1



语法格式为synchronized 方法返回类型方法名(参数列表){

    // 其他代码

}

【代码】

package thread;
public class MyRunnable implements Runnable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
MyRunnable(String name){
this.name = name;
}
private int count = 5;
@Override
public void run() {
        for(int i=1;i<10;i++){
        sale();
        }
}
public synchronized void sale(){
if(count > 0){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("剩余票数: "+ count--);
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable m = new MyRunnable("Thread1");
System.out.println(m.count);
Thread t1 = new Thread(m);
t1.setName("AA");
Thread t2 = new Thread(m);
t2.setName("BB");
Thread t3 = new Thread(m);
t3.setName("CC");
t1.start();
t2.start();
t3.start();
}
}


【运行结果】

5

剩余票数: 5

剩余票数: 4

剩余票数: 3

剩余票数: 2

剩余票数: 1




提醒一下,当多个线程共享一个资源的时候需要进行同步,但是过多的同步可能导致死锁。生产者和消费者问题。

此处列举经典的生产者和消费者问题。

【代码】

信息类

package lock;
public class Info {
private String name = "wangting";
private int age = 20;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}

生产者

package lock;
class Producer implements Runnable{
    private Info info=null;
    Producer(Info info){
        this.info=info;
    }
     
    public void run(){
        boolean flag=false;
        for(int i=0;i<25;++i){
            if(flag){
                this.info.setName("wangting");
                try{
                    Thread.sleep(100);
                }catch (Exception e) {
                    e.printStackTrace();
                }
                this.info.setAge(20);
                flag=false;
            }else{
                this.info.setName("齐天大圣");
                try{
                    Thread.sleep(100);
                }catch (Exception e) {
                    e.printStackTrace();
                }
                this.info.setAge(10000);
                flag=true;
            }
        }
    }
}


消费者

package lock;
class Consumer implements Runnable{
    private Info info=null;
    public Consumer(Info info){
        this.info=info;
    }
     
    public void run(){
        for(int i=0;i<25;++i){
            try{
                Thread.sleep(100);
            }catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(this.info.getName()+"<---->"+this.info.getAge());
        }
    }
}

测试代码

package lock;
public class Test {
    public static void main(String[] args) {
        Info info=new Info();
        Producer pro=new Producer(info);
        Consumer con=new Consumer(info);
        new Thread(pro).start();
        new Thread(con).start();
    }
}


【运行结果】

wangting<---->10000

wangting<---->20

wangting<---->10000

齐天大圣<---->20

wangting<---->10000

齐天大圣<---->20

wangting<---->10000

齐天大圣<---->20

wangting<---->10000

齐天大圣<---->20

wangting<---->10000

齐天大圣<---->20

wangting<---->10000

齐天大圣<---->20

wangting<---->10000

齐天大圣<---->20

wangting<---->10000

齐天大圣<---->20

wangting<---->10000

齐天大圣<---->20

wangting<---->10000

齐天大圣<---->20

wangting<---->10000

齐天大圣<---->20

齐天大圣<---->10000





大家可以从结果中看到,名字和年龄并没有对应上。

齐天大圣<---->20

齐天大圣<---->10000


两种解决方法

1)加入同步

2)加入等待和唤醒


1 加入同步

【代码】

信息类

package lock;
public class Info {
private String name = "wangting";
private int age = 20;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public synchronized void set(String name, int age){
        this.name=name;
        try{
            Thread.sleep(100);
        }catch (Exception e) {
            e.printStackTrace();
        }
        this.age=age;
    }
     
    public synchronized void get(){
        try{
            Thread.sleep(100);
        }catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(this.getName()+"<===>"+this.getAge());
    }
    
}


生产者

package lock;
class Producer implements Runnable{
    private Info info=null;
    Producer(Info info){
        this.info=info;
    }
     
    public void run(){
        boolean flag=false;
        for(int i=0;i<25;++i){
            if(flag){
                try{
                    Thread.sleep(100);
                }catch (Exception e) {
                    e.printStackTrace();
                }
                this.info.set("wangting",20);
                flag=false;
            }else{
                try{
                    Thread.sleep(100);
                }catch (Exception e) {
                    e.printStackTrace();
                }
                this.info.set("齐天大圣",10000);
                flag=true;
            }
        }
    }
}


消费者

package lock;
class Consumer implements Runnable{
    private Info info=null;
    public Consumer(Info info){
        this.info=info;
    }
     
    public void run(){
        for(int i=0;i<25;++i){
            try{
                Thread.sleep(100);
            }catch (Exception e) {
                e.printStackTrace();
            }
            this.info.get();
        }
    }
}


测试代码

package lock;
public class Test {
    public static void main(String[] args) {
        Info info=new Info();
        Producer pro=new Producer(info);
        Consumer con=new Consumer(info);
        new Thread(pro).start();
        new Thread(con).start();
    }
}


【运行结果】

齐天大圣<===>10000

齐天大圣<===>10000

wangting<===>20

wangting<===>20

齐天大圣<===>10000

wangting<===>20

齐天大圣<===>10000

wangting<===>20

齐天大圣<===>10000

wangting<===>20

齐天大圣<===>10000

wangting<===>20

齐天大圣<===>10000

wangting<===>20

齐天大圣<===>10000

wangting<===>20

齐天大圣<===>10000

wangting<===>20

齐天大圣<===>10000

wangting<===>20

齐天大圣<===>10000

wangting<===>20

齐天大圣<===>10000

齐天大圣<===>10000

wangting<===>20



以上, 对应错乱的问题解决了。

Object类帮忙解决重复问题

但是还是出现了重复读取的问题,也肯定有重复覆盖的问题。如果想解决这个问题,就需要使用Object类帮忙

,我们可以使用其中的等待和唤醒操作。

要完成上面的功能,我们只需要修改Info类,在其中加上标志位,并且通过判断标志位完成等待和唤醒的操作,代码如下:

【代码】

package lock;
public class Info {
private String name = "wangting";
private int age = 20;
private boolean flag = false;// 标志位
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public synchronized void set(String name, int age){
if(!flag){
try {
super.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
        this.name=name;
        try{
            Thread.sleep(100);
        }catch (Exception e) {
            e.printStackTrace();
        }
        this.age=age;
        flag=false;
        super.notify();
    }
     
    public synchronized void get(){
    if(flag){
    try {
super.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
    }
        try{
            Thread.sleep(100);
        }catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(this.getName()+"<===>"+this.getAge());
        flag=true;
        super.notify();
    }
    
}


【运行结果】

wangting<===>20

齐天大圣<===>10000

wangting<===>20

齐天大圣<===>10000

wangting<===>20

齐天大圣<===>10000

wangting<===>20

齐天大圣<===>10000

wangting<===>20

齐天大圣<===>10000

wangting<===>20

齐天大圣<===>10000

wangting<===>20

齐天大圣<===>10000

wangting<===>20

齐天大圣<===>10000

wangting<===>20

齐天大圣<===>10000

wangting<===>20

齐天大圣<===>10000

wangting<===>20

齐天大圣<===>10000

wangting<===>20

齐天大圣<===>10000

wangting<===>20



你可能感兴趣的:(java多线程示例记录)