美文网首页
Java 多线程

Java 多线程

作者: buyaole | 来源:发表于2016-08-04 17:24 被阅读0次

电脑里运行的一个程序成为称为一个进程,但是一个程序可以分成多个子程序来执行(我们称为线程),也就是一个进程中包含了多个线程(我们称为多线程),一个进程中至少包含一个线程。

一个进程中可以包含多个线程
多个线程中有些资源是共享的,有些是加锁的,有些是不共享的

创建一个线程的两种方法:

  1. 继承Thread类,重写run方法
  2. 实现Runnable接口,实现run方法

常用方法:
start : 启动一个线程,系统会调用线程中的run方法
run:必须要有的方法
setName:给线程一个名字
isAlive:线程是否活着
setPriority : 设置线程优先级(1-10),默认是5,这个优先级是相对的
setDaemon : 设置守护线程,默认为false。(守护线程,调用它的线程结束,自己也结束)
join : (先执行此线程),join(100):优先执行100毫秒
currentThread:得到当前线程
sleep:让线程休眠一段时间,单位:毫秒


线程安全,线程同步

假设:

  1. 有一个银行账号,里面余额1000
  2. 两个线程(二个人)同时往账号里面存200
    代码如下:
public class ThreadSync {

    public static void main(String[] args) {
        
        BankAccount ba = new BankAccount();
        Thread husband = new BankThread(ba);
        Thread wife = new BankThread(ba);
        husband.start();
        wife.start();
    }
    
}
class BankAccount{
    private double balance = 1000;
    
    public boolean deposit(double add){
        if(add <= 0)
            return false;
        else{
                System.out.println("当前余额为:"+ balance);
                balance += add;
                System.out.println("新的余额为:"+ balance);
            return true;
        }
    }
}

class BankThread extends Thread {
    private BankAccount a;
    public BankThread(BankAccount a){
        this.a = a;
    }
    public void run(){
        a.deposit(200);
    }
}
结果是这样的:
也可能是这样的:

这是因为两个线程都对同一个资源(BankAccount)进行了操作,都在抢占同一个对象,这样就很容易出错,所以导致每次的输出结果可能不一样。

解决办法:

BankAccount中的deposit方法的声明中加一个synchronized关键字 :
public synchronized boolean deposit(double add)
表示该方法是线程安全的,当有一个线程调用这个方法时,会给它加一把锁,这时候其他线程就不能调用这个方法了,只有等到上一个线程调用完成之后才能访问。

其实也可以这样:

public /*synchronized*/ boolean deposit(double add){
        if(add <= 0)
            return false;
        else{
            synchronized(this){
                System.out.println("当前余额为:"+ balance);
                balance += add;
                System.out.println("新的余额为:"+ balance);
            }
            return true;
        }
    }

synchronized来包裹一个代码块也是可以的,关于synchronized的用法这里不再赘述,修改之后的运行结果为:

无论运行多少次都是结果

线程间的通信

wait:wait()方法使得当前线程必须要等待,等到另外一个线程调用notify()或者notifyAll()方法。

线程调用wait()方法,释放它对锁的拥有权,然后等待另外的线程来通知它(通知的方式是notify()或者notifyAll()方法),这样它才能重新获得锁的拥有权和恢复执行。
  要确保调用wait()方法的时候拥有锁,即,wait()方法的调用必须放在synchronized方法或synchronized块中。

notify:notify()方法会唤醒一个等待当前对象的锁的线程。

和wait()方法一样,notify方法调用必须放在synchronized方法或synchronized块中。


线程的死锁


死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。不同的线程都在等待那些根本不可能被释放的锁,从而导致所有的工作都无法完成。如上图所示,有两个线程都要获得Lock1和Lock2,当线程A获得了Lock1时,它会等待Lock2,但是Lock2已经被线程B获得,线程B在等待Lock1,双方都在等待对方的Lock解锁才能进行下一步操作,那么就会无限地等待下去,就造成了线程的死锁。
代码:
public class DeadLock {

