Java多线程的练习

线程类的部分方法

有关方法1

void start() : 启动线程并执行run()
run() : 线程被调度时执行的操作
String getName(): 放回当前线程的名字
void setName(): 设置当前线程的名字
static Thread currentThread(): 返回当前线程, 在Thread子类中就是 this, 通常用于主线程和Runnable实现类

有关方法而2

static void yield() : 线程让步, 暂停当前正在执行的线程,把执行的机会让给优先级相同或者更高的线程, 若队列里没有优先级更高的线程,忽略此方法
join() : 阻塞当前线程,join()其他线程,当加入的线程执行完后,此线程才会继续
static void sleep(long millis) : (指定时间毫秒): 是令当前的活动线程再指定时间内放弃对cup的控制,使其他的线程有机会被执行,时间到后重新排队;抛出InterruptedException异常

创建线程

第一种创建线程的方式 (继承Thread)

/**

  • 多线程的创建, 方式一 继承于 Thread类
    1. 创建一个继承于 Thread的子类
    1. 重写Thread类的run()
    1. 创建继承于Thread类的子类的对象
    1. 通过此对象调用 start()
      */

创建线程类

// 1. 创建一个继承于 Thread的子类
class  MyThread extends Thread {
    String flag;

    public MyThread(String flag) {
        this.flag = flag;
    }

    // 2. 重写Thread类的run()
    @Override
    public void run() {
        for (int i=0; i<100; i++) {
            if(i%2==0) {
                System.out.println(i + this.flag + Thread.currentThread().getName());
            }
        }
    }
}

使用线程

public class Thread01 {
    public static void main(String[] args) {

        // 3. 创建继承于Thread类的子类的对象
        MyThread m1 = new MyThread("*****线程1*****");
        // 4. 通过此对象调用  start()  ①: 启动当前线程  ②: 调用当前线程的run()
        m1.start();
    }
}

使用匿名类创建 线程

new Thread() {
            @Override
            public void run() {
                for (int i=0; i<100; i++) {
                    System.out.println(i + Thread.currentThread().getName());
                }
            }
        }.start();

第二种创建线程的方式 (实现 Runnable)

    1. 创建一个实现 Runnable接口的类
    1. 实现类去实现抽象方法 run()
    1. 创建实现类对象
    1. 将此对象作为参数传递到Thread类的构造器中, 创建Thread的对象
    1. 通过Thread类的对象调用start() 方法

代码实现

package jzq.com.Thread03;
/**
 * 1. 创建一个实现  Runnable接口的类
 * 2. 实现类去实现抽象方法 run()
 * 3. 创建实现类对象
 * 4. 将此对象作为参数传递到Thread类的构造器中, 创建Thread的对象
 * 5. 通过Thread类的对象调用start() 方法
 */


public class Thread03 {
    public static void main(String[] args) {
        // 3. 创建实现类对象
        MThread mm = new MThread();
        //  4. 将此对象作为参数传递到Thread类的构造器中, 创建Thread的对象
        Thread t1 = new Thread();

        // 5. 通过Thread类的对象调用start() 方法
        t1.start();
    }

}



//1. 创建一个实现  Runnable接口的类
class MThread implements Runnable {
    //2. 实现类去实现抽象方法 run()
    @Override
    public void run() {
        for (int i=100; i<100; i++) {
            System.out.println(i);
        }
    }
}

分析两种线程的创建

开发中优先选择实现 Runnable接口的方式
原因:
1.实现的方式没有类的单继承的局限性
2.实现的方式更合适处理有共享数据的情况

第三种创建线程(实现Callable接口)

此创建 方法 可以 有返回值(object类型),通过重写 call 。可以抛出异常。
如果想要接收返回值,需要借助FutureTask, 通过此对象的get() 方法获取。
需要通过 new Thread(futureTask).start() 将线程跑起来。

代码如下:

package jzq.com.Thread11;

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

public class THread11 {
    public static void main(String[] args) {
        MyThread mm = new MyThread();

        // 获取该线程的返回值 (通过 get() )
        FutureTask futureTask = new FutureTask(mm);

        // 启动线程
        new Thread(futureTask).start();
        try {
            Object sum = futureTask.get();
            System.out.println(sum);
        }catch (InterruptedException e) {
            e.printStackTrace();
        }catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}



class MyThread implements Callable {
    @Override
    public Object call() throws Exception {
        int sum = 0;
        for (int i = 0; i <= 100; i++) {
            if(i % 2 == 0) {
                sum += i;
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
        return sum;
    }
}


线程池的使用 (ExecutorService)

创建线程池: ExecutorService service = Executors.newFixedThreadPool(10);
线程池跑 线程:
service.execute(new MThread2());// execute 适合于 Runnable
service.submit(futureTask); // submit 适合 Callable
关闭线程池:
service.shutdown()

测试代码如下:

package jzq.com.Thread12;

import java.util.concurrent.*;

public class Thread13 {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(10);

        //  execute 适合于  Runnable
        service.execute(new MThread2());
        service.execute(new MThread2());

        // 接收返回值
        MyThread1 mm = new MyThread1();
        FutureTask futureTask = new FutureTask(mm);


        // submit 适合 Callable
        service.submit(futureTask);
        service.submit(new MyThread1());

    }
}



class MyThread1 implements Callable {
    @Override
    public Object call() throws Exception {
        int sum = 0;
        for (int i = 0; i <= 100; i++) {
            if(i % 2 == 0) {
                sum += i;
                System.out.println(Thread.currentThread().getName() + ":(Callable)" + i);
            }
        }
        return sum;
    }
}

class MThread2 implements Runnable {
    //2. 实现类去实现抽象方法 run()
    @Override
    public void run() {
        for (int i=0; i<100; i++) {
            System.out.println(Thread.currentThread().getName() + ":(Runnable)" + i);
        }
    }
}

线程的优先级

优先级

MAX_PRIORITY: 10
MIN_PRIORITY: 1
NPRMPRIORITY: 5 (默认优先级)

设置以及获取优先级的方法

