栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java多线程(续)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java多线程(续)

一、和线程调度有关的方法 

1.void setPriority(int newPriority) 设置线程的优先级

2.int getPriority() 获取线程优先级

package com.java.javase.Thread;
public class ThreadTest08 {
    public static void main(String[] args) {
        Thread currentThread=Thread.currentThread();
        System.out.println(currentThread.getName()+"线程的默认优先级是:"+currentThread.getPriority());
        Thread t=new Thread(new MyThread8());
        t.setName("t");
        t.start();
    }
}
class MyThread8 implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程的默认优先级是:"+Thread.currentThread().getPriority());
    }
}

输出结果:

main线程的默认优先级是:5
t线程的默认优先级是:5

3.Thread.yield() 当前线程暂停一下 

package com.java.javase.Thread;
public class ThreadTest {
    public static void main(String[] args) {
        
        Thread t=new Thread(new MyRunable());
        t.setName("t");
        t.start();
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+"--->"+i);
        }
    }
}
class MyRunable implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<100;i++){
            // 每10个让位一次
            if(i%10==0){
                Thread.yield(); // 当前线程暂停一下,让给主线程
            }
            System.out.println(Thread.currentThread().getName()+"--->"+i);
        }
    }
}

4.void join() 合并线程  

package com.java.javase.Thread;
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("main begin");
        Thread t=new Thread(new MyRunable());
        t.setName("t");
        t.start();
        // 合并线程
        t.join(); // t合并到当前线程中,当前线程受到阻塞,t线程执行直到结束,栈之间进行了协调
        System.out.println("main over");
    }
}
class MyRunable implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<5;i++){
            System.out.println(Thread.currentThread().getName()+"--->"+i);
        }
    }
}

 输出结果:

main begin
t--->0
t--->1
t--->2
t--->3
t--->4
main over

二、线程同步机制 

不使用线程同步机制下—模拟两个线程对同一个账户取款

账户类 

package com.java.javase.ThreadSafe;

public class Account {
    private String no;
    private double balance;
    public Account() {
    }
    public Account(String no, double balance) {
        this.no = no;
        this.balance = balance;
    }
    public String getNo() {
        return no;
    }
    public void setNo(String no) {
        this.no = no;
    }
    public double getBalance() {
        return balance;
    }
    public void setBalance(double balance) {
        this.balance = balance;
    }
    // 提供一个取款的方法
    public void withDraw(double money){
        // 未使用线程同步机制
        // t1和t2并发这个方法(t1和t2是两个栈,两个栈操作堆中同一个对象)
         double before=this.getBalance();
         double after=before-money;
         this.setBalance(after);
    }
}

线程类

package com.java.javase.ThreadSafe;

public class AccountThread extends Thread {
    // 两个线程必须共享同一个账户对象
    private Account act;
    public AccountThread(Account act){
        this.act=act;
    }
    @Override
    public void run() {
        double money=5000;
        act.withDraw(money);
        System.out.println(Thread.currentThread().getName()+"对001账户取款成功 "+"剩余余额:"+act.getBalance()  );
    }
}

测试类

package com.java.javase.ThreadSafe;

public class Test {
    public static void main(String[] args) {
        // 相当于同一张银行卡,属性有卡的账号和余额
        Account act=new Account("001",10000);
        // 两个栈操作堆中同一个对象,相当于两个人对同一张银行卡进行操作
        Thread t1=new AccountThread(act);
        Thread t2=new AccountThread(act);
        t1.setName("t1");
        t2.setName("t2");
        t1.start();
        t2.start();
    }
}

安全情况下:

t2对001账户取款成功 剩余余额:0.0
t1对001账户取款成功 剩余余额:5000.0

但是会出现线程安全问题:

t2对001账户取款成功 剩余余额:5000.0
t1对001账户取款成功 剩余余额:5000.0

如何使用线程同步机制解决线程安全问题:

我们只需要将账户类中的取款方法改为:

package com.java.javase.ThreadSafe;

public class Account {
    private String no;
    private double balance;
    public Account() {
    }
    public Account(String no, double balance) {
        this.no = no;
        this.balance = balance;
    }
    public String getNo() {
        return no;
    }
    public void setNo(String no) {
        this.no = no;
    }
    public double getBalance() {
        return balance;
    }
    public void setBalance(double balance) {
        this.balance = balance;
    }
    // 提供一个取款的方法
    public void withDraw(double money) {
        // 使用线程同步机制
          synchronized (this){
            // 这几行代码必须是排队的,不能并发
            // 线程同步机制的语法:
            // synchronized (){线程同步代码块}
            // ()中的内容相当关键
              
            try{
                Thread.sleep(1000);
            }catch (InterruptedException e){
            }
            double before=this.balance;
            double after=before-money;
            this.setBalance(after);
          }
    }
}

