Java学习(9)—— 多线程

一.   多线程的创建

1. 方式一:继承Thread类

(1)创建一个继承于Thread类的子类;

(2)重写Thread类的run():将此线程要执行的操作声明在run()中;

(3)创建子类对象;

(4)通过此对象调用start():启动当前线程,调用当前线程的run();

注意:不能直接调用run()来启动线程;如果想再启动一个线程来执行该任务,需要再创建一个对象来调用start();如果想同时启动多个不同任务的线程,需要创建多个任务的子类,再分别创建对应的对象来调用start()。

public class Test{
    public static void main(String[] args) {
        MyThread1 thread = new MyThread1();
        thread.start();

        MyThread1 thread1 = new MyThread1();
        thread1.start();

        MyThread2 thread2 = new MyThread2();
        thread2.start();

    }

}

class MyThread1 extends Thread{
    @Override
    public void run() {
        for(int i = 0; i <= 100; i++){
            if(i % 2 == 0){
                System.out.println("偶数" + i);
            }
        }
    }
}

class MyThread2 extends Thread{
    @Override
    public void run() {
        for(int i = 0; i <= 100; i++){
            if(i % 2 != 0){
                System.out.println("奇数" + i);
            }
        }
    }
}
//使用匿名子类方式启动多线程
public class Test{
    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run() {
                for(int i = 0; i <= 100; i++){
                    if(i % 2 == 0){
                        System.out.println("偶数" + i);
                    }
                }
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                for(int i = 0; i <= 100; i++){
                    if(i % 2 != 0){
                        System.out.println("奇数" + i);
                    }
                }
            }
        }.start();
        
    }
}

2. 方式二:实现Runnable接口(通常优先考虑使用这种方式)

(1)创建一个实现了Runnable接口的类;

(2)实现类去重写Runnable中的抽象方法:run();

(3)创建实现类的对象;

(4)创建Thread类的对象,将实现类的对象作为参数传递到Thread类的构造器中;

(5)通过Thread类的对象调用start()。

public class Test{
    public static void main(String[] args) {
        MyThread mythread = new MyThread();     //3:创建实现类的对象
        Thread thread = new Thread(mythread);   //4:创建Thread类的对象,将实现类的对象作为参数传递到Thread类的构造器中
        thread.start();                         //5:通过Thread类的对象调用start()

        Thread thread2 = new Thread(mythread);   //启动另一个线程
        thread2.start();
    }
}

class MyThread implements Runnable{         //1:创建一个实现了Runnable接口的类
    @Override
    public void run() {                     //2:实现类去重写Runnable中的抽象方法
        for(int i = 0; i <= 100; i++){
            if(i % 2 == 0){
                System.out.println(i);
            }
        }
    }
}
//使用匿名子类方式启动多线程
public class Test{
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i <= 100; i++){
                    if(i % 2 == 0){
                        System.out.println("偶数" + i);
                    }
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i <= 100; i++){
                    if(i % 2 != 0){
                        System.out.println("奇数" + i);
                    }
                }
            }
        }).start();

    }
}

3. Thread类中的方法:

(1)start():启动当前线程,调用当前线程的run();

(2)run():通常需要重写此方法,将该线程要执行的操作声明在此方法中;

(3)currentThread():静态方法,返回执行当前代码的线程;

(4)getName():获取当前线程的名字;

(5)setName():设置当前线程的名字;

public class Test{
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.setName("线程1");
        thread.start();

