乐趣区

关于java:多线程

创立和启动线程的形式一

  • 自定义类 继承 Thread 类 并重写 run 办法,而后创立该类的对象调用 start 办法。

创立和启动线程的形式二

  • 自定义类 实现 Runnable 接口 并重写 run 办法,创立该类的对象作为实参来结构 Thread 类型的对象,而后应用 Thread 类型的对象调用 start 办法。
public static void main(String[] args) {new Thread(){
        @Override
        public void run() {System.out.println("t1");
        }
    }.start();

    new Thread(new Runnable() {
        @Override
        public void run() {System.out.println("t2");
        }
    }).start();

    new Thread(()->{System.out.println("t3");
    }).start();}

1. 继承形式治理线程编号和名称

public class ThreadIdNameTest extends Thread{public ThreadIdNameTest(String sun) {
        // 调用父类的构造方法
        super(sun);
    }

    @Override
    public void run() {System.out.println("子线程的 ID:" + this.getId());
        System.out.println("子线程的名称:" + getName());
    }

    public static void main(String[] args) {ThreadIdNameTest t1 = new ThreadIdNameTest("sun");
        t1.start();

        System.out.println("主线程的 ID:" + Thread.currentThread().getId());
        System.out.println("主线程的名称:" + Thread.currentThread().getName());
    }
}

===========
主线程的 ID:1
子线程的名称:sun
主线程的名称:main

2. 接口的形式治理线程编号和名称

public class MainTest {public static void main(String[] args) {new Thread(()->{System.out.println("子线程的 ID:" + Thread.currentThread().getId());
            System.out.println("子线程的名称:" + Thread.currentThread().getName());
            Thread.currentThread().setName("tttt");
            System.out.println("子线程的名称:" + Thread.currentThread().getName());
        }).start();

        System.out.println("主线程的 ID:" + Thread.currentThread().getId());
        System.out.println("主线程的名称:" + Thread.currentThread().getName());
    }
}
===========
子线程的 ID:14
主线程的 ID:1
子线程的名称:Thread-0
主线程的名称:main
子线程的名称:tttt

3. 罕用的办法

  • Sleep

public class ThreadSleepTest extends Thread {

    private boolean flag = true;

    @Override
    public void run() {while (flag) {LocalDateTime now = LocalDateTime.now();
            String time = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:ss:mm"));
            System.out.println(time);
            try {Thread.sleep(1000);
            } catch (InterruptedException e) {e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {ThreadSleepTest t1 = new ThreadSleepTest();

        t1.start();

        try {Thread.sleep(5000);
        } catch (InterruptedException e) {e.printStackTrace();
        }

        t1.flag = false;
    }
}
  • 守护线程

public class ThreadDaemonTest extends Thread{

    @Override
    public void run() {
        // 默认不是守护线程
        // 当子线程不是守护线程时,尽管主线程先完结了,然而子线程仍然会继续执行,直到打印所有数据为止
        // 当子线程是守护时,当主线程完结后,则子线程随之完结
        for(int i = 0;i < 50;i++){System.out.println("子线程:i ="+ i);
        }
    }

    public static void main(String[] args) {ThreadDaemonTest t1 = new ThreadDaemonTest();
        // 必须在线程启动之前设置子线程为守护线程
        t1.setDaemon(true);
        t1.start();

        for(int i = 0;i < 20;i++){System.out.println("主线程:i ="+ i);
        }
    }
}
  • join 期待该线程终止

/**
 * @Author 振帅
 * @Date 2021/05/26 21:49
 */
public class ThreadJoinTest {public static void main(String[] args) {

        // 偶数
        Thread t1 = new Thread(()->{for(int i = 0;i < 100;i += 2){System.out.println("ti==:" + i);
            }
        });
        // 奇数
        Thread t2 = new Thread(()->{for(int i = 1;i < 100;i += 2){System.out.println("t2:" + i);
            }
        });

        System.out.println("主线程开始");
        t1.start();
        t2.start();
        try {t1.join();
            t2.join();} catch (InterruptedException e) {e.printStackTrace();
        }
        System.out.println("主线程完结");

    }
}

线程同步机制

1. 同步代码块实现线程同步形式一

  • synchronized (dm)
/**
 * @Author 振帅
 * @Date 2021/05/26 22:39
 * synchronized 模仿取钱
 */
public class AccountRunnableTest implements Runnable{class Demo {}
    // 锁
    Demo dm = new Demo();
    // 账户余额
    private int balance;

    public AccountRunnableTest(int balance) {this.balance = balance;}

    public int getBalance() {return balance;}

    public void setBalance(int balance) {this.balance = balance;}

    @Override
    public void run() {System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
        //synchronized (new Demo()){ // 锁不住
        synchronized (dm){
            //1. 模仿从后盾查问余额
            int temp  = getBalance();
            //2. 模仿取款 200
            if (temp > 200) {System.out.println("正在出钞,请稍后...");
                temp -= 200;
                try {Thread.sleep(5000);
                } catch (InterruptedException e) {e.printStackTrace();
                }
                System.out.println("请取走你的钞票!");
            } else {System.out.println("余额有余");
            }
            //3. 更新余额
            setBalance(temp);
        }
    }

    public static void main(String[] args) {AccountRunnableTest account = new AccountRunnableTest(1000);
        Thread t1 = new Thread(account);
        Thread t2 = new Thread(account);

        t1.start();
        t2.start();
        System.out.println("主线程开始期待...");
        try {t1.join();
            t2.join();} catch (InterruptedException e) {e.printStackTrace();
        }
        System.out.println("最终账户余额为:"+ account.getBalance());
    }
}

2. 同步代码块实现线程同步形式二

  • static 润饰锁
/**
 * @Author 振帅
 * @Date 2021/05/26 22:39
 * synchronized 模仿取钱
 */
public class AccountThreadTest extends Thread{static class Demo {}
    // 锁 属于类层级 所有对象共享一个
    private static Demo  dm = new Demo();
    // 账户余额
    private int balance;

    public AccountThreadTest(int balance) {this.balance = balance;}

    public int getBalance() {return balance;}

    public void setBalance(int balance) {this.balance = balance;}

    @Override
    public void run() {System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
        synchronized (dm){
            //1. 模仿从后盾查问余额
            int temp  = getBalance();
            //2. 模仿取款 200
            if (temp > 200) {System.out.println("正在出钞,请稍后...");
                temp -= 200;
                try {Thread.sleep(5000);
                } catch (InterruptedException e) {e.printStackTrace();
                }
                System.out.println("请取走你的钞票!");
            } else {System.out.println("余额有余");
            }
            //3. 更新余额
            setBalance(temp);
        }
    }

    public static void main(String[] args) {AccountThreadTest at1 = new AccountThreadTest(1000);
        AccountThreadTest at2 = new AccountThreadTest(1000);

        at1.start();
        at2.start();
        System.out.println("主线程开始期待...");
        try {at1.join();
            at2.join();} catch (InterruptedException e) {e.printStackTrace();
        }
        System.out.println("at1 账户余额为:"+ at1.getBalance());
        System.out.println("at2 账户余额为:"+ at2.getBalance());
    }
}

3. 同步办法块实现线程同步形式一

  • 应用 synchronized 锁住整个办法
/**
 * @Author 振帅
 * @Date 2021/05/26 22:39
 * synchronized 模仿取钱
 */
public class AccountRunnableTest implements Runnable{

    // 账户余额
    private int balance;

    public AccountRunnableTest(int balance) {this.balance = balance;}

    public int getBalance() {return balance;}

    public void setBalance(int balance) {this.balance = balance;}

    @Override
    public synchronized void run() {System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
        //1. 模仿从后盾查问余额
        int temp  = getBalance();
        //2. 模仿取款 200
        if (temp > 200) {System.out.println("正在出钞,请稍后...");
            temp -= 200;
            try {Thread.sleep(3000);
            } catch (InterruptedException e) {e.printStackTrace();
            }
            System.out.println("请取走你的钞票!");
        } else {System.out.println("余额有余");
        }
        //3. 更新余额
        setBalance(temp);

    }

    public static void main(String[] args) {AccountRunnableTest account = new AccountRunnableTest(1000);
        Thread t1 = new Thread(account);
        Thread t2 = new Thread(account);

        t1.start();
        t2.start();
        System.out.println("主线程开始期待...");
        try {t1.join();
            t2.join();} catch (InterruptedException e) {e.printStackTrace();
        }
        System.out.println("最终账户余额为:"+ account
        
        .getBalance());
    }
}

该形式等价于:

synchronized(this) {整个办法体的代码}

this 代表调用 run 办法的对象,由源码可知 调用 run 办法的对象是account

因而 synchronized(this){} 能够锁住

@Override
public void run() {synchronized (this){System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
        //1. 模仿从后盾查问余额
        int temp  = getBalance();
        //2. 模仿取款 200
        if (temp > 200) {System.out.println("正在出钞,请稍后...");
            temp -= 200;
            try {Thread.sleep(5000);
            } catch (InterruptedException e) {e.printStackTrace();
            }
            System.out.println("请取走你的钞票!");
        } else {System.out.println("余额有余");
        }
        //3. 更新余额
        setBalance(temp);
    }
}

4. 同步办法块实现线程同步形式二

  • 当咱们对一个静态方法加锁,如:
    public synchronized static void xxx(){….}
  • 那么该办法锁的对象是类对象。每个类都有惟一的一个类对象。获取类对象的形式: 类名.class。
  • 静态方法与非静态方法同时应用了 synchronized 后它们之间是非互斥关系的。
  • 起因在于:静态方法锁的是类对象而非静态方法锁的是以后办法所属对象。
/**
 * @Author 振帅
 * @Date 2021/05/26 22:39
 * synchronized
 */
public class AccountThreadTest extends Thread{
    // 账户余额
    private int balance;

    public AccountThreadTest(int balance) {this.balance = balance;}

    public int getBalance() {return balance;}

    public void setBalance(int balance) {this.balance = balance;}

    public synchronized static void test(){System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
        System.out.println("正在出钞,请稍后...");
        try {Thread.sleep(5000);
        } catch (InterruptedException e) {e.printStackTrace();
        }
        System.out.println("请取走你的钞票!");
    }

    @Override
    public void run() {test();
    }

    public static void main(String[] args) {AccountThreadTest at1 = new AccountThreadTest(1000);
        AccountThreadTest at2 = new AccountThreadTest(1000);

        at1.start();
        at2.start();
        System.out.println("主线程开始期待...");
        try {at1.join();
            at2.join();} catch (InterruptedException e) {e.printStackTrace();
        }
        System.out.println("at1 账户余额为:"+ at1.getBalance());
        System.out.println("at2 账户余额为:"+ at2.getBalance());
    }
}

等价于

public static void test(){synchronized(AccountThreadTest.class){System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
        System.out.println("正在出钞,请稍后...");
        try {Thread.sleep(5000);
        } catch (InterruptedException e) {e.printStackTrace();
        }
        System.out.println("请取走你的钞票!");
    }
}

5. 应用 Lock(锁)实现线程同步

  • 从 Java5 开始提供了更弱小的线程同步机制—应用显式定义的同步锁对象来实现。
  • java.util.concurrent.locks.Lock 接口是管制多个线程对共享资源进行拜访的工具。
  • 该接口的次要实现类是 ReentrantLock 类,该类领有与 synchronized 雷同的并发性,在当前的线程安全控制中,常常应用 ReentrantLock 类显式加锁和开释锁。

与 synchronized 形式的比拟

  • Lock 是显式锁,须要手动实现开启和敞开操作,而 synchronized 是隐式锁,执行锁定代码后主动
    开释。
  • Lock 只有同步代码块形式的锁,而 synchronized 有同步代码块形式和同步办法两种锁。
  • 应用 Lock 锁形式时,Java 虚拟机将破费较少的工夫来调度线程,因而性能更好。
/**
 * @Author 振帅
 * @Date 2021/05/26 22:39
 * ReentrantLock 模仿取钱
 */
public class AccountRunnableTest implements Runnable{

    // 账户余额
    private int balance;

    // 筹备锁
    private ReentrantLock lock = new ReentrantLock();


    public AccountRunnableTest(int balance) {this.balance = balance;}

    public int getBalance() {return balance;}

    public void setBalance(int balance) {this.balance = balance;}

    @Override
    public void run() {
        // 开始加锁
        lock.lock();
        System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
        //1. 模仿从后盾查问余额
        int temp  = getBalance();
        //2. 模仿取款 200
        if (temp > 200) {System.out.println("正在出钞,请稍后...");
            temp -= 200;
            try {Thread.sleep(3000);
            } catch (InterruptedException e) {e.printStackTrace();
            }
            System.out.println("请取走你的钞票!");
        } else {System.out.println("余额有余");
        }
        //3. 更新余额
        setBalance(temp);
        // 解锁
        lock.unlock();}

    public static void main(String[] args) {AccountRunnableTest account = new AccountRunnableTest(1000);
        Thread t1 = new Thread(account);
        Thread t2 = new Thread(account);

        t1.start();
        t2.start();
        System.out.println("主线程开始期待...");
        try {t1.join();
            t2.join();} catch (InterruptedException e) {e.printStackTrace();
        }
        System.out.println("最终账户余额为:"+ account.getBalance());
    }
}

线程通信

1.Object 类罕用的办法

办法申明 性能介绍
void wait() 用于使得线程进入期待状态,直到其它线程调用 notify()或 notifyAll()方 法
void wait(long timeout) 用于进入期待状态,直到其它线程调用办法或参数指定的毫秒数曾经过来为止
void notify() 用于唤醒期待的单个线程
void notifyAll() 用于唤醒期待的所有线程
/**
 * @Author 振帅
 * @Date 2021/05/26 23:53
 * 线程通信 两个线程交替打印 1 -100
 */
public class ThreadCommunicateTest implements Runnable {

    private int cnt = 1;

    @Override
    public void run() {while (true) {synchronized (this){
                // 用于唤醒期待的单个线程 写在锁外面
                notify();
                if(cnt <= 100){System.out.println("线程:"+ Thread.currentThread().getName() +"中的 cnt:=" +cnt);
                    try {Thread.sleep(200);
                    } catch (InterruptedException e) {e.printStackTrace();
                    }
                    cnt++;
                    try {// 用于使得线程进入期待状态,直到其它线程调用 notify()或 notifyAll()办法 写在锁外面
                        // 以后线程打印完一个数 为了避免持续打印 调用 wait()
                        wait();} catch (InterruptedException e) {e.printStackTrace();
                    }
                }else {break;}
            }

        }
    }

    public static void main(String[] args) {ThreadCommunicateTest tc = new ThreadCommunicateTest();
        Thread t1 = new Thread(tc);
        Thread t2 = new Thread(tc);
        t1.start();
        t2.start();}
}

2. 生产者消费者模型实现

仓库类

/**
 * @Author 振帅
 * @Date 2021/05/27 0:20
 * 仓库类
 */
public class StoreHouse {

    // 记录产品的数量
    private int cnt = 0;
    
    // 生产办法
    public synchronized void produceProduct(){notify();
        if (cnt < 10) {System.out.println("线程"+Thread.currentThread().getName()+"正在生产第"+(cnt+1)+"个产品");
            cnt++;
        } else {
            try {wait();
            } catch (InterruptedException e) {e.printStackTrace();
            }
        }
    }
    
     // 生产办法
    public synchronized void consumerProduct(){notify();
        if (cnt > 0) {System.out.println("线程"+Thread.currentThread().getName()+"正在生产第"+(cnt)+"个产品");
            cnt--;
        } else {
            try {wait();
            } catch (InterruptedException e) {e.printStackTrace();
            }
        }

    }
}

生产者

/**
 * @Author 振帅
 * @Date 2021/05/27 0:22
 * 生产者
 */
public class ProduceThread extends Thread{

    // 申明一个仓库类型的援用作为成员变量,为了能调用仓库类中的生产办法 合成复用准则
    private StoreHouse storeHouse;

    // 为了确保两个线程共用一个仓库
    public ProduceThread(StoreHouse storeHouse){this.storeHouse = storeHouse;}

    @Override
    public void run() {while (true) {storeHouse.produceProduct();
            try {Thread.sleep(500);
            } catch (InterruptedException e) {e.printStackTrace();
            }
        }
    }
}

消费者

/**
 * @Author 振帅
 * @Date 2021/05/27 0:22
 * 消费者
 */
public class ConsumerThread extends Thread{

    // 申明一个仓库类型的援用作为成员变量,为了能调用仓库类中的生产办法 合成复用准则
    private StoreHouse storeHouse;

    // 为了确保两个线程共用一个仓库
    public ConsumerThread(StoreHouse storeHouse){this.storeHouse = storeHouse;}

    @Override
    public void run() {while (true) {storeHouse.consumerProduct();
            try {Thread.sleep(1000);
            } catch (InterruptedException e) {e.printStackTrace();
            }
        }
    }
}

测试类

/**
 * @Author 振帅
 * @Date 2021/05/27 0:27
 */
public class StoreHouseTest {public static void main(String[] args) {
        // 创立仓库类对象
        StoreHouse storeHouse = new StoreHouse();
        ProduceThread t1 = new ProduceThread(storeHouse);
        ConsumerThread t2 = new ConsumerThread(storeHouse);
        t1.start();
        t2.start();}
}

创立和启动线程的形式三

  • 实现 Callable 接口
  • FutureTask 类 extends RunnableFuture extends Runnable
/**
 * @Author 振帅
 * @Date 2021/05/27 0:44
 */
public class ThreadCallableTest implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        // 计算 1 -10000 之间累加和
        int sum = 0;
        for (int i = 1;i <= 1000; i++){sum = sum+i;}
        System.out.println("计算的后果为:" + sum);
        return sum;
    }

    public static void main(String[] args) {ThreadCallableTest tct = new ThreadCallableTest();
        // 用于形容可勾销的异步计算,该类提供了 Future 接口的根本实
        // 现,包含启动和勾销计算、查问计算是否实现以及检索计算结果的办法,也能够用于获取办法调用
        // 后的返回后果
        FutureTask<Integer> task = new FutureTask<Integer>(tct);
        Thread th = new Thread(task);
        th.start();
        Integer result = null;
        try {
            // 获取 call 办法计算的后果
            result = task.get();} catch (InterruptedException e) {e.printStackTrace();
        } catch (ExecutionException e) {e.printStackTrace();
        }

        System.out.println("线程处理结果:" + result);

    }
}

创立和启动线程的形式四

  • Executors 类

工具类和线程池的工厂类,能够创立并返回不同类型的线程池,罕用办法如下:

办法申明 性能介绍
static ExecutorService newCachedThreadPool() 创立一个可依据须要创立新线程的线程池
static ExecutorService newFixedThreadPool(int nThreads) 创立一个可重用固定线程数的线程池
static ExecutorService newSingleThreadExecutor() 创立一个只有一个线程的线程池
  • ExecutorService 接口

真正的线程池接口,次要实现类是 ThreadPoolExecutor,罕用办法如下:

办法申明 性能介绍
void execute(Runnable command) 执行工作和命令,通常用于执行 Runnable
Future submit(Callable task) 执行工作和命令,通常用于执行 Callable
void shutdown() 启动有序敞开
public class ThreadPoolTest {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(10);
        ThreadCallableTest threadCallableTest = new ThreadCallableTest();
        Future<Integer> future = executorService.submit(threadCallableTest);
        Integer result = null;
        try {result = future.get();
        } catch (InterruptedException e) {e.printStackTrace();
        } catch (ExecutionException e) {e.printStackTrace();
        }
        System.out.println("线程处理结果:" + result);
        executorService.shutdown();}
}
退出移动版