一、实现多线程
1、继承Thread类
方法名 | 说明 |
---|---|
void run() | 在线程开启后,此方法将被调用执行 |
void start() | 使此线程开始执行,Java虚拟机会调用run方法() |
-
run()方法和start()方法的区别?
run()
:封装线程执行的代码,直接调用,相当于普通方法的调用
start()
:启动线程;然后由JVM调用此线程的run()方法 -
实现步骤
- 定义一个类MyThread继承Thread类
- 在MyThread类中重写run()方法
- 创建MyThread类的对象
- 启动线程
//实现多线程
class MyThread extends Thread {
@Override
public void run() {
// 当前线程
String name = Thread.currentThread().getName();
System.out.println(name);
//代码就是线程在开启之后执行的代码
for (int i = 0; i < 3; i++) {
System.out.println("线程开启了" + i);
}
}
}
// 使用
public class Demo {
public static void main(String[] args) {
// 当前线程
String name = Thread.currentThread().getName();
System.out.println(name);
//创建一个线程对象
MyThread t1 = new MyThread();
//表示的仅仅是创建对象,用对象去调用方法,并没有开启线程
t1.run();
//开启一条线程
t1.start();
}
}
2、实现Runnable接口
方法名 | 说明 |
---|---|
Thread(Runnable target) | 分配一个新的Thread对象 |
Thread(Runnable target, String name) | 分配一个新的Thread对象 |
-
实现步骤
- 定义一个类MyRunnable实现Runnable接口
- 在MyRunnable类中重写run()方法
- 创建MyRunnable类的对象
- 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
- 启动线程
// 实现Runnable接口
class MyRunnable implements Runnable{
@Override
public void run() {
// 当前线程
String name = Thread.currentThread().getName();
System.out.println(name);
//线程启动后执行的代码
for (int i = 0; i < 3; i++) {
System.out.println( "Runnable:" + i);
}
}
}
public class Demo {
public static void main(String[] args) {
// 当前线程
String name = Thread.currentThread().getName();
System.out.println(name);
//创建了一个参数的对象
MyRunnable mr = new MyRunnable();
//创建了一个线程对象,并把参数传递给这个线程.
//在线程启动之后,执行的就是参数里面的run方法
Thread t1 = new Thread(mr);
//开启线程
t1.start();
}
}
3.实现Callable接口
方法名 | 说明 |
---|---|
V call() | 计算结果,如果无法计算结果,则抛出一个异常 |
FutureTask(Callable<V> callable) | 创建一个 FutureTask,一旦运行就执行给定的 Callable |
V get() | 如有必要,等待计算完成,然后获取其结果 |
-
实现步骤
- 定义一个类MyCallable实现Callable接口
- 在MyCallable类中重写call()方法
- 创建MyCallable类的对象
- 创建Future的实现类FutureTask对象,把MyCallable对象作为构造方法的参数
- 创建Thread类的对象,把FutureTask对象作为构造方法的参数
- 启动线程
- 再调用get方法,就可以获取线程结束之后的结果。
/ 实现Callable接口
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
// 当前线程
String name = Thread.currentThread().getName();
System.out.println(name);
for (int i = 0; i < 3; i++) {
System.out.println("Callable:" + i);
}
//返回值就表示线程运行完毕之后的结果
return "答应";
}
}
public class Demo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 当前线程
String name = Thread.currentThread().getName();
System.out.println(name);
//线程开启之后需要执行里面的call方法
MyCallable mc = new MyCallable();
//可以获取线程执行完毕之后的结果.也可以作为参数传递给Thread对象
FutureTask<String> ft = new FutureTask<>(mc);
//创建线程对象
Thread t1 = new Thread(ft);
//开启线程
t1.start();
String s = ft.get();
System.out.println(s);
}
}
4.三种实现方式的对比
- 实现Runnable、Callable接口
- 好处: 扩展性强,实现该接口的同时还可以继承其他的类
- 缺点: 编程相对复杂,不能直接使用Thread类中的方法
- 继承Thread类
- 好处: 编程比较简单,可以直接使用Thread类中的方法
- 缺点: 可以扩展性较差,不能再继承其他的类
二、多线程的使用
1.设置和获取线程名称
方法名 | 说明 |
---|---|
void setName(String name) | 将此线程的名称更改为等于参数name |
String getName() | 返回此线程的名称 |
Thread currentThread() | 返回对当前正在执行的线程对象的引用 |
static void sleep(long millis) | 使当前正在执行的线程停留(暂停执行)指定的毫秒数 |
final int getPriority() | 返回此线程的优先级 |
final void setPriority(int newPriority) | 更改此线程的优先级线程默认优先级是5;线程优先级的范围是:1-10 |
void setDaemon(boolean on) | 将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出 |
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 30; i++) {
try {
// 不同线程睡眠时间不同,观察控制台打印
String name = Thread.currentThread().getName();
if (name.equals("1")){
Thread.sleep(10);
}else{
Thread.sleep(20);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
}
public class Demo {
public static void main(String[] args) throws InterruptedException {
MyRunnable mr = new MyRunnable();
//线程是有默认名字的,格式:Thread-编号
// 设置线程名
Thread t1 = new Thread(mr,"1");
Thread t2 = new Thread(mr,"2");
//设置优先级: 1 - 10 默认值:5
t1.setPriority(10);
t2.setPriority(1);
t1.start();
t2.start();
}
}
2.线程锁:synchronized
- 安全问题出现的条件
- 是多线程环境
- 有共享数据
- 有多条语句操作共享数据
- 同步代码块格式:
synchronized(任意对象) {
多条语句操作共享数据的代码
}
synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁
public class MyThread extends Thread {
private static int ticketCount = 100;
private static Object obj = new Object();
@Override
public void run() {
while(true){
synchronized (obj){ //就是当前的线程对象.
if(ticketCount <= 0){
//卖完了
break;
}else{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticketCount--;
System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticketCount + "张票");
}
}
}
}
}
public class Demo {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("窗口一");
t2.setName("窗口二");
t1.start();
t2.start();
}
}
2.线程锁:ReentrantLock
方法名 | 说明 |
---|---|
ReentrantLock() | 创建一个ReentrantLock的实例 |
-
加锁解锁方法
方法名 说明 void lock() 获得锁 void unlock() 释放锁
public class Ticket implements Runnable {
//票的数量
private int ticket = 100;
private Object obj = new Object();
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
if (ticket <= 0) {
//卖完了
break;
} else {
ticket--;
System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");
}
lock.unlock();
}
}
}
public class Demo {
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
Thread t3 = new Thread(ticket);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
3..生产者消费者
-
Object类的等待和唤醒方法
方法名 说明 void wait() 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法 void notify() 唤醒正在等待对象监视器的单个线程 void notifyAll() 唤醒正在等待对象监视器的所有线程
网友评论