        Thread.currentThread().setName("主线程");
        for(int i = 0; i <= 100; i++){
            if(i % 2 != 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

class MyThread extends Thread{
    @Override
    public void run() {
        for(int i = 0; i <= 100; i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

            (6)yield():释放当前线程CPU的执行权;

            (7)join():在线程A中调用线程B的join(),此时线程A进入阻塞状态,直到线程B全部执行结束后,线程A才解除阻塞状态;

            (8)stop():强制结束当前线程;

            (9)sleep():让当前线程 “睡眠” 指定时间,在睡眠期间,该线程是阻塞状态;

           (10)isalive():判断当前线程是否存活。     

4. 线程的优先级:

    优先级分为:MAX_PRIORITY:10    MIN_PRIORITY:1    NORM_PRIORITY:5  

    获取线程的优先级:getPriority()

    设置线程的优先级:setPriority(int p)

     注意:高优先级的线程要抢占低优先级线程CPU的执行权,高优先级的线程以高概率优先执行,但是不意味着只有高优先级的线程全部执行完之后,低优先级的线程才执行。

public class Test{
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.setName("线程1");
        thread.setPriority(Thread.MAX_PRIORITY);
        thread.start();

        Thread.currentThread().setName("主线程");
        for(int i = 0; i <= 100; i++){
            if(i % 2 != 0){
                System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority());
            }
        }
    }
}

class MyThread extends Thread{
    @Override
    public void run() {
        for(int i = 0; i <= 100; i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority());
            }
        }
    }
}

二.   线程同步

       例:卖票 —— 创建三个售票窗口,总票数为100张

public class Test{
    public static void main(String[] args) {
        Window window1 = new Window();
        Window window2 = new Window();
        Window window3 = new Window();

        window1.setName("窗口一");
        window2.setName("窗口二");
        window3.setName("窗口三");

        window1.start();
        window2.start();
        window3.start();
    }
}

class Window extends Thread{
    private static int ticket = 100;

    @Override
    public void run() {
        while(true){
            if(ticket > 0){
                System.out.println(Thread.currentThread().getName() + ",票号为:" + ticket);
                ticket--;
            }else{
                break;
            }
        }
    }
}
public class Test{
    public static void main(String[] args) {
        Window window = new Window();

        Thread thread1 = new Thread(window);
        Thread thread2 = new Thread(window);
        Thread thread3 = new Thread(window);

        thread1.setName("窗口一");
        thread2.setName("窗口二");
        thread3.setName("窗口三");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

class Window implements Runnable{
    private int ticket = 100;
    @Override
    public void run() {
        while(true){
            if(ticket > 0){
                System.out.println(Thread.currentThread().getName() + ",票号为:" + ticket);
                ticket--;
            }else{
                break;
            }
        }

    }
}

        注意:上述例子多个线程共同操作同一个数据,会存在线程的安全问题

        解决方法:当线程A在操作ticket时,其他线程不能参与进来,直到线程A操作完ticket时(即使线程A出现了阻塞),其他线程才可以操作ticket。

1. 同步代码块

            synchronized(同步监视器){

                        多个线程需要同步的代码

            } 

            说明:(1)同步的代码是指操作共享数据的代码,共享的数据是指多个线程共同操作的变量;

                       (2)任何一个类的对象都可以充当同步监视器(锁),所有线程必须共用一把锁。

            注意:该方法在操作同步代码块时,实际上是单线程,只有一个线程参与,其他线程等待。

public class Test{
    public static void main(String[] args) {
        Window window = new Window();

        Thread thread1 = new Thread(window);
        Thread thread2 = new Thread(window);
        Thread thread3 = new Thread(window);

        thread1.setName("窗口一");
        thread2.setName("窗口二");
        thread3.setName("窗口三");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

class Window implements Runnable{
    private int ticket = 100;
    Object object = new Object();

    @Override
    public void run() {
        while(true){
            synchronized(object){
                if(ticket > 0){
                    System.out.println(Thread.currentThread().getName() + ",票号为:" + ticket);
                    ticket--;
                }else{
                    break;
                }
            }
        }
    }
}
public class Test{
    public static void main(String[] args) {
        Window window = new Window();

        Thread thread1 = new Thread(window);
        Thread thread2 = new Thread(window);
        Thread thread3 = new Thread(window);

        thread1.setName("窗口一");
        thread2.setName("窗口二");
        thread3.setName("窗口三");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

class Window implements Runnable{
    private int ticket = 100;

    @Override
    public void run() {
        while(true){
            synchronized(this){    //this指代创建的唯一对象window
                if(ticket > 0){
                    System.out.println(Thread.currentThread().getName() + ",票号为:" + ticket);
                    ticket--;
                }else{
                    break;
                }
            }
        }
    }
}
public class Test{
    public static void main(String[] args) {
        Window window1 = new Window();
        Window window2 = new Window();
        Window window3 = new Window();

        window1.setName("窗口一");
        window2.setName("窗口二");
        window3.setName("窗口三");

        window1.start();
        window2.start();
        window3.start();
    }
}

class Window extends Thread{
    private static int ticket = 100;
    static Object object = new Object();

    @Override
    public void run() {
        while(true){
            //synchronized(this){  错误,这里的this指代三个对象
            synchronized(object){
                if(ticket > 0){
                    System.out.println(Thread.currentThread().getName() + ",票号为:" + ticket);
                    ticket--;
                }else{
                    break;
                }
            }
        }
    }
}
public class Test{
    public static void main(String[] args) {
        Window window1 = new Window();
        Window window2 = new Window();
        Window window3 = new Window();

        window1.setName("窗口一");
        window2.setName("窗口二");
        window3.setName("窗口三");

        window1.start();
        window2.start();
        window3.start();
    }
}

class Window extends Thread{
    private static int ticket = 100;

    @Override
    public void run() {
        while(true){
            //synchronized(this){  错误,这里的this指代三个对象
            synchronized(Window.class){
                if(ticket > 0){
                    System.out.println(Thread.currentThread().getName() + ",票号为:" + ticket);
                    ticket--;
                }else{
                    break;
                }
            }
        }
    }
}

2.同步方法

           同步方法仍然涉及到锁,只是不用显示的声明,非静态的同步方法中锁是this,静态的同步方法中锁是当前类本身。

public class Test{
    public static void main(String[] args) {
        Window window = new Window();

        Thread thread1 = new Thread(window);
        Thread thread2 = new Thread(window);
        Thread thread3 = new Thread(window);

        thread1.setName("窗口一");
        thread2.setName("窗口二");
        thread3.setName("窗口三");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

class Window implements Runnable{
    private int ticket = 100;

    @Override
    public void run() {
        while(true){
            show();
        }
    }

    public synchronized void show(){
            if(ticket > 0){
                System.out.println(Thread.currentThread().getName() + ",票号为:" + ticket);
                ticket--;
            }
        }
}
public class Test{
    public static void main(String[] args) {
        Window window1 = new Window();
        Window window2 = new Window();
        Window window3 = new Window();

        window1.setName("窗口一");
        window2.setName("窗口二");
        window3.setName("窗口三");

        window1.start();
        window2.start();
        window3.start();
    }
}

class Window extends Thread{
    private static int ticket = 100;
    static Object object = new Object();

    @Override
    public void run() {
        while(true){
            show();
        }
    }

    //错误:public synchronized void show(){
    public static synchronized void show(){
        if(ticket > 0){
            System.out.println(Thread.currentThread().getName() + ",票号为:" + ticket);
            ticket--;
        }
    }
}

3.使用Lock锁

import java.util.concurrent.locks.ReentrantLock;

public class Test{
    public static void main(String[] args) {
        Window window = new Window();

        Thread thread1 = new Thread(window);
        Thread thread2 = new Thread(window);
        Thread thread3 = new Thread(window);

        thread1.setName("窗口一");
        thread2.setName("窗口二");
        thread3.setName("窗口三");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

class Window implements Runnable{
    private int ticket = 100;
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while(true){
            try{
                lock.lock();
                if(ticket > 0){
                    System.out.println(Thread.currentThread().getName() + ",票号为:" + ticket);
                    ticket--;
                }else{
                    break;
                }
            }finally {
                lock.unlock();
            }

        }
    }
}

         synchronized与Lock的异同:(1)相同:都用来解决线程安全问题;(2)不同:synchronized在执行完同步代码之后自动解开锁,Lock需要手动启动同步(lock()),结束同步时需要手动解锁(unlock())。

三.   线程通信

        例子:使用两个线程交替打印1-100

public class Test{
    public static void main(String[] args) {
        Printer printer = new Printer();

        Thread thread1 = new Thread(printer);
        Thread thread2 = new Thread(printer);

        thread1.setName("打印一");
        thread2.setName("打印二");

        thread1.start();
        thread2.start();
    }
}

class Printer implements Runnable{
    private int number = 1;

    @Override
    public void run() {
        while(true){
            synchronized (this){
                notify();   //唤醒被阻塞的线程       省略的是this.

                if(number <= 100){
                    System.out.println(Thread.currentThread().getName() + "," + number);
                    number++;

                    try{ 
                        wait();   //调用wait方法使线程进入阻塞状态        省略的是this.
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }

                }else{
                    break;
                }
            }
        }
    }
}

         wait():一旦执行此方法,当前线程进入阻塞状态,释放锁

         notify():一旦执行此方法,就会唤醒被wait()的一个线程,如果多个线程被wait(),就唤醒优先级高的那个。

         notify():一旦执行此方法,就会唤醒所有被wait()的线程。

         注意:wait()、notify()、notify()只能使用在同步代码块或同步方法中,且只能被同步监视器调用。

         sleep与wait的不同:(1)sleep()声明在Thread类中,wait()声明在Object类中;(2)sleep()可以在任何情况下调用,wait()只能使用在同步代码块或同步方法中;(3)如果两个方法都使用在同步代码块或同步方法,sleep()不会释放同步监视器,wait()会释放同步监视器。

四.   创建多线程的两种新方式

1. 新方式一:实现Callable接口

          (1)创建一个实现了Callable接口的类;

          (2)实现类去重写Callable中的call()方法,将此线程需要执行的操作声明在call()中,此方法可以有返回值;

          (3)创建Callable接口实现类的对象;

          (4)创建FutureTask类的对象,将实现类的对象作为参数传递到FutureTask类的构造器中;

          (5)创建Thread类的对象,将FutureTask类的对象作为参数传递到Thread类的构造器中;

          (6)通过Thread类的对象调用start()。

          (7)可以使用get()方法获取Callable中call()方法的返回值

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

public class Test{
    public static void main(String[] args) {
        MyThread mythread = new MyThread();
        FutureTask futureTask = new FutureTask(mythread);
        Thread thread = new Thread(futureTask);
        thread.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){
                System.out.println(i);
                sum+= i;
            }
        }
        return sum;
    }
}

2. 新方式二:使用线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test{
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);  //创建一个固定线程数量的线程池

        MyThread1 mythread1 = new MyThread1();
        executorService.execute(mythread1);   //用于实现Runnable接口
        //executorService.submit();   用于实现Callable接口

        MyThread2 mythread2 = new MyThread2();
        executorService.execute(mythread2);

        executorService.shutdown();    //关闭线程池

    }
}

class MyThread1 implements Runnable {
    @Override
    public void run(){
        for(int i = 0; i <= 100; i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + "," + i);
            }
        }
    }
}

class MyThread2 implements Runnable {
    @Override
    public void run(){
        for(int i = 0; i <= 100; i++){
            if(i % 2 != 0){
                System.out.println(Thread.currentThread().getName() + "," + i);
            }
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值