多线程编程
1.多线程的实现方式
1.1 继承Thread类
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new MyThread();
thread.start();
}
}
//Java 实现线程方式 1 继承 Thread
class MyThread extends Thread{
private int count = 7;
@Override
public void run() {
while(count>0){
System.out.println(count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
}
}
}
优点:实现简单,只需实例化继承类的实例,即可使用线程 缺点:扩展性不足,Java是单继承的语言,如果一个类已经继承了其他类,就无法通过这种方式实现自定义线程
常用方法:
Thread():用于构造一个新的Thread。
Thread(Runnable target):用于构造一个新的Thread,该线程使用了指定target的run方法。
Thread(ThreadGroup group,Runnable target):用于在指定的线程组中构造一个新的Thread,该
线程使用了指定target的run方法。
currentThread():获得当前运行线程的对象引用。
interrupt():将当前线程置为中断状态。
sleep(long millis):使当前运行的线程进入睡眠状态,睡眠时间至少为指定毫秒数。
join():等待这个线程结束,即在一个线程中调用other.join(),将等待other线程结束后才继续本线程。
yield():当前执行的线程让出CPU的使用权,从运行状态进入就绪状态,让其他就绪线程执行。
2)Object类
wait():让当前线程进入等待阻塞状态,直到其他线程调用了此对象的notify()或notifyAll()方法后,当
前线程才被唤醒进入就绪状态。
notify():唤醒在此对象监控器上等待的单个线程。
notifyAll():唤醒在此对象监控器上等待的所以线程。
注:wait()、notify()、notifyAll()都依赖于同步锁,而同步锁是对象持有的,且每个对象只有一个,所以
这些方法定义在Object类中,而不是Thread类中。
3)yield()、sleep()、wait()比较
wait():让线程从运行状态进入等待阻塞状态,并且会释放它所持有的同步锁。
yield():让线程从运行状态进入就绪状态,不会释放它锁持有的同步锁 (让出本次线程,与其他线程一起竞争)。
sleep():让线程从运行状态进入阻塞状态,不会释放它锁持有的同步锁。
1.2 实现Runnable接口
public static void main(String[] args) throws InterruptedException {
// Thread thread = new MyThread();
// thread.start();
Runnable runnable = new Myrunable();
runnable.run();
}
class Myrunable implements Runnable{
private int count = 7;
@Override
public void run() {
while(count>0){
synchronized (this){
System.out.println(Thread.currentThread());
System.out.println("ticket:"+count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
}
}
}
}
优点:
- 扩展性好,可以在此基础上继承其他类,实现其他必需的功能
- 对于多线程共享资源的场景,具有天然的支持,适用于多线程处理一份资源的场景
缺点:构造线程实例的过程相对繁琐一点
线程中共用变量,方法的情况
public class ThreadTest{
public static void main(String[] args){
Runnable r = new MyThread();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
}
class MyThread implements Runnable{
private int tickets = 7;
@Override
public void run(){
while(tickets>0){
synchronized(this){ //获取当前对象的同步锁
if(tickets>0){
System.out.println("tickets:"+tickets);
tickets--;
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
}
}
synchronized用法:
1)synchronized方法:如果一个线程要在某个对象上调用synchronized方法,那么它必须先获取这个对象的
锁,然后执行方法体,最后释放这个对象上的锁,而与此同时,在同一个对象上调用synchronized方法的其他
线程将阻塞,直到这个对象的锁被释放为止。
public synchronized void show(){
System.out.println("hello world");
}
2)synchronized静态方法:静态方法也可以被声明为synchronized的,每个类都有与之相关联的Class对象,
而静态同步方法获取的就是它所属类的Class对象上的锁,两个线程不能同时执行同一个类的静态同步方法,如
果静态数据是在线程之间共享的,那么对它的访问就必须利用静态同步方法来进行保护。
3)synchronized语句:静态语句可以使我们获取任何对象上的锁而不仅仅是当前对象上的锁,也能够让我们定
义比方法还要小的同步代码区,这样可以让线程持有锁的时间尽可能短,从而提高性能。
private final Object lock = new Object();
public void show(){
synchronized(lock){
System.out.println("hello world");
}
}
2.线程停止
当使用Thread.stop()时报错,这个方法会报错,抛出了UnsupportedOperationException异常,它在JDK中已经被声明“过期/作废”的方法,显然它在功能上有缺陷,不建议使用。
最好不要使用
1.使用推出标志
public class MyThread implements Runnable {
private int number;
private volatile boolean isStop;//使用volatile关键字修饰,可以在多线程之间共享,成员变量来控制线程的停止
@Override
public void run() {
super.run();
System.out.println(Thread.currentThread().getName() + " == 进入");
while (!isStop) {
synchronized (MyThread.class) {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " == " + number);
number++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 设置线程标识
* @param isStop true:停止 false:不停止
*/
public void stopThread(boolean isStop) {
this.isStop = isStop;
}
}
public static void main(String[] args) {
try {
//自定义线程
MyThread runnable = new MyThread();
Thread thread = new Thread(runnable, "子线程");
thread.start();
//睡5秒
Thread.sleep(5000);
//停止线程
runnable.stopThread(true);
} catch (InterruptedException e) {
e.printStackTrace();
}
2.使用interrupt停止线程
很多人认为线程的“中断”就是让线程的停止,在Java中线程的中断(interrupt)只是改变线程的中断状态,对于一般逻辑的线程,如果调用interrupt()方法,对这个线程没有任何影响,但是中断标志变为true,但是并不会停止这个线程的运行。
Thread.isInterrupted() 测试线程是否已经中断,但是不能清除状态标识
Thread.interrupted() 中断线程,改变线程状态,连续调用两次,将会更改为默认状态false
interrupt并不能中断线程,所以需要其他方法
2.1 抛出异常
public class MyThread2 extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 5000; i++) {
if (this.isInterrupted()) {//获取线程的中断状态
System.out.println(Thread.currentThread().getName() + ":isInterrupted():" + this.isInterrupted() + " == " + i);
break;
}
System.out.println(Thread.currentThread().getName() + " == " + i);
}
System.out.println("=== for下面的语句,线程并没有结束 ===");
}
}
public static void main(String[] args) {
try {
//自定义线程
MyThread2 thread = new MyThread2();
thread.start();
//睡0.01秒
Thread.sleep(10);
//中断线程(不是真正停止线程,而是改变线程中断状态)
thread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("异常信息 == "+e.getMessage());
}
}
2.2 使用return