synchronized执行原理:

1.假设t1和t2线程并发,开始执行同步代码块的时候,肯定有一个先有一个后

2.假设t1先执行了,遇到了synchronized关键字,这个时候自动找”后面共享对象“的对象锁,找到之后占有这把锁,然后执行同步代码块中的程序,在程序执行过程中一直都是占有这把锁的,直到同步代码块执行结束,这把锁才会被释放

3.假设t1已经占有这把锁,此时t2也遇到synchronized关键字,也会去占有后面共享对象的这把锁,结果这把锁已经被t1占有,t2只能在同步代码块外面等待t1的结束,直到t1把同步代码块执行结束了,t1归还了这把锁,此时t2终于等到这把锁,然后t2占有这把锁,进入同步代码块执行程序,这样就达到了线程排队执行

synchronized可以出现实例方法上:

package com.java.javase.ThreadSafe;

public class Account {
    private String no;
    private double balance;
    public Account() {
    }
    public Account(String no, double balance) {
        this.no = no;
        this.balance = balance;
    }
    public String getNo() {
        return no;
    }
    public void setNo(String no) {
        this.no = no;
    }
    public double getBalance() {
        return balance;
    }
    public void setBalance(double balance) {
        this.balance = balance;
    }
    // 提供一个取款的方法

    
    public synchronized void withDraw(double money) {
        double before=this.balance;
        double after=before-money;
        try{
            Thread.sleep(1000);
        }catch (InterruptedException e){
        }
        this.setBalance(after);
    }
}

三、练习题  

练习1: 

package com.java.javase.Thread;
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
       MyClass mc=new MyClass();
       Thread t1=new MyThread(mc);
       Thread t2=new MyThread(mc);
       t1.setName("t1");
       t2.setName("t2");
       t1.start();
       Thread.sleep(1000); // 确保t1线程先执行
       t2.start();
    }
}
class MyThread extends Thread{
    private MyClass mc;
    public MyThread(MyClass mc){
        this.mc=mc;
    }
    public void run(){
        if(Thread.currentThread().getName().equals("t1")){
            mc.doSome();
        }else if(Thread.currentThread().getName().equals("t2")){
            mc.doOther();
        }
    }
}
class MyClass{
    public synchronized void doSome(){
        System.out.println("doSome begin");
        try{
            Thread.sleep(1000*10);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }

    
    public void doOther(){
        System.out.println("doOther begin");
        System.out.println("doOther over");
    }
}

输出结果: 

doSome begin
doOther begin
doOther over
doSome over

在上题中的doOther方法上加上synchronized,结果会怎么样?

package com.java.javase.Thread;
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
       MyClass mc=new MyClass();
       Thread t1=new MyThread(mc);
       Thread t2=new MyThread(mc);
       t1.setName("t1");
       t2.setName("t2");
       t1.start();
       Thread.sleep(1000); // 确保t1线程先执行
       t2.start();
    }
}
class MyThread extends Thread{
    private MyClass mc;
    public MyThread(MyClass mc){
        this.mc=mc;
    }
    public void run(){
        if(Thread.currentThread().getName().equals("t1")){
            mc.doSome();
        }else if(Thread.currentThread().getName().equals("t2")){
            mc.doOther();
        }
    }
}
class MyClass{
    public synchronized void doSome(){
        System.out.println("doSome begin");
        try{
            Thread.sleep(1000*10);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }

    
    public synchronized void doOther(){
        System.out.println("doOther begin");
        System.out.println("doOther over");
    }
}

输出结果:

doSome begin
doSome over
doOther begin
doOther over

synchronized 出现在静态方法上:

package com.java.javase.Thread;
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
       MyClass mc=new MyClass();
       Thread t1=new MyThread(mc);
       Thread t2=new MyThread(mc);
       t1.setName("t1");
       t2.setName("t2");
       t1.start();
       Thread.sleep(1000); // 确保t1线程先执行
       t2.start();
    }
}
class MyThread extends Thread{
    private MyClass mc;
    public MyThread(MyClass mc){
        this.mc=mc;
    }
    public void run(){
        if(Thread.currentThread().getName().equals("t1")){
            mc.doSome();
        }else if(Thread.currentThread().getName().equals("t2")){
            mc.doOther();
        }
    }
}
class MyClass{
    public synchronized static void doSome(){
        System.out.println("doSome begin");
        try{
            Thread.sleep(1000*10);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }

    
    public synchronized static void doOther(){
        System.out.println("doOther begin");
        System.out.println("doOther over");
    }
}

doSome begin
doSome over
doOther begin
doOther over

四、死锁

package com.java.javase.deadLock;
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        Object o1=new Object();
        Object o2=new Object();
        // t1和t2两个线程共享o1,o2
        Thread t1=new MyThread1(o1,o2);
        Thread t2=new MyThread2(o1,o2);
        t1.start();
        t2.start();
    }
}
class MyThread1 extends Thread{
   Object o1;
   Object o2;
   public MyThread1(Object o1,Object o2){
      this.o1=o1;
      this.o2=o2;
   }
    @Override
    public void run() {
        synchronized (o1){
            try {
                Thread.sleep(1000*5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o2){

            }
        }
    }
}
class MyThread2 extends Thread{
    Object o1;
    Object o2;
    public MyThread2(Object o1,Object o2){
        this.o1=o1;
        this.o2=o2;
    }
    @Override
    public void run() {
        synchronized (o2){
            try {
                Thread.sleep(1000*5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o1){

            }
        }
    }
}

五、 守护线程和定时器

package com.java.javase.Thread;
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
       Thread t1=new MyThread();
       t1.setName("备份数据的线程");
       // 启动线程之前,将线程设置为守护线程
        t1.setDaemon(true);
        t1.start();
       // 主线程:用户线程
        for(int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"---"+i);
            Thread.sleep(1000);
        }
    }
}
class MyThread extends Thread{
    @Override
    public void run() {
        // 即使是死循环,但由于该线程是守护线程,当用户线程结束,守护线程自动停止
       int i=0;
       while (true) {
           System.out.println(Thread.currentThread().getName() + "---" + (i++));
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
    }
}

备份数据的线程---7
备份数据的线程---8
main---8
main---9
备份数据的线程---9
备份数据的线程---10

Process finished with exit code 0

定时器: 

package com.java.javase.Timer;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class TimerTest01 {
    public static void main(String[] args) throws Exception{
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date firstTime=sdf.parse("2021-11-01 14:36:00");
        // 创建定时器对象
        Timer timer=new Timer();
        // 三个参数:指定定时任务,第一次执行时间,间隔多久执行一次
        timer.schedule(new LogTimerTask(),firstTime,1000*10);

    }
}
// 编写一个定时任务类
// TimerTask实现了Runnable接口
class LogTimerTask extends TimerTask{
    @Override
    public void run() {
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        String strTime=sdf.format(new Date());
        System.out.println(strTime+":完成了一次数据备份");
    }
}

2021-11-01 14:36:00 016:完成了一次数据备份
2021-11-01 14:36:10 019:完成了一次数据备份
2021-11-01 14:36:20 035:完成了一次数据备份

六、实现线程的第三种方式 ,实现Callable接口

package com.java.javase.Thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;


public class ThreadTest {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // 创建一个"未来任务类"对象
        // 需要给一个Callable接口实现类对象
        FutureTask futureTask=new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception {
                // call()方法就相当于run方法,只不过这个有返回值
                // 线程执行一个任务,执行之后可能会有一个执行结果
                System.out.println("call begin");
                Thread.sleep(1000*10);
                System.out.println("call over");
                return new Object();
            }
        });
        // 创建线程对象
        // 这里是main方法,在主线程中
        // 在主线程中怎么获取t线程的返回结果?
        // get()方法的执行会导致当前线程阻塞
        Thread t=new Thread(futureTask);
        t.start();
        futureTask.get();
        // main方法的这里的程序要想执行必须等待get()方法的结束
        // 而get()方法可能需要很久
        System.out.println("拿到执行结果");
    }
}

