【JavaSE】《基础篇006》多线程

实现多线程的方式

  1. 继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口,线程池

1. 继承Thread类

编写一个线程类,并重写 run() 方法
外部实例化线程,并调用 start() 方法启动前程,执行线程体内的方法

  • setName :设置线程名
  • setPriority :置优先级
  • Thread.sleep :让线程暂停执行
  • join:等待这个线程死亡
  • wait :阻塞该线程
  • notify :唤醒该线程
public class MyThread extends Thread{
    @Override
    public void run() {
       for (int i = 1; i <= 30 ;i++){

          // String name = this.getName();
           String name = Thread.currentThread().getName();
           System.out.println(name + ": " + i);
       }
    }
}


public class MyThreadTest {
    public static void main(String[] args) throws InterruptedException {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();

        thread1.setName("thread1");//设置线程名词
        thread1.setPriority(1);//设置线程的优先级
        thread2.setName("thread2");
        thread2.setPriority(10);
        
        Thread.sleep(3000);//设置线程暂停的毫秒数
        thread1.start();//启动线程
        thread2.start();
    }
}

2. 实现Runnable接口

Runnable类是任务类,需要一个线程来承载任务,通过new Thread(new Runnable()).start()来运行任务。
好处是线程体类可以继承其他父类,不受限制。

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}


public class MyRunnableTest {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();

        Thread thread1 = new Thread(myRunnable,"thread1");
        Thread thread2 = new Thread(myRunnable,"thread2");

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

线程加锁同步的方式

1. ReentrantLock

我们知道synchronized是Java语言层面提供的语法,可以对方法和代码块加锁,控制只能一个线程进入同步区域。

顾名思义,ReentrantLock是可重入锁,它和synchronized一样,一个线程可以多次获取同一个锁。

ReentrantLock,它是Java代码实现的锁,我们可以手动调用lock.lock()获取锁,然后在finally中调用 lock.unlock()正确释放锁。

//计数器
public class Counter {
    private final Lock lock = new ReentrantLock();
    private int count;

    public void add(int n) {
        lock.lock(); //获得锁
        try {
            count += n;
        } finally {
            lock.unlock(); //释放锁
        }
    }
}

笔试题:使用ReentrantLock控制线程的同步执行

  • 题目:有ABC三个线程,,A线程输出AB线程输出BC线程输出C,要求,同时启动三个线程,,按顺序输出ABC,循环10次。

public class ABCRunnable implements Runnable {
    private Lock lock;      //锁对象
    private String name;    //线程名字
    private int flag;       //逻辑取模运算

    public static int count = 0;    //计数(0:A, 1:B  2:C)
    public static final int MAX = 30;   //线程执行30次

    public ABCRunnable(String name, Lock lock, int flag) {
        this.lock = lock;
        this.name = name;
        this.flag = flag;
    }

    @Override
    public void run() {

        while (true) {
            try {
                if (lock.tryLock(1, TimeUnit.SECONDS)) {
                    //尝试获取锁的时候,最多等待1秒。如果1秒后仍未获取到锁,tryLock()返回false,程序就可以做一些额外处理,而不是无限等待下去。
                    if (count >= MAX) {
                        lock.unlock();
                        return;
                    }
                    if (count % 3 == flag) {
                        System.out.println(name);
                        count++;
                    }
                    lock.unlock();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}

在这里插入图片描述

2. synchronized

synchronized是一个java关键字,可作用在对象上、Class实例、代码块、方法上。

synchronized与Lock的区别

  • 使用后会自动释放锁,而Lock需要手动unlock
  • synchronized 是非公平锁,即不能保证等待锁线程的顺序
  • synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
  • synchronized是一个悲观锁(synchronznized底层对应两个指令:monitorenter、monitorexit,)
  • Lock是一个乐观锁(底层基于volatile和cas实现)

练习

  • 注意synchronized的作用实例,是对象实例,那锁住的就是对象
  • 如果是Class对象,锁住的就是整个类。
public class Test {
	//公共变量
	int count=0;
	public static void main(String[] args){
		//new一个实现Runnable的类
		Test test=new Test();
		//创建1个任务
		MyRunnable myRunnable1=test.new MyRunnable();
		//创建5个线程
		for(int i=0;i<4;i++){
			new Thread(myRunnable1).start();
		}
	}
	
	
	class MyRunnable implements Runnable{
		public void run() {
			while(true){
				//锁住的是同一对象
				synchronized(this){
					if(count>=1000){
						break;
					}
					System.out.println(Thread.currentThread().getName()+":count:"+(++count));
					//测试时,线程更容易切换
					Thread.yield();
				}
				
			}
		}
		
	}
 
}

由于5个线程都使用的是同一个任务对象,所以想要线程同步,对任务对象加锁即可。每次只允许一个线程使用该任务对象。

public class Test {
	//公共变量
	int count=0;
	public static void main(String[] args){
		//new一个实现Runnable的类
		Test test=new Test();
		//创建5个任务
		MyRunnable myRunnable1=test.new MyRunnable();
		MyRunnable myRunnable2=test.new MyRunnable();
		MyRunnable myRunnable3=test.new MyRunnable();
		MyRunnable myRunnable4=test.new MyRunnable();
		MyRunnable myRunnable5=test.new MyRunnable();
		//创建5个线程
		new Thread(myRunnable1).start();
		new Thread(myRunnable2).start();
		new Thread(myRunnable3).start();
		new Thread(myRunnable4).start();
		new Thread(myRunnable5).start();
	}
	//创建一个实现Runnable的类
	class MyRunnable implements Runnable{
		public void run() {
			while(true){
				//锁住的是整个MyRunnable类
				synchronized(MyRunnable.class){
					if(count>=1000){
						break;
					}
					System.out.println(Thread.currentThread().getName()+":count:"+(++count));
					//测试时,线程更容易切换
					Thread.yield();
				}
				
			}
		}
		
	}
 
}

由于5个线程使用了5个不同的任务对象,要想5个线程同步执行任务(从1数到10000),则需要对任务整个类 MyRunnable.class 加锁。因为MyRunnable.class.this是类加载到静态方法区中,是一直存在不变的。
原文连接:
https://blog.csdn.net/wzmde007/article/details/79641084

3.CountDownLatch

CountDownLatch 是一个非常实用的多线程控制工具类,通常用来控制线程的等待,它可以让某个线程等待直到倒计时结束

CountDownLatch 提供了两个主要的方法,await()、countDown()。

  • await:使当前线程阻塞,等待计数器为 0

  • countDown:计数器减一,计数为零时,释放所有在等待的线程

参考:
https://www.cnblogs.com/zhengbin/p/9223635.html

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_popo_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值