  1. getpriority() : 获取当前线程的优先级
  2. setpriority(int p) : 设置当前线程的优先级

线程的生命周期

图解线程的生命周期(参考尚硅谷视频)

在这里插入图片描述

线程的同步

同步的优缺点

优点

解决了线程的安全性问题

缺点

操作同步代码的时候,只能有一个线程参与,其他线程等待,相当于一个线程操作,效率低。有局限性!

同步代码块

synchronized (同步监视器) {// 需要被同步的代码}
说明:

  1. 操作共享数据的代码,即为需要被同步的代码,测试的时候如果拿循环来测试,一定要在循环体内, 不然就成为单线程了
  2. 共享数据, 多个线程共同操作的变量
  3. 同步监视器, 俗称:锁。 任何一个类的对象都可以叫锁。 但是这个吧锁要是唯一的,不然会出现线程的安全性问题。

代码如下:

package jzq.com.Thread04;

public class Thread04 {
    public static void main(String[] args) {
        MyThread mm = new MyThread();
        Thread t1 = new Thread(mm);
        Thread t2 = new Thread(mm);

        t1.start();
        t2.start();
    }

}



class MyThread implements Runnable {
    private int s = 100;
    Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            synchronized(MyThread.class) {
                if (s<0) {
                    break;
                }else {
                    try {
                        Thread.sleep(100);
                    }catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"当前售票" + s);
                    s--;
                }
            }
        }

    }
}

同步方法

将同步的代码封装到方法,此方法被synchronized 所修饰

代码如下:

public synchronized void show() {
        if (s>0) {
            try {
                Thread.sleep(100);
            }catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"当前售票" + s);
            s--;
        }
    }

当使用 继承Thread的线程实现抢票功能时注意问题

变量值

注意值一定要是静态的,不然就是三个线程使用的不同的资源。

使用同步代码块或者同步方法的时需要注意

同步代码块,所添加的锁(同步监视器),一定是一个对象,可以是静态的对象或者class
同步方法一定要声明静态,不然自动获取的锁不是唯一的,也会出现安全性问题。

代码如下

package jzq.com.Thread05;

public class Thread05 {
    public static void main(String[] args) {
        MyThread m1 = new MyThread();
        MyThread m2 = new MyThread();
        MyThread m3 = new MyThread();


        m1.start();
        m2.start();
        m3.start();

    }
}


class MyThread extends Thread {
    private static int s = 100;

    @Override
    public void run() {
        while (true) {
//            synchronized (MyThread.class) {
//                if(s>0) {
//                    try {
//                        Thread.sleep(100);
//                    }catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
//
//                    System.out.println(Thread.currentThread().getName() + "抢票中---" + s);
//                    s--;
//                }
//            }
            show();
        }
    }

    public synchronized static void show() {
        if(s>0) {
            try {
                Thread.sleep(100);
            }catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + "抢票中---" + s);
            s--;
        }
    }
}

使用 ReentrantLock 线程锁解决同步带来的安全性问题

创建 一个ReentrantLock 对象,通过 此对象调用lock():锁定方法 与 unlock(): 解锁方法进行控制,开锁后的同步代码,需要在执行完手动解锁!

代码示例

package Thread08;

import java.util.concurrent.locks.ReentrantLock;

public class Thread08 {
    public static void main(String[] args) {
        MyThread mm = new MyThread();

        Thread t1 = new Thread(mm);
        Thread t2 = new Thread(mm);


        t1.start();
        t2.start();
    }
}


class MyThread implements Runnable {
    private int s = 100;

    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {

            try {
                lock.lock(); //调用 锁定方法 lock()
                if(s>0) {
                    try {
                        Thread.sleep(100);
                        System.out.println(Thread.currentThread().getName() + ':' + s);
                        s--;
                    }catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }finally {
                lock.unlock(); // 调用解锁方法
            }
        }
    }
}

线程同步出现死锁问题

什么叫死锁?
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
出现死锁后,不会出现异常,不会出现提示,知识所有的线程都处于阻塞状态,无法继续
如何解决死锁问题?
尽量减少同步资源的定义
尽量避免嵌套同步

线程通信问题

使用线程通信完成两个线程之间交替

通过调用 wait()方法 与 notify()或者notifyall()

函数解释:
wait(): 一旦执行此方法, 当前线程就会进入阻塞状态,并释放同步监视器
notify(): 一旦执行此方法, 就会唤醒wait的一个线程, 如果多个线程被wait,就唤醒优先级高的。
notifyall(): 一旦执行此方法,就会唤醒所有被wait的线程。

说明:
这三个方法必须使用在同步代码块,或者同步方法中。
这三个的调用者必须是同步方法或者同步代码块中的同步监视器。 否则会出现IllegalMonitorStateException异常
这三个方法都是定义在java.lang.Object类中。

代码:

package jzq.com.Thread10;

public class Thread10 {
    public static void main(String[] args) {
        MyThread mm = new MyThread();

        Thread t1 = new Thread(mm);
        Thread t2 = new Thread(mm);


        t1.start();
        t2.start();

    }
}


class MyThread implements Runnable {

    private int s = 0;
    private Object obj = new Object();

    @Override
    public void run() {

        while (true) {
            synchronized (obj) {

                obj.notify();

                if(s<100) {
                    try {
                        Thread.sleep(100);
                        System.out.println(Thread.currentThread().getName() + ":" + s);
                        s++;
                    }catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else {
                    break;
                }

                try {
                    obj.wait();
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值