JavaSE——多线程编程

在 Java 中实现多线程有两种手段,一种是继承 Thread 类,另一种就是实现 Runnable 接口。

一、线程基本操作:

1、多线程的两种实现方法

1、继承Thread类
2、实现Runnable接口

相比继承Thread类,实现Runnable接口的好处:
1、避免了java单继承的局限性
2、适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据分离,较好的体现了面向对象的设计思想

2、线程的基本操作

Thread.currentThread().getName()
取得当前线程的名称

new Thread(myRunnable,“线程名”)

setName(“线程名”)

设置线程的名字
setDaemon()

后台线程

在 Java 程序中,只要前台有一个线程在运行,则整个 Java 进程都不会消失,所以此时可以设置一个后台线程,这样即使 Java 线程结束了,此后台线程依然会继续执行,要想实现这样的操作,直接使用 setDaemon() 方法即可。

sleep(毫秒数) 线程休眠
interrupt()

线程中断

当一个线程运行时,另外一个线程可以直接通过interrupt()方法中断其运行状态。

setPriority(优先级)

设置线程优先级

MIN_PRIORITY(优先级最低)

MAX_PRIORITY(优先级最高)

NORM_PRIORITY(优先级中等)

3、线程中断测试

当一个线程运行时,另外一个线程可以直接通过interrupt()方法中断其运行状态。

class MyThread1 implements Runnable{ // 实现Runnable接口
    public void run(){  // 覆写run()方法
        System.out.println("1、进入run()方法") ;
        try{
            Thread.sleep(10000) ;   // 线程休眠10秒
            System.out.println("2、已经完成了休眠") ;
        }catch(InterruptedException e){
            System.out.println("3、休眠被终止") ;
            return ; // 返回调用处
        }
        System.out.println("4、run()方法正常结束") ;
    }
};
public class MyTest{
    public static void main(String args[]){
        MyThread1 mt = new MyThread1() ;  // 实例化Runnable子类对象
        Thread t = new Thread(mt,"线程");     // 实例化Thread对象
        t.start() ; // 启动线程
        try{
            Thread.sleep(2000) ;    // 线程休眠2秒
        }catch(InterruptedException e){
            System.out.println("3、休眠被终止") ;
        }
        t.interrupt() ; // 中断线程执行
    }
};

二、数据不同步问题解决

测试类中,有一个静态成员a,定义2000个子线程,分别执行对静态变量a的++操作,然后在主线程中输出累加后的a值

MyRunnable:

public class MyRunnable implements Runnable{
    Object lock = new Object();
    @Override
    public void run() {
        synchronized (lock) {
            MyTest.a++;
        }
    }
}

MyTest:

public class MyTest {
    public volatile static int a = 0;
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        for (int i = 0; i < 2000; i++) {
            Thread t = new Thread(myRunnable);
            t.start();
        }

        System.out.println("a = " + a);
    }
}

以上代码,在输出时,出现数据不同步,理论上该输出2000,结果输出一个未知大小的值,问题分析:

在子线程执行过程中,主线程也参与进来,导致主线程过早的从内存中读取了a的值,导致a的值输出错误

解决办法1:

使用延时函数,延迟主函数的执行

public class MyTest {
    public static int a = 0;
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        for (int i = 0; i < 2000; i++) {
            Thread t = new Thread(myRunnable);
            t.start();
        }

        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(a);
    }
}

解决办法2:

使用volatile关键字对变量a进行修饰,强制每次从内存中读取a的值

public class MyTest {
    public volatile static int a = 0;
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        for (int i = 0; i < 2000; i++) {
            Thread t = new Thread(myRunnable);
            t.start();
        }

        while(true) {
            if(a == 2000) {
                break;
            }
        }
        System.out.println(a);
    }
}

解决办法3:

使用闭锁,2000个线程分别配备一把锁,等待所有的锁释放后再进行主线程输出操作

MyTest:

public class MyTest {
    public static int a = 0;
    //增加2000把闭锁
    public static CountDownLatch countDownLatch = new CountDownLatch(2000);
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        for (int i = 0; i < 2000; i++) {
            Thread t = new Thread(myRunnable);
            t.start();
        }

        try {
            //等待所有的闭锁释放完毕
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(a);
    }
}

MyRunnable:

public class MyRunnable implements Runnable{
    Object lock = new Object();
    @Override
    public void run() {
        synchronized (lock) {
            MyTest.a++;
            //释放该线程的闭锁
            MyTest.countDownLatch.countDown();
        }
    }
}

三、卖票案例

SellTicket:

public class SellTicket implements Runnable {
    private int tickets = 100;
    //任意定义一个引用对象,充当锁旗标,单次只允许一个对象拿到该锁
    private Object lock = new Object();
    @Override
    public void run() {
        while(tickets > 0) {
            synchronized (lock){
                if(tickets > 0) {
                    System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
                    tickets--;
                }
            }
        }
    }
}

SellTicketDemo:

public class SellTicketDemo {
    public static void main(String[] args) {
        SellTicket st = new SellTicket();

        //定义三个Thread对象,把SellTicket对象作为构造方法参数,并给出窗口名字
        Thread t1 = new Thread(st, "窗口1");
        Thread t2 = new Thread(st, "窗口2");
        Thread t3 = new Thread(st, "窗口3");

        //启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_42067873/article/details/113627049