首页 > 编程语言 > 详细

Java核心技术:【8】多线程

时间:2021-04-01 00:42:51      阅读:24      评论:0      收藏:0      [点我收藏+]

 

技术分享图片

 

 

一、实现多线程

1.1 通过继承Thread类实现

继承Thread类实现多线程的步骤

  1. 在Java中负责实现线程功能的类是java.lang.Thread 类。
  2. 可以通过创建 Thread 的实例来创建新的线程。
  3. 每个线程都是通过某个特定的 Thread 对象所对应的方法 run( ) 来完成其操作的,方法 run( ) 称为线程体。
  4. 通过调用 Thread 类的 start() 方法来启动一个线程。
package com.test.java;
/**
 * 测试通过继承Thread类实现多线程
 * @author 林
 *
 */
public class TestJava {
    public static void main(String[] args) {
        TestThread t1=new TestThread();
        t1.start();
        TestThread t2=new TestThread();
        t2.start();
    }
}

class TestThread extends Thread {
    public void run() {        //实现run方法
        for(int i=0;i<10;i++) {
            System.out.println(this.getName()+":"+i);    //getName()返回线程名称
        }
    }
}

输出结果:

Thread-1:0
Thread-1:1
Thread-1:2
Thread-1:3
Thread-0:0
Thread-1:4
Thread-1:5
Thread-1:6
Thread-0:1
Thread-1:7
Thread-1:8
Thread-1:9
Thread-0:2
Thread-0:3
Thread-0:4
Thread-0:5
Thread-0:6
Thread-0:7
Thread-0:8
Thread-0:9

 

此种方式的缺点:如自定义类已经继承了一个类(如小程序必须继承自Applet类),则无法再继承 Thread 类。

 

1.2 通过Runnable接口实现

自定义类在实现Runnable接口的同时还可以继承某个类。

在开发中,应用更多的是通过Runnable接口实现多线程。

package com.test.java;
/**
 * 测试通过Runnable接口实现多线程
 * @author 林
 *
 */
public class TestJava {
    public static void main(String[] args) {
        //创建线程对象,把实现了Runnable接口的对象作为参数传入
        Thread t1=new Thread(new TestThread());
        t1.start();
        Thread t2=new Thread(new TestThread());
        t2.start();
    }
}

class TestThread implements Runnable {
    public void run() {        //实现run方法
        for(int i=0;i<10;i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);    
        }
    }
}

输出结果:

Thread-0:0
Thread-1:0
Thread-1:1
Thread-1:2
Thread-0:1
Thread-0:2
Thread-1:3
Thread-1:4
Thread-1:5
Thread-0:3
Thread-1:6
Thread-0:4
Thread-1:7
Thread-0:5
Thread-0:6
Thread-0:7
Thread-0:8
Thread-0:9
Thread-1:8
Thread-1:9

 

 

二、线程状态

线程生命周期内,经历5个状态:

  • 新生状态(New)
  • 就绪状态(Runnable)
  • 运行状态(Running)
  • 阻塞状态(Blocked)
  • 死亡状态(Terminated)

 

2.1 终止线程

终止线程通常的做法是提供一个boolean型的终止变量,当这个变量置为false,则终止线程的运行。

package com.test.java;
/**
 * 测试终止线程
 * @author 林
 *
 */
public class TestJava {
    public static void main(String[] args) {
        TestThread tt1=new TestThread("线程A");
        Thread t1=new Thread(tt1);
        t1.start();
        for(int i=0;i<100;i++) {
            System.out.println("线程B:"+i);
        }
        tt1.terminate();    //调用自定义停止方法
        System.out.println("线程A停止");        
    }
}

class TestThread implements Runnable {
    String name;
    boolean live=true;    // 标记变量,表示线程是否可中止
    public TestThread(String name) {
        super();
        this.name=name;
    }
    public void run() {
        int i=0;
        while(live) {
            System.out.println(name+(i++));
        }
    }
    public void terminate() {    //自定义线程停止方法
        live=false;
    }
}

 

输出结果:

...

线程B:91
线程B:92
线程B:93
线程B:94
线程B:95
线程B:96
线程B:97
线程B:98
线程B:99
线程A18
线程A停止

 

2.2 暂停线程

暂停线程执行常用的方法有sleep()和yield()方法,这两个方法的区别是:

  • sleep()方法:可以让正在运行的线程进入阻塞状态,直到休眠时间满了,进入就绪状态。
  • yield()方法:可以让正在运行的线程直接进入就绪状态,让出CPU的使用权。
package com.test.java;
/**
 * 测试暂停线程sleep()方法
 * @author 林
 *
 */
public class TestJava {
    public static void main(String[] args) {
        TestThread t1=new TestThread();
        t1.start();
        TestThread t2=new TestThread();
        t2.start();
    }
}

class TestThread extends Thread {
    public void run() {
        for(int i=0;i<10;i++) {
            System.out.println(getName()+":"+i);
            try {
                Thread.sleep(2000);        //暂停线程2s
            }catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
package com.test.java;
/**
 * 测试暂停线程yield()方法
 * @author 林
 *
 */
public class TestJava {
    public static void main(String[] args) {
        TestThread t1=new TestThread();
        t1.start();
        TestThread t2=new TestThread();
        t2.start();
    }
}

class TestThread extends Thread {
    public void run() {
        for(int i=0;i<10;i++) {
            System.out.println(getName()+":"+i);
            Thread.yield();  //调用yield方法,让出CPU使用权
        }
    }
}

 

 

2.3 联合线程

 线程A在运行期间,可以调用线程B的join()方法,让线程B和线程A联合。这样,线程A就必须等待线程B执行完毕后,才能继续执行。

package com.test.java;
/**
 * 测试线程联合join()方法
 * @author 林
 *
 */
public class TestJava {
    public static void main(String[] args) {
        Thread a=new Thread(new AThread());
        a.start();
    }
}

class AThread implements Runnable {
    public void run() {
        System.out.println("A线程开始运行");
        System.out.println("A线程请求B线程");
        Thread b=new Thread(new BThread());
        b.start();
        try {
            b.join();    //调用join方法,必须等B线程执行完成,才执行A线程后面代码
        }catch(InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("A线程运行完成");
    }
}

class BThread implements Runnable {
    public void run() {
        System.out.println("B线程开始运行");
        try {
            System.out.println("B线程运行中");
            Thread.sleep(2000);
        }catch(InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("B线程运行完成");
    }
}

输出结果:

A线程开始运行
A线程请求B线程
B线程开始运行
B线程运行中
B线程运行完成
A线程运行完成

 

三、线程常用方法

Thread方法:

  • isAlive() :判断线程是否活动(即线程是否还未终止)。
  • getPriority() :获取线程的优先级。
  • setPriority() :设置线程的优先级。
  • getName() :获取线程的名字。
  • setName() :设置线程名字。
  • currentThread() :获取当前正在运行的线程对象。
package com.test.java;
/**
 * 测试Thread常用方法
 * @author 林
 *
 */
public class TestJava {
    public static void main(String[] args) throws InterruptedException {
        Runnable r=new MyThread();
        Thread t=new Thread(r,"Test");
        t.start();
        System.out.println(t.getName());    //获取线程名称
        Thread.currentThread().sleep(2000);    //线程暂停2s
        System.out.println(t.isAlive());    //判断线程还在运行
        System.out.println("Over!");
    }
}
 
class MyThread implements Runnable{
    public void run() {
        for(int i=0;i<10;i++) {
            System.out.println(i);
        }
    }
}

输出结果:

Test
0
1
2
3
4
5
6
7
8
9
false
Over!

 

四、线程优先级

  • 处于就绪状态的线程,会进入“就绪队列”等待JVM来挑选。
  • 线程的优先级用数字表示,范围从1到10,一个线程的缺省优先级是5。
  • 使用int getPriority() 或 void setPriority(int newPriority)方法获得或设置线程对象的优先级。

注意:优先级低只是意味着获得调度的概率低,并不是绝对先调用优先级高的线程后调用优先级低的线程。

package com.test.java;
/**
 * 测试线程优先级
 * @author 林
 *
 */
public class TestJava {
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(new MyThread(),"t1");    //设置线程名为t1
        Thread t2=new Thread(new MyThread(),"t2");
        t1.setPriority(1);        //设置线程优先级
        t2.setPriority(10);
        t1.start();
        t2.start();
    }
}
 
class MyThread extends Thread{
    public void run() {
        for(int i=0;i<100;i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

 

 输出结果:

...

t1:91
t2:96
t2:97
t1:92
t2:98
t1:93
t2:99
t1:94
t1:95
t1:96
t1:97
t1:98
t1:99

 

五、线程同步

线程同步:多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用。

Java通过synchronized 块解决线程冲突问题。

语法:

synchronized(syncObject)
   { 
   //允许访问控制的代码 
   }

 

package com.test.java;
/**
 * 测试没有线程同步机制异常
 * @author 林
 *
 */
public class TestThread {
    public static void main(String[] args) throws InterruptedException {
        Account a1=new Account("林",1000);
        Drawing d1=new Drawing(a1,800);
        Drawing d2=new Drawing(a1,700);
        d1.start();
        d2.start();
    }
}
 
 
class Account{
    String name;
    int money;
    public Account(String name,int money) {
        super();
        this.name=name;
        this.money=money;
    }
}
 
class Drawing extends Thread{
    Account account;    //账户
    int drawingNum;    //提取金额
    int sum;    //提取总金额
    public Drawing(Account account,int drawingNum) {
        super();
        this.account=account;
        this.drawingNum=drawingNum;
    }
    @Override
    public void run() {
        if((account.money-drawingNum)<0) {
            return;
        }
        try {
            Thread.sleep(2000);
        }catch(Exception e) {
            e.printStackTrace();
        }
        account.money-=drawingNum;
        sum+=drawingNum;
        System.out.println(this.getName()+"账户余额:"+account.money);
        System.out.println(this.getName()+"提取总金额:"+sum);
    }    
}

输出结果:

Thread-0账户余额:-500
Thread-0提取总金额:800
Thread-1账户余额:-500
Thread-1提取总金额:700

 

package com.test.java;
/**
 * 测试线程同步机制
 * @author 林
 *
 */
public class TestThread {
    public static void main(String[] args) throws InterruptedException {
        Account a1=new Account("林",1000);
        Drawing d1=new Drawing(a1,800);
        Drawing d2=new Drawing(a1,700);
        d1.start();
        d2.start();
    }
}
 
 
class Account{
    String name;
    int money;
    public Account(String name,int money) {
        super();
        this.name=name;
        this.money=money;
    }
}
 
class Drawing extends Thread{
    Account account;    //账户
    int drawingNum;    //提取金额
    int sum;    //提取总金额
    public Drawing(Account account,int drawingNum) {
        super();
        this.account=account;
        this.drawingNum=drawingNum;
    }
    @Override
    public void run() {
        draw();
    }        
        
    void draw() {
        synchronized(account) {        //synchronized块,控制到具体的“成员变量”
            if((account.money-drawingNum)<0) {
                System.out.println(this.getName()+"余额不足");
                return;
            }
            try {
                Thread.sleep(1000);        //判断完后阻塞。其他线程开始运行
            }catch(Exception e) {
                e.printStackTrace();
            }
            account.money-=drawingNum;
            sum+=drawingNum;
        }
        System.out.println(this.getName()+"账户余额:"+account.money);
        System.out.println(this.getName()+"提取总金额:"+sum);
    }    
}

输出结果:

Thread-0账户余额:200
Thread-1余额不足
Thread-0提取总金额:800

 

六、线程死锁

死锁:多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。

解决方法:同一个代码块,不要同时持有两个对象锁。

package com.test.java;
/**
 * 测试死锁
 * @author 林
 *
 */
public class TestThread {
    public static void main(String[] args) {
        C c1=new C();
        c1.flag=0;
        C c2=new C();
        c2.flag=1;
        c1.start();
        c2.start();
    }
}
 
class A{}
class B{}
class C extends Thread{
    int flag;
    static A a=new A();
    static B b=new B();
    @Override
    public void run() {
        test();
    }
    void test() {
        if(flag==0) {
            synchronized(a) {  //获取a的锁
                System.out.println("a1 running");
                try {
                    Thread.sleep(1000);
                }catch(Exception e) {
                    e.printStackTrace();
                }
                synchronized(b) {    //获取b的锁
                    System.out.println("b1 running");
                }
            }
        }else {
            synchronized(b) {  //获取b的锁
                System.out.println("b2 running");
                try {
                    Thread.sleep(1000);
                }catch(Exception e) {
                    e.printStackTrace();
                }
                synchronized(a) {    //获取a的锁
                    System.out.println("a2 running");
                }
            }
        }
    }
}

 

输出结果:

a1 running
b2 running

 

package com.test.java;
/**
 * 测试解决死锁
 * @author 林
 *
 */
public class TestThread {
    public static void main(String[] args) {
        C c1=new C();
        c1.flag=0;
        C c2=new C();
        c2.flag=1;
        c1.start();
        c2.start();
    }
}
 
class A{}
class B{}
class C extends Thread{
    int flag;
    static A a=new A();
    static B b=new B();
    @Override
    public void run() {
        test();
    }
    void test() {
        if(flag==0) {
            synchronized(a) {
                System.out.println("a1 running");
                try {
                    Thread.sleep(1000);
                }catch(Exception e) {
                    e.printStackTrace();
                }
            }
            synchronized(b) {
                System.out.println("b1 running");
            }
        }else {
            synchronized(b) {
                System.out.println("b2 running");
                try {
                    Thread.sleep(1000);
                }catch(Exception e) {
                    e.printStackTrace();
                }
            }
            synchronized(a) {
                System.out.println("a2 running");
            }
        }
    }
}

 

 输出结果:

b2 running
a1 running
a2 running
b1 running

 

 

七、线程并发协作

多线程并发协作模型“生产者/消费者模式”。

java.lang.Object类的方法

  • final void wait() :线程一直等待,直到其它线程通知。
  • void wait(long timeout) :线程等待指定毫秒参数的时间。
  • final void wait(long timeout,int nanos) :线程等待指定毫秒、微妙参数的时间。
  • final void notify() :唤醒一个处于等待状态的线程。
  • final void notiyAll() :唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程先执行。
package com.test.java;
/**
 * 测试生产/消费架构
 * @author 林
 *
 */
public class TestThread {
    public static void main(String[] args) {
        Buffer b1=new Buffer();
        A a=new A(b1);
        B b=new B(b1);
        a.start();
        b.start();
    }
}
 
class Message{    //消息类
    int id;
    Message(int id){
        this.id=id;
    }
}
 
class Buffer{    //缓冲区类
    int index=0;
    Message[] m1=new Message[10];
    public synchronized void push(Message m) {
        while(index==m1.length) {    //缓冲区类消息已满
            try {
                this.wait();    //线程暂停,进入阻塞状态,等待消费
            }catch(Exception e) {
                e.printStackTrace();
            }
        }
        this.notify();
        m1[index]=m;
        index++;
    }
    public synchronized Message pop() {
        while(index==0) {    //缓冲区类消息已空
            try {
                this.wait();    //线程暂停,进入阻塞状态,等待生产
            }catch(Exception e) {
                e.printStackTrace();
            }
        }
        this.notify();
        index--;
        return m1[index];
    }
}
 
class A extends Thread{        //生产类
    Buffer b1=null;
    public A(Buffer b1) {
        this.b1=b1;
    }
    @Override
    public void run() {
        for(int i=0;i<10;i++) {
            System.out.println("生产:"+i);
            Message m1=new Message(i);
            b1.push(m1);
        }
    }
}
 
class B extends Thread{        //消费类
    Buffer b1=null;
    public B(Buffer b1) {
        this.b1=b1;
    }
    @Override
    public void run() {
        for(int i=0;i<10;i++) {
            Message m1=b1.pop();
            System.out.println("消费:"+i);
        }
    }
}

 

 

 

八、任务定时调度

java.util.Timer:定时或者每隔一定时间触发一次线程。

java.util.TimerTask:是一个抽象类,该类实现了Runnable接口,所以该类具备多线程的能力。

package com.test.java;
 
import java.util.GregorianCalendar;
import java.util.Timer;
import java.util.TimerTask;
 
/**
 * 测试任务调度
 * @author 林
 *
 */
public class TestThread {
    public static void main(String[] args) {
        Timer t1=new Timer();
        MyTask m1=new MyTask();
        t1.schedule(m1, 3000);    //3秒后执行
        //t1.schedule(m1,5000,1000);        //5秒以后每隔1秒执行一次
        //GregorianCalendar g1=new GregorianCalendar(2021,1,1,12,00,00);  //指定时间
        //t1.schedule(m1,g1.getTime())
    }
}
 
class MyTask extends TimerTask{
    public void run() {
        for(int i=0;i<10;i++) {
            System.out.println("任务:"+i);
        }
    }
}

 

Java核心技术:【8】多线程

原文:https://www.cnblogs.com/linwenhai/p/14603832.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!