实现多线程的方式
- 继承Thread类
- 实现Runnable接口
- 实现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
控制线程的同步执行
- 题目:有
A
,B
,C
三个线程,,A
线程输出A
,B
线程输出B
,C
线程输出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:计数器减一,计数为零时,释放所有在等待的线程