七、Object类中的wait和notify方法 —生产者和消费者模式

package com.java.javase.ProducerAndCustomer;

import java.util.ArrayList;
import java.util.List;
// 使用wait方法和notify方法实现“生产者和消费者模式”
public class ThreadTest {
    public static void main(String[] args) {
        List list=new ArrayList();
        Thread t1=new Thread(new Producer(list));
        Thread t2=new Thread(new Customer(list));
        t1.setName("生产者线程");
        t2.setName("消费者线程");
        t1.start();
        t2.start();
    }
}
// 生产线程
class Producer implements Runnable{
    // 仓库
    private List list;
    public Producer(List list) {
        this.list = list;
    }
    @Override
    public void run() {
        while(true){
            // 给仓库对象list加锁
            synchronized (list){
                // 大于0,说明仓库中已经有一个元素了
                if(list.size()>0){
                    try {
                        // 当前线程进入等待状态,并且释放Producer之前占有的list集合的锁
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Object obj = new Object();
                list.add(obj);
                System.out.println(Thread.currentThread().getName() + "-----生产了一个" + obj);
                list.notifyAll();
            }
        }
    }
}
// 消费线程
class Customer implements Runnable{
    private List list;
    public Customer(List list) {
        this.list = list;
    }
    @Override
    public void run() {
        while(true){
            synchronized (list){
                if(list.size()==0){
                    try {
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Object obj=list.remove(0);
                System.out.println(Thread.currentThread().getName()+"-----消费了一个"+obj);
                list.notifyAll();
            }
        }
    }
}


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/396552.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号