线程类的部分方法
有关方法1
void start() : 启动线程并执行run()
run() : 线程被调度时执行的操作
String getName(): 放回当前线程的名字
void setName(): 设置当前线程的名字
static Thread currentThread(): 返回当前线程, 在Thread子类中就是 this, 通常用于主线程和Runnable实现类
有关方法而2
static void yield() : 线程让步, 暂停当前正在执行的线程,把执行的机会让给优先级相同或者更高的线程, 若队列里没有优先级更高的线程,忽略此方法
join() : 阻塞当前线程,join()其他线程,当加入的线程执行完后,此线程才会继续
static void sleep(long millis) : (指定时间毫秒): 是令当前的活动线程再指定时间内放弃对cup的控制,使其他的线程有机会被执行,时间到后重新排队;抛出InterruptedException异常
创建线程
第一种创建线程的方式 (继承Thread)
/**
- 多线程的创建, 方式一 继承于 Thread类
- 创建一个继承于 Thread的子类
- 重写Thread类的run()
- 创建继承于Thread类的子类的对象
- 通过此对象调用 start()
*/
创建线程类
// 1. 创建一个继承于 Thread的子类
class MyThread extends Thread {
String flag;
public MyThread(String flag) {
this.flag = flag;
}
// 2. 重写Thread类的run()
@Override
public void run() {
for (int i=0; i<100; i++) {
if(i%2==0) {
System.out.println(i + this.flag + Thread.currentThread().getName());
}
}
}
}
使用线程
public class Thread01 {
public static void main(String[] args) {
// 3. 创建继承于Thread类的子类的对象
MyThread m1 = new MyThread("*****线程1*****");
// 4. 通过此对象调用 start() ①: 启动当前线程 ②: 调用当前线程的run()
m1.start();
}
}
使用匿名类创建 线程
new Thread() {
@Override
public void run() {
for (int i=0; i<100; i++) {
System.out.println(i + Thread.currentThread().getName());
}
}
}.start();
第二种创建线程的方式 (实现 Runnable)
- 创建一个实现 Runnable接口的类
- 实现类去实现抽象方法 run()
- 创建实现类对象
- 将此对象作为参数传递到Thread类的构造器中, 创建Thread的对象
- 通过Thread类的对象调用start() 方法
代码实现
package jzq.com.Thread03;
/**
* 1. 创建一个实现 Runnable接口的类
* 2. 实现类去实现抽象方法 run()
* 3. 创建实现类对象
* 4. 将此对象作为参数传递到Thread类的构造器中, 创建Thread的对象
* 5. 通过Thread类的对象调用start() 方法
*/
public class Thread03 {
public static void main(String[] args) {
// 3. 创建实现类对象
MThread mm = new MThread();
// 4. 将此对象作为参数传递到Thread类的构造器中, 创建Thread的对象
Thread t1 = new Thread();
// 5. 通过Thread类的对象调用start() 方法
t1.start();
}
}
//1. 创建一个实现 Runnable接口的类
class MThread implements Runnable {
//2. 实现类去实现抽象方法 run()
@Override
public void run() {
for (int i=100; i<100; i++) {
System.out.println(i);
}
}
}
分析两种线程的创建
开发中优先选择实现 Runnable接口的方式
原因:
1.实现的方式没有类的单继承的局限性
2.实现的方式更合适处理有共享数据的情况
第三种创建线程(实现Callable接口)
此创建 方法 可以 有返回值(object类型),通过重写 call 。可以抛出异常。
如果想要接收返回值,需要借助FutureTask, 通过此对象的get() 方法获取。
需要通过 new Thread(futureTask).start() 将线程跑起来。
代码如下:
package jzq.com.Thread11;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class THread11 {
public static void main(String[] args) {
MyThread mm = new MyThread();
// 获取该线程的返回值 (通过 get() )
FutureTask futureTask = new FutureTask(mm);
// 启动线程
new Thread(futureTask).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) {
sum += i;
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
return sum;
}
}
线程池的使用 (ExecutorService)
创建线程池: ExecutorService service = Executors.newFixedThreadPool(10);
线程池跑 线程:
service.execute(new MThread2());// execute 适合于 Runnable
service.submit(futureTask); // submit 适合 Callable
关闭线程池:
service.shutdown()
测试代码如下:
package jzq.com.Thread12;
import java.util.concurrent.*;
public class Thread13 {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
// execute 适合于 Runnable
service.execute(new MThread2());
service.execute(new MThread2());
// 接收返回值
MyThread1 mm = new MyThread1();
FutureTask futureTask = new FutureTask(mm);
// submit 适合 Callable
service.submit(futureTask);
service.submit(new MyThread1());
}
}
class MyThread1 implements Callable {
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 0; i <= 100; i++) {
if(i % 2 == 0) {
sum += i;
System.out.println(Thread.currentThread().getName() + ":(Callable)" + i);
}
}
return sum;
}
}
class MThread2 implements Runnable {
//2. 实现类去实现抽象方法 run()
@Override
public void run() {
for (int i=0; i<100; i++) {
System.out.println(Thread.currentThread().getName() + ":(Runnable)" + i);
}
}
}
线程的优先级
优先级
MAX_PRIORITY: 10
MIN_PRIORITY: 1
NPRMPRIORITY: 5 (默认优先级)
设置以及获取优先级的方法
- getpriority() : 获取当前线程的优先级
- setpriority(int p) : 设置当前线程的优先级
线程的生命周期
图解线程的生命周期(参考尚硅谷视频)
线程的同步
同步的优缺点
优点
解决了线程的安全性问题
缺点
操作同步代码的时候,只能有一个线程参与,其他线程等待,相当于一个线程操作,效率低。有局限性!
同步代码块
synchronized (同步监视器) {// 需要被同步的代码}
说明:
- 操作共享数据的代码,即为需要被同步的代码,测试的时候如果拿循环来测试,一定要在循环体内, 不然就成为单线程了
- 共享数据, 多个线程共同操作的变量
- 同步监视器, 俗称:锁。 任何一个类的对象都可以叫锁。 但是这个吧锁要是唯一的,不然会出现线程的安全性问题。
代码如下:
package jzq.com.Thread04;
public class Thread04 {
public static void main(String[] args) {
MyThread mm = new MyThread();
Thread t1 = new Thread(mm);
Thread t2 = new Thread(mm);
t1.start();
t2.start();
}
}
class MyThread implements Runnable {
private int s = 100;
Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized(MyThread.class) {
if (s<0) {
break;
}else {
try {
Thread.sleep(100);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"当前售票" + s);
s--;
}
}
}
}
}
同步方法
将同步的代码封装到方法,此方法被synchronized 所修饰
代码如下:
public synchronized void show() {
if (s>0) {
try {
Thread.sleep(100);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"当前售票" + s);
s--;
}
}
当使用 继承Thread的线程实现抢票功能时注意问题
变量值
注意值一定要是静态的,不然就是三个线程使用的不同的资源。
使用同步代码块或者同步方法的时需要注意
同步代码块,所添加的锁(同步监视器),一定是一个对象,可以是静态的对象或者class
同步方法一定要声明静态,不然自动获取的锁不是唯一的,也会出现安全性问题。
代码如下
package jzq.com.Thread05;
public class Thread05 {
public static void main(String[] args) {
MyThread m1 = new MyThread();
MyThread m2 = new MyThread();
MyThread m3 = new MyThread();
m1.start();
m2.start();
m3.start();
}
}
class MyThread extends Thread {
private static int s = 100;
@Override
public void run() {
while (true) {
// synchronized (MyThread.class) {
// if(s>0) {
// try {
// Thread.sleep(100);
// }catch (InterruptedException e) {
// e.printStackTrace();
// }
//
// System.out.println(Thread.currentThread().getName() + "抢票中---" + s);
// s--;
// }
// }
show();
}
}
public synchronized static void show() {
if(s>0) {
try {
Thread.sleep(100);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "抢票中---" + s);
s--;
}
}
}
使用 ReentrantLock 线程锁解决同步带来的安全性问题
创建 一个ReentrantLock 对象,通过 此对象调用lock():锁定方法 与 unlock(): 解锁方法进行控制,开锁后的同步代码,需要在执行完手动解锁!
代码示例
package Thread08;
import java.util.concurrent.locks.ReentrantLock;
public class Thread08 {
public static void main(String[] args) {
MyThread mm = new MyThread();
Thread t1 = new Thread(mm);
Thread t2 = new Thread(mm);
t1.start();
t2.start();
}
}
class MyThread implements Runnable {
private int s = 100;
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock(); //调用 锁定方法 lock()
if(s>0) {
try {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + ':' + s);
s--;
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}finally {
lock.unlock(); // 调用解锁方法
}
}
}
}
线程同步出现死锁问题
什么叫死锁?
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
出现死锁后,不会出现异常,不会出现提示,知识所有的线程都处于阻塞状态,无法继续
如何解决死锁问题?
尽量减少同步资源的定义
尽量避免嵌套同步
线程通信问题
使用线程通信完成两个线程之间交替
通过调用 wait()方法 与 notify()或者notifyall()
函数解释:
wait(): 一旦执行此方法, 当前线程就会进入阻塞状态,并释放同步监视器
notify(): 一旦执行此方法, 就会唤醒wait的一个线程, 如果多个线程被wait,就唤醒优先级高的。
notifyall(): 一旦执行此方法,就会唤醒所有被wait的线程。
说明:
这三个方法必须使用在同步代码块,或者同步方法中。
这三个的调用者必须是同步方法或者同步代码块中的同步监视器。 否则会出现IllegalMonitorStateException异常
这三个方法都是定义在java.lang.Object类中。
代码:
package jzq.com.Thread10;
public class Thread10 {
public static void main(String[] args) {
MyThread mm = new MyThread();
Thread t1 = new Thread(mm);
Thread t2 = new Thread(mm);
t1.start();
t2.start();
}
}
class MyThread implements Runnable {
private int s = 0;
private Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) {
obj.notify();
if(s<100) {
try {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + ":" + s);
s++;
}catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
try {
obj.wait();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}