    public static void main(String[] args) {
        Object lock1 = new Object();
        Object lock2 = new Object();
        
        ThreadA ta = new ThreadA(lock1, lock2);
        ThreadB tb = new ThreadB(lock1, lock2);
        ta.start();
        tb.start();
    }
}

class ThreadA extends Thread {
    Object lock1,lock2;
    public ThreadA(Object lock1, Object lock2) {
        this.lock1 = lock1;
        this.lock2 = lock2;
    }
    @Override
    public void run() {
        synchronized(lock1){
            System.out.println("线程A拿到了lock1");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            synchronized(lock2){
                System.out.println("线程A拿到了lock1和lock2");
            }
        }
    }
}

class ThreadB extends Thread {
    Object lock1,lock2;
    public ThreadB(Object lock1, Object lock2) {
        this.lock1 = lock1;
        this.lock2 = lock2;
    }
    @Override
    public void run() {
        synchronized(lock2){
            System.out.println("线程B拿到了lock2");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            synchronized(lock1){
                System.out.println("线程B拿到了lock1和lock2");
            }
        }
    }
}

运行结果:


可以看到程序运行到这里就会一直处于等待状态,并且会一直等下去。

解决:

解决的方式有很多种,这里就说一种比较简单的。上面的程序中线程A是先拿Lock1,而线程B是先拿Lock2,我们只要让两个线程拿的顺序是一样的就行了:
线程B变为:

class ThreadB extends Thread {
    Object lock1,lock2;
    public ThreadB(Object lock1, Object lock2) {
        this.lock1 = lock1;
        this.lock2 = lock2;
    }
    @Override
    public void run() {
        synchronized(lock1){
            System.out.println("线程B拿到了lock1");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            synchronized(lock2){
                System.out.println("线程B拿到了lock1和lock2");
            }
        }
    }
}

运行结果:

这样就不会死锁了

以上就是一个简单的死锁,当然,我们在实际的编程中,大多数的死锁不会这么显而易见,还需仔细分析代码才能看出。

总结

Java单单一块多线程就可以写好基本书,以上只是对多线程的一个初步的理解,要想用好多线程,还需更深入的学习和研究。


相关文章

  • 带你搞懂Java多线程(五)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三)带你搞懂Java多线程(四) ...

  • 带你搞懂Java多线程(六)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三)带你搞懂Java多线程(四)带...

  • Java多线程目录

    Java多线程目录 Java多线程1 线程基础Java多线程2 多个线程之间共享数据Java多线程3 原子性操作类...

  • java多线程--Callable

    **移步[java多线程系列文章]Java多线程(二十二)---LockSupport工具Java 停止线程 一、...

  • android 多线程 — 线程的面试题和答案

    这里都是我从各个地方找来的资料,鸣谢: Java多线程干货系列—(一)Java多线程基础 JAVA多线程和并发基础...

  • 5月份第一周学习安排

    学习内容: java多线程及线程同步的方法(使用) java多线程各种同步方法的原理和优缺点 java多线程设计模...

  • 带你搞懂Java多线程(四)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三) 什么是线程间的协作 线程之间...

  • Java基础(六)

    多线程 Java多线程并发 1.1 JAVA 并发知识库 1.2 JAVA 线程实现/创建方式 1.2.1 继承 ...

  • (五) volatile关键字

    Java多线程目录 1 背景 理解Java多线程的内存抽象逻辑请阅读java多线程内存模型,当代操作系统,处理器为...

  • Java多线程高级特性(JDK8)

    [TOC] 一、Java多线程 1.Java多线程基础知识 Java 给多线程编程提供了内置的支持。一条线程指的是...

网友评论

      本文标题:Java 多线程

      本文链接:https://www.haomeiwen.com/subject/mgjcsttx.html