第二十五天 多线程-常用方法&线程池【悟空教程】
第25天 多线程
继承Thread方法:
public class MyThread extends Thread {
//重写 run
@Override
public void run() {
for (int i = 0; i < 1000 ; i++) {
System.out.println(getName() + " ==== " + i );
}
}
}
/*
* 多线程 简单方法:
* String getName() 返回该线程的名称。
* void setName(String name) 设置线程名称
* Thread currentThread() , 返回的就是当前的执行线程.
*/
public class Demo {
public static void main(String[] args) {
// 创建 MyThread 对象
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
mt1.setName("姚明");
mt2.setName("郭敬明");
mt1.start();
mt2.start();
}
}
实现Runnable接口
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 1000; i >= 0; i--) {
// 获取当前方法的执行线程
Thread currentThread = Thread.currentThread(); // 执行当前的方法的线程对象.
System.out.println(currentThread.getName() + " ==== " + i);
}
}
}
public class Demo {
public static void main(String[] args) {
//创建 子类对象
MyRunnable myRunnable = new MyRunnable();
//创建线程对象
Thread thread = new Thread(myRunnable);
Thread thread2 = new Thread(myRunnable);
thread.setName("XXX");
thread2.setName("AAA");
// 开启线程
thread.start();
thread2.start();
Thread currentThread = Thread.currentThread();
currentThread.setName("^(* ̄(oo) ̄)^");
System.out.println(currentThread.getName());
}
}
public final void setPriority(int newPriority) 设置优先级,取值:1-10
public final int getPriority() 获取优先级
/*
public final void setPriority(int newPriority) 设置优先级,取值:1-10
public final int getPriority() 获取优先级
*/
public class Demo2 {
public static void main(String[] args) {
Thread currentThread = Thread.currentThread();
System.out.println(currentThread);
int priority = currentThread.getPriority();
System.out.println(priority); // 默认是 5 ,最小 1 ,最大10;
MyThread myThread = new MyThread();
myThread.setPriority(10);
myThread.start();
for (int i = 0; i < 1000 ; i++) {
System.out.println("main ---" + i);
}
}
}
public final void join() throws InterruptedException 线程加入(插队,加入方法)
public final void setDaemon(boolean on) 设置守护线程(坦克大战,守护main)
public class T1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
if (i == 200) {
// 加入另外一条线程
try {
T2 t2 = new T2();
t2.start();
t2.join(); // 加入方法, 就是插队.
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(i + " ==== t1" );
}
}
}
public class T2 extends Thread {
@Override
public void run() {
for (int i = 0; i < 500 ; i++) {
System.out.println(" t2 ---- " + i );
}
}
}
/*
* join . 加入
* setDaemon(boolean on) 设置守护线程
*
*/
public class Demo3 {
public static void main(String[] args) {
T1 t1 = new T1();
t1.setDaemon(true); // t1 就是守护线程 了
t1.start();
for (int i = 0; i < 10; i++) {
System.out.println(i); // 主线程 一完成, 守护也就结束.
}
}
}
public void interrupt() 被中断的线程会报被中断异常,这时需要使用try/catch语句解决相关问题,线程后代码仍然可以继续执行
public final void stop() (已过时) 直接停止线程,线程后代码无法被执行
public static void sleep(long millis) throws InterruptedException
/*
* public static void sleep(long millis) 睡眠 .
*/
public class Demo {
public static void main(String[] args) {
for (int i = 10; i > 0; i--) {
// 暂停
try {
Thread.sleep(10000); // 到指定的时间,自动醒过来.
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
System.out.println("点火 ");
}
}
/*
* 获取垃圾回收线程的名字.
*/
public class Demo {
public static void main(String[] args) {
for (int i = 0; i < 100000; i++) {
new Person();
System.out.println(i);
}
}
}
public class Person {
public Person() {
System.out.println(" 我来了, HELLO==WORLD");
}
@Override
protected void finalize() throws Throwable {
System.out.println(" 我是 垃圾 了, 我挂了, ByeBye World ");
Thread currentThread = Thread.currentThread();
System.out.println(currentThread.getName());
}
}
在开始讲解等待唤醒机制之前,有必要搞清一个概念——线程之间的通信:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。通过一定的手段使各个线程能有效的利用资源。而这种手段即—— 等待唤醒机制。
等待唤醒机制所涉及到的方法:
其实,所谓唤醒的意思就是让 线程池中的线程具备执行资格。必须注意的是,这些方法都是在 同步中才有效。同时这些方法在使用时必须标明所属锁,这样才可以明确出这些方法操作的到底是哪个锁上的线程。
仔细查看JavaAPI之后,发现这些方法 并不定义在 Thread中,也没定义在Runnable接口中,却被定义在了Object类中,为什么这些操作线程的方法定义在Object类中?
因为这些方法在使用时,必须要标明所属的锁,而锁又可以是任意对象。能被任意对象调用的方法一定定义在Object类中。
接下里,我们先从一个简单的示例入手:
如上图说示,输入线程向Resource中输入name ,sex , 输出线程从资源中输出,先要完成的任务是:
下面代码,模拟等待唤醒机制的实现:
public class Resource {
private String name;
private String sex;
private boolean flag = false;
public synchronized void set(String name, String sex) {
if (flag)
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 设置成员变量
this.name = name;
this.sex = sex;
// 设置之后,Resource中有值,将标记该为 true ,
flag = true;
// 唤醒output
this.notify();
}
public synchronized void out() {
if (!flag)
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 输出线程将数据输出
System.out.println("姓名: " + name + ",性别: " + sex);
// 改变标记,以便输入线程输入数据
flag = false;
// 唤醒input,进行数据输入
this.notify();
}
}
public class Input implements Runnable {
private Resource r;
public Input(Resource r) {
this.r = r;
}
@Override
public void run() {
int count = 0;
while (true) {
if (count == 0) {
r.set("小明", "男生");
} else {
r.set("小花", "女生");
}
// 在两个数据之间进行切换
count = (count + 1) % 2;
}
}
}
public class Output implements Runnable {
private Resource r;
public Output(Resource r) {
this.r = r;
}
@Override
public void run() {
while (true) {
r.out();
}
}
}
public class ResourceDemo {
public static void main(String[] args) {
// 资源对象
Resource r = new Resource();
// 任务对象
Input in = new Input(r);
Output out = new Output(r);
// 线程对象
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
// 开启线程
t1.start();
t2.start();
}
}
线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
我们详细的解释一下为什么要使用线程池?
在java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。为了防止资源不足,需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务。
线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。
通常,线程池都是通过线程池工厂创建,再调用线程池中的方法获取线程,再通过线程去执行任务方法。
代码演示:
public class ThreadPoolDemo {
public static void main(String[] args) {
//创建线程池对象
ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
//创建Runnable实例对象
MyRunnable r = new MyRunnable();
//自己创建线程对象的方式
//Thread t = new Thread(r);
//t.start(); ---> 调用MyRunnable中的run()
//从线程池中获取线程对象,然后调用MyRunnable中的run()
service.submit(r);
//再获取个线程对象,调用MyRunnable中的run()
service.submit(r);
service.submit(r);
//注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中
//关闭线程池
//service.shutdown();
}
}
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("我要一个教练");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("教练来了: " +Thread.currentThread().getName());
System.out.println("教我游泳,交完后,教练回到了游泳池");
}
}
代码演示:
public class ThreadPoolDemo {
public static void main(String[] args) {
//创建线程池对象
ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
//创建Callable对象
MyCallable c = new MyCallable();
//从线程池中获取线程对象,然后调用MyRunnable中的run()
service.submit(c);
//再获取个教练
service.submit(c);
service.submit(c);
//注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中
//关闭线程池
//service.shutdown();
}
}
public class MyCallable implements Callable {
@Override
public Object call() throws Exception {
System.out.println("我要一个教练:call");
Thread.sleep(2000);
System.out.println("教练来了: " +Thread.currentThread().getName());
System.out.println("教我游泳,交完后,教练回到了游泳池");
return null;
}
}
要求:通过线程池中的线程对象,使用Callable接口完成两个数求和操作
代码演示:
public class ThreadPoolDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//创建线程池对象
ExecutorService threadPool = Executors.newFixedThreadPool(2);
//创建一个Callable接口子类对象
//MyCallable c = new MyCallable();
MyCallable c = new MyCallable(100, 200);
MyCallable c2 = new MyCallable(10, 20);
//获取线程池中的线程,调用Callable接口子类对象中的call()方法, 完成求和操作
//<Integer> Future<Integer> submit(Callable<Integer> task)
// Future 结果对象
Future<Integer> result = threadPool.submit(c);
//此 Future 的 get 方法所返回的结果类型
Integer sum = result.get();
System.out.println("sum=" + sum);
//再演示
result = threadPool.submit(c2);
sum = result.get();
System.out.println("sum=" + sum);
//关闭线程池(可以不关闭)
}
}
public class MyCallable implements Callable<Integer> {
//成员变量
int x = 5;
int y = 3;
//构造方法
public MyCallable(){
}
public MyCallable(int x, int y){
this.x = x;
this.y = y;
}
@Override
public Integer call() throws Exception {
return x+y;
}
}
多个线程想保证线程安全,必须要使用同一个锁对象
synchronized (锁对象){
可能产生线程安全问题的代码
}
同步代码块的锁对象可以是任意的对象
public synchronized void method()
可能产生线程安全问题的代码
}
同步方法中的锁对象是 this
public synchronized void method()
可能产生线程安全问题的代码
}
静态同步方法中的锁对象是 类名.class
a, 继承Thread类
b, 实现Runnable接口
c, 通过线程池,实现Callable接口
a,同步代码块
b,同步方法
静态同步方法
启动一个线程是start()
区别:
start: 启动线程,并调用线程中的run()方法
run : 执行该线程对象要执行的任务
sleep: 不释放锁对象, 释放CPU使用权
在休眠的时间内,不能唤醒
wait(): 释放锁对象, 释放CPU使用权
在等待的时间内,能唤醒
锁对象可以是任意类型的对象
启动一个线程是调用start()方法,使线程就绪状态,以后可以被调度为运行状态,一个线程必须关联一些具体的执行代码,run()方法是该线程所关联的执行代码。
答案:
public class Ticket implements Runnable {
//多个对象 共享同一个资源, 需要static修饰
private int ticket = 100;
//创建锁对象(可以是任意对象)
private Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized ( obj ){
if (ticket >0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":正在卖第 "+ ticket-- +"张
票");
}
}
}
}
}
public class SellTicket {
public static void main(String[] args) {
//创建自定义类对象
Ticket ticket = new Ticket();
//创建线程对象
Thread t1 = new Thread(ticket, "窗口1");
Thread t2 = new Thread(ticket, "窗口2");
Thread t3 = new Thread(ticket, "窗口3");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
其实就是多个线程在操作同一个资源,但是操作的动作不同。如果一个进程中的所有线程都不需要相互传递数据就可以顺利完成,那么程序运行的性能自然是最好的,但是实际上,很少有现成能够在所有的时间都独立的进行操作,通常在以下两种情况下,线程之间需要进行通信。 多个线程都对共享资源资源进行访问,但不希望共享资源被破坏。 一个线程完成了任务,要通知其他的线程。
多线程有两种实现方法,分别是继承Thread类与实现Runnable接口
同步的实现方面有两种,分别是synchronized,wait与notify
wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
1、通过一个线程获取一个0-100内的随机数并在将随机数返回到main方法中,在main方法中将该随机数添加到list集合中;
2、将该任务向线程池提交3次,每次生成随机数之前让线程休眠1000毫秒,然后打印“”“线程XXXX生成的随机数为:XXX”;
3、在main方法中打印集合的内容
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Demo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
m2();
}
public static void m2() throws InterruptedException, ExecutionException{
//1、使用线程池工厂创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);
//2、向线程池中提交任务
MyCallable my = new MyCallable();
List<Integer> list = new ArrayList<Integer>();
for(int i=0;i<3;i++){
//循环向线程池中提交任务
Future<Integer> s1 = pool.submit(my);
//解析线程执行的结果
Integer num1 = s1.get();
list.add(num1);
}
System.out.println(list);
//5、关闭线程池(可关可不关)
pool.shutdown();
}
}
public class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
Thread thread = Thread.currentThread();
thread.sleep(1000);
int i = new Random().nextInt(100);
System.out.println("线程:"+thread.getName()+"生成的随机数为:"+i);
return i;
}
}
答案:
public class Test02 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService es = Executors.newSingleThreadExecutor();
//创建的是Callable对象
Future<Integer> f = es.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
//求和并返回结果
int sum = 0;
for(int i = 1; i <= 100; i++){
sum += i;
}
return sum;
}
});
//获取结果
System.out.println(f.get());
es.shutdown();
}
}
public class MyLock {
public static final Object lockA = new Object();
public static final Object lockB = new Object();
}
public class ThreadTask implements Runnable {
int x = new Random().nextInt(1);//0,1
//指定线程要执行的任务代码
@Override
public void run() {
while(true){
if (x%2 ==0) {
//情况一
synchronized (MyLock.lockA) {
System.out.println("if-LockA");
synchronized (MyLock.lockB) {
System.out.println("if-LockB");
System.out.println("if大口吃肉");
}
}
} else {
//情况二
synchronized (MyLock.lockB) {
System.out.println("else-LockB");
synchronized (MyLock.lockA) {
System.out.println("else-LockA");
System.out.println("else大口吃肉");
}
}
}
x++;
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
//创建线程任务类对象
ThreadTask task = new ThreadTask();
//创建两个线程
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
//启动线程
t1.start();
t2.start();
}
}
答案:
public class Phone {
private String bland; //型号
private String color; //颜色
private boolean isNewPhone = false;//是否有新手机
//购买手机
public synchronized void get(){
//判断是否有新手机
if (!this.isNewPhone) {
//进来了,代表没有新手机
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//代码执行到了这里, 说明此刻有新手机
//购买手机
System.out.println("购买了 :" + this.bland + "..." + this.color);
//更新新 手机的状态
this.isNewPhone = false;
//通知生产商, 没有新手机了, 要生产新手机
this.notify();
}
//生产手机
public synchronized void set(String bland, String color){
//判断是否有新手机
if (this.isNewPhone) {
//进来了,代表有手机
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果走到了这个位置, 说明当前没有新手机
//生产一部新手机
this.bland = bland;
this.color = color;
System.out.println("生产了:" + this.bland + "---" + this.color);
//更新手机的状态为 有新手机
this.isNewPhone = true;
//通知消费者,有新手机了,可以购买
this.notify();
}
}
/*
* 生产商
*/
public class SetPhone implements Runnable {
private Phone phone;
int num = 0;
public SetPhone(Phone phone){
this.phone = phone;
}
@Override
public void run() {
//生产手机
while(true){
//生产一部新手机
if (num%2==0) {
phone.set("Iphone6 Plus", "土豪金");
} else {
phone.set("金立语音王", "红色");
}
num++;
}
}
}
/*
* 消费者类(顾客)
*/
public class GetPhone implements Runnable {
private Phone phone;
public GetPhone(Phone phone){
this.phone = phone;
}
@Override
public void run() {
while(true){
phone.get();
}
}
}
/*
* 测试类
*/
public class PhoneTest {
public static void main(String[] args) {
//定义一个手机对象
Phone phone = new Phone();
SetPhone setPhone = new SetPhone(phone);
GetPhone getPhone = new GetPhone(phone);
//创建线程对象
Thread setThread = new Thread(setPhone);
Thread getThread = new Thread(getPhone);
//启动线程
setThread.start();
getThread.start();
}
}
每个人相当于一条线程(需要给三个命名),每次抢到的红包随机为1-10元,要求每人每次只能抢一个红包,抢红包的过程中需要睡眠300毫秒,并且抢完的人还可以继续参与抢红包.抢到红包后在控制台打印输出”XXX线程抢到第X个红包,红包金额为X元,还剩余X个红包...”,另外在所有红包抢完后提示”红包已抢完”,程序结束.(要求使用Thread类和Runnable中的一种方式去实现).