美文网首页
Java多线程

Java多线程

作者: 海人为记 | 来源:发表于2018-07-20 20:52 被阅读9次

进程:执行中的程序;一个进程至少包含一个线程

线程:进程中负责程序执行的执行单元
线程本身依靠程序进行运行
线程是程序中的顺序控制流,只能使用分配给程序的资源和环境
单线程:程序中只存在一个线程,实际上主方法就是一个主线程
多线程:指的是这个程序(一个进程)运行时产生了不止一个线程,目的是为了更好地使用CPU资源,多个线程并发执行可以提高程序的效率,可以同时完成多项工作,多线程节约的是执行程序的等待时间,如果程序排列紧密,没有等待时间,多线程不能真正的提高效率。

相比于多进程,多线程的优势有:

  • 进程之间不能共享数据,线程可以
  • 系统创建进程需要为该进程重新分配系统资源,故创建线程代价比较小
  • Java语言内置了多线程功能支持,简化了Java多线程编程

线程的实现

继承Tread类的实现
在java.lang包中定义,继承Thread类必须重写run()方法

public class ThreadLearn extends Thread {
    
    @Override
    public void run() {
        System.out.println("主动创建线程");
    }
}
public class Example {
    public static void main(String[] args) {    
        ThreadLearn thread = new ThreadLearn();
        thread1.start();
    }
}
打印:主动创建线程
public class ThreadLearn extends Thread {   
    private String name;
    public ThreadLearn(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        System.out.println("name"+this.name+"子线程ID"+Thread.currentThread().getId());
    }
}
public static void main(String[] args) {
    System.out.println("主线程ID:"+Thread.currentThread().getId());
    ThreadLearn thread1 = new ThreadLearn("thread1");
    thread1.start();        
    ThreadLearn thread2 = new ThreadLearn("thread2");
    thread2.run();
}
//打印结果
主线程ID:1
namethread2子线程ID1
namethread1子线程ID11

从输出结果可以得出以下结论:

  • thread1和thread2的线程ID不同,thread2和主线程ID相同,说明通过run方法调用并不会创建新的线程,而是在主线程中直接运行run方法,跟普通的方法调用没有任何区别;
  • 虽然thread1的start方法调用在thread2的run方法前面调用,但是先输出的是thread2的run方法调用的相关信息,说明新线程创建的过程不会阻塞主线程的后续执行。

定义类实现Runnable接口
在Java中创建线程除了继承Thread类之外,还可以通过实现Runnable接口来实现类似的功能。实现Runnable接口必须重写其run方法。

public class RunnableLearn implements Runnable {
    @Override
    public void run() {
        System.out.println("子线程ID:"+Thread.currentThread().getId());
    }
}
public class Example {

public static void main(String[] args) {
    System.out.println("主线程ID:"+Thread.currentThread().getId());
    RunnableLearn runnable = new RunnableLearn();
    Thread thread = new Thread(runnable);
    thread.start();
}
//打印
主线程ID:1
子线程ID:11

Thread类中定义了一个Runnable类型的成员变量target用来接收Runnable的子类对象;当调用Thread对象的start()方法的时候,方法内首先会判断target是否为null。如果不为null就调用Runnable子类对象的run方法;多个Thread对象可以共享一个Runnable子类对象。

在Java中,这2种方式都可以用来创建线程去执行子任务,具体选择哪一种方式要看自己的需求。
继承Thread的多线程实现可以直接使用Thread类中的方法,代码简单;弊端是如果已经有了父类,就不能用这种方法(Java只允许单继承),所以如果自定义类需要继承其它类,则只能选择实现Runnable接口。而实现Runnable接口的类不能直接使用Thread中的方法,需要先获取到线程对象后,才能得到Thread的方法,代码复杂。

匿名内部类实现多线程的两种方式

继承Thread类
// 匿名内部类的多继承的实现
new Thread() {
    public void run() {
        while(true) 
            System.out.println("继承Thread的线程");
    }
}.start();
Thread thread = new Thread() {
    public void run() {
        while(true) 
            System.out.println("继承Thread的线程");
    }
};
thread.start();
//创建Thread对象,提供Runnable对象
new Thread(new Runnable() {
    public void run() {
        while(true) {
            System.out.println("实现Runnable接口的线程");
        } 
    }
}).start();
Thread thread1 = new Thread(new Runnable() {
    public void run() {
    while(true)
            System.out.println("实现Runnable接口的线程");
    }
});
thread1.start();

多线程的方法

  • 通过getName()方法获取线程对象的名字
  • 通过构造方法传入String类型的名字或通过setName(String name)方法设置线程对象的名字
  • 获取当前线程的对象Thread.currentThrad()
  • 休眠线程Thread.sleep(millis)//参数为毫秒
  • 守护线程setDaemon(): 围绕着其它非守护线程运行,该线程不会单独运行,当其它非守护线程都执行结束后,自动退出
  • 加入线程join()/join(int)
  • 礼让线程yield
  • 设置线程的优先级setPriority()
public class Example {

    public static void main(String[] args) {
        // 获取当前主线程ID
        System.out.println("主线程ID:"+Thread.currentThread().getId());
        Thread thread = new Thread("线程一"); // 传入字符串设置线程名字
        String threadName = thread.getName(); // 获取线程名字
        System.out.println(threadName);  //线程一
        thread.setName("线程二");  // 将线程一改名为线程二
        threadName = thread.getName();
        System.out.println(threadName);  // 线程二
        
        try {
            Thread.sleep(1000);//休眠线程(设置线程等待1秒)
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }  
        
        //守护线程
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.print("线程t1运行中");
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("守护线程t2运行中");
            }
        });
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                    System.out.print("线程t3运行中");
            }
        });
        t2.setDaemon(true); // 设置守护线程
        t1.start();
        t3.start();
        t2.start(); // t1和t3运行之后,自动退出
        
        // 加入线程
        // 当前线程暂停,等待指定的线程执行结束后,当前线程在继续
        
        Thread tj1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i < 50; i++) {
                    System.out.println("线程一运行中...");
                    try {
                        Thread.sleep(10);
                    }catch(InterruptedException e) {
                        e.printStackTrace();
                    }
                }   
            }
        });
        Thread tj2 = new Thread(new Runnable() {
            @Override
            public void run() {
                
                for (int i = 0; i < 50; i++) {
                    System.out.println("线程二运行中..........");
                    try {
//                      tj1.join(); //让线程一先执行
                        tj1.join(100);//让线程一先执行100毫秒,100毫秒之后,当前加入线程之后就不起作用了
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        //只有调用了加入线程的线程才会停止运行,其他线程不受影响
        Thread tj3 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                
                for (int i = 0; i < 50; i++) {
                    System.out.println("线程三运行中..........");
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        tj1.start();
        tj2.start();
        tj3.start();
        
        // 礼让线程
        //让出当前线程的执行权
        Thread ty1 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                
                for (int i = 0; i < 50; i++) {
                    System.out.println("y线程一运行中");
                    //让出当前线程的执行权
                    Thread.yield();
                }
            }
            
        });
        
        Thread ty2 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                
                for (int i = 0; i < 50; i++) {
                    System.out.println("y线程二运行中..........");        
                }
            }
        });

        ty1.start();
        ty2.start();
        
        //设置线程的优先级
        //每个线程都有优先级 默认是5,范围是1-10,1表示优先级最低
        //优先级高的线程在争夺cpu的执行权上有一定的优势,但不是绝对的
        Thread tp1 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                
                for (int i = 0; i < 50; i++) {
                    System.out.println("p线程一运行中");
                    
                }
            }
            
        });
        
        Thread tp2 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                
                for (int i = 0; i < 50; i++) {
                    System.out.println("p线程二运行中..........");
                    
                }
            }
        });
        
        tp1.setPriority(1);
        tp2.setPriority(10); //线程二的优先级高
        tp1.start();
        tp2.start();
    }
}

共享资源及安全问题

所谓的共享资源,指的是多条线程在运行时操作的(共享的)同一内容

  • 实现Runnable接口:
    操作共享资源,只需要在Runnable类中创建成员变量,通过run方法来操作该成员变量,该成员变量就是共享资源
public class Demo2_apple {

    public static void main(String[] args) {
        // 思路:
        // 1.创建两条线程,接收实现Runnable接口的对象
        SubRunnable myRunnable = new SubRunnable();
        Thread t1 = new Thread(myRunnable);
        Thread t2 = new Thread(myRunnable);
        t1.setName("小明");
        t2.setName("小清");
        t1.start();
        t2.start();
    }

}
class SubRunnable implements Runnable {
    int appleCount = 5;
    @Override
    public void run() {
        while(appleCount>0) {
            appleCount--;
            System.out.println(Thread.currentThread().getName()+"抢到了一个苹果"
                    + ",还剩"+appleCount+"个苹果");
        }
    }
}
  • 继承Thread类的方式:
    通过将共享资源作为对象,通过构造器传递给Thread
public class Demo1_apple {

    // 两个小朋友,一个叫刘文浩,一个叫龚陈超,两个小朋友去抢苹果,
    // 一共有五个苹果,一次只能拿一个,看谁抢的多
    public static void main(String[] args) {
        // 思路:
        // 1.创建两条线程,将Apple类对象通过构造器传入
        Apple apple = new Apple();
        Thread t1 = new SubThread(apple);
        Thread t2 = new SubThread(apple);
        t1.setName("小明");
        t2.setName("小清");
        t1.start();
        t2.start();
    }

}
// 共享的资源
class Apple {
    int appleCount = 5;
}
class SubThread extends Thread {
    Apple apple; // 共享资源
    public SubThread(Apple apple) {
        this.apple = apple;
    }

    @Override
    public void run() {
        while(apple.appleCount>0) {
            apple.appleCount--;
            System.out.println(Thread.currentThread().getName()+"抢到了一个苹果"
                    + ",还剩"+apple.appleCount+"个苹果");
        }
    }
}

发现了线程安全的问题
在多条操作同一资源时,一条线成夹在另一条线程内执行,对共享资源进行多次修改,上面的代码执行的结果为

通过实现Runnable接口实现的共享资源,打印的内容
小明抢到了一个苹果,还剩4个苹果
小清抢到了一个苹果,还剩4个苹果
小明抢到了一个苹果,还剩3个苹果
小清抢到了一个苹果,还剩2个苹果
小明抢到了一个苹果,还剩1个苹果
小清抢到了一个苹果,还剩0个苹果
通过继承Thread类的方式实现的共享资源,打印的内容
小明抢到了一个苹果,还剩3个苹果
小明抢到了一个苹果,还剩2个苹果
小明抢到了一个苹果,还剩1个苹果
小清抢到了一个苹果,还剩3个苹果
小明抢到了一个苹果,还剩0个苹果

上面的输出结果,两条线程有输出为一样的结果,这就是两个线程都对资源进行了修改,这样就出现了线程安全问题,我们要怎么解决这个问题呢,答案是使用锁.

多线程加锁

Java通过①synchronized关键字;②Java.util.concurrent包中的lock接口和ReentrantLock实现类这两种方式来实现加锁.

synchronized

  • 同步代码块:谁充当着锁,synchronized(对象锁)
    同步代码块使用的是对象锁,"对象锁"对对象没有要求,任何对象都可以充当锁,对象锁的意义是形成互斥
    注意:当一个对象充当锁时,该对象锁要同时被多条线程共享才有意义,否则不能形成互斥; 对象锁的互斥是面向所有线程的.
  • 同步方法:方法所在的对象充当着锁.
public synchronized void method(){}
  • 静态同步方法:所在类的类模板(字节码)充当着锁
public static synchronized void method() {} // 是这个类的类对象充当着锁 Class.class

当我们给线程加上所之后

        while(apple.appleCount>0) {
            synchronized(apple) {
                if(apple.appleCount>0) {
                    apple.appleCount--;
                    System.out.println(Thread.currentThread().getName()+"抢到了一个苹果"
                    + ",还剩"+apple.appleCount+"个苹果");
                }
            }
        }

执行的结果,就不会出现了重复的苹果

ReentrantLock

使用ReentrantLock对象实现上锁

class MyRunnable1 implements Runnable {
    int num = 0;
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while(num<100) {
            lock.lock();
            num++;
            System.out.println(Thread.currentThread().getName()+":"+num);           
            lock.unlock();
        }
    }
}

线程的死锁

死锁的定义:使用同步的多个线程同时持有对方运行时所需的资源
多线程同步时,当线程需要需要的锁被另一条线程占用,另一条线程没有办法释放锁,因此当前线程也无法获得所,于是程序就卡住了.
应该尽量避免使用嵌套的synchronized,因为这样会很容易出现死锁问题.

public class Demo4_deadLock {
    public static void main(String[] args) {        
        final StringBuffer sb1 = new StringBuffer();
        final StringBuffer sb2 = new StringBuffer();
        new Thread() {
            @Override
            public void run() {
                synchronized(sb1) {
                    sb1.append("a");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    synchronized(sb2) {
                        sb2.append("b");
                    }
                }
                
            }
        }.start();      
        new Thread() {
            @Override
            public void run() {
                synchronized(sb2) {
                    sb2.append("c");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    synchronized(sb1) {
                        sb1.append("d");
                    }
                }
            }
        }.start();      
        System.out.println(sb1);
        System.out.println(sb2);
    }
}
image

线程的通信

1. 使用synchronized实现线程的通信

多条线程之间实现通信,需要使用以下几个方法

  • wait() 让当前线程等待
  • notify() 唤醒正在等待的某一条线程,选择是随机的
  • notifyAll() 唤醒正在等待的所有线程

我们现在用一个例子来说明线程之间的通信

// 线程的通信 synchronized
public class Demo5_communication {
    // 让两个线程交替打印1~100
    public static void main(String[] args) {
        
        MRCommRunnable runnable = new MRCommRunnable();
        Thread t1 = new Thread(runnable);
        Thread t2 = new Thread(runnable);
        t1.setName("线程1");
        t2.setName("线程2");
        t1.start();
        t2.start();
        
    }
}
class MRCommRunnable implements Runnable {
    int num = 1;
    @Override
    public void run() {
        while(num < 100) {
            synchronized(this) {
                notifyAll();
                num++;
                System.out.println(Thread.currentThread().getName()+":"+num);
                try {
                    wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}

这样使用就可以实现线程之间的交替打印
我们在main方法中在创建一个线程

public static void main(String[] args) {
    Thread t3 = new Thread( new Runnable() {
        @Override
        public void run() {
            while(num1 < 100) {
                synchronized(this) {
                    this.notifyAll();
                    num1++;
                    System.out.println(Thread.currentThread().getName()+":"+num1);
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
    });
    t3.setName("线程3");
    t3.start();
}

当在执行后,这个方法进入线程等待状态 wait(),而在Runnable子类中的唤醒功能却唤醒不了t3线程,这是因为它们的对象锁不是一样的.
注意:这几个方法,只能在synchronized中使用;对象锁拥有两个队列,用于存放被等待的线程,和被唤醒的线程,因此,wait, notify和notifyAll方法是属于Object的;对象锁是谁,就由谁来调用这三个方法

synchronized(a) {
        a.wait();  //wait()==this.wait()  不行
}

2.使用Lock实现线程的通信

因为wait, notify,notifyAll只能使用在synchronized关键字中,因此,Lock使用Condition对象的await,singal, singalAll来代替.通过Lock对象的newCondition()来获得Condition对象.

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
// 线程通信 Lock
public class Demo6_comm_lock {
    // 让两个线程交替打印1~100
    public static void main(String[] args) {
        MRCommRunnableLock runnable = new MRCommRunnableLock();
        Thread t1 = new Thread(runnable);
        Thread t2 = new Thread(runnable);
        t1.setName("线程1");
        t2.setName("线程2");
        t1.start();
        t2.start();
    }
}
class MRCommRunnableLock implements Runnable {
    int num = 1;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition(); // 通过Condition对象实现线程的通信
    @Override
    public void run() {
        while(num < 100) {
            lock.lock(); // 上锁
            condition.signalAll(); // 唤醒线程
            num++;
            System.out.println(Thread.currentThread().getName()+":"+num);
            try {
                condition.await(); // 让线程等待
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                lock.unlock(); // 解锁
            }
        }
    }
}

线程的的局部变量-------ThreadLocal

堆空间中的资源往往对于多线程来说都是共享的.在设计一个类时,如果希望类的某些属性不被多线程共享,就可以把这些属性设计交给ThreadLocak来维护.于是,每条线程在使用该类时,都会自动创建ThreadLocal的副本,在实际操作中,操作的每个线程独有的那个ThreadLocak,而不会实现共享.

public class Demo7_threadLocal {

    public static void main(String[] args) {
            
        MRThreadLocalRunnable runnable = new MRThreadLocalRunnable("aaa", 123);
        Thread t1 = new Thread(runnable);
        Thread t2 = new Thread(runnable);
        t1.setName("肖邦");
        t2.setName("爱迪生");
        t1.start();
        t2.start();
    }

}
class MRThreadLocalRunnable implements Runnable {
    private String threadName = new String();
    int num;
    public MRThreadLocalRunnable(String threadName, int num) {
        this.threadName = threadName;
        this.num = num;
    }
    @Override
    public void run() {
        for(int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"name:"+threadName);
            // 设置当前线程的名字
            if(i==5)
                threadName = Thread.currentThread().getName();
        }
    }
}
这输出打印的是
肖邦name:aaa
肖邦name:aaa
肖邦name:aaa
肖邦name:aaa
肖邦name:aaa
肖邦name:aaa
肖邦name:肖邦
肖邦name:肖邦
肖邦name:肖邦
肖邦name:肖邦
爱迪生name:aaa
爱迪生name:肖邦
爱迪生name:肖邦
爱迪生name:肖邦
爱迪生name:肖邦
爱迪生name:肖邦
爱迪生name:爱迪生
爱迪生name:爱迪生
爱迪生name:爱迪生
爱迪生name:爱迪生
public class Demo7_threadLocal {

    public static void main(String[] args) {
            
        MRThreadLocalRunnable runnable = new MRThreadLocalRunnable("aaa", 123);
        Thread t1 = new Thread(runnable);
        Thread t2 = new Thread(runnable);
        t1.setName("肖邦");
        t2.setName("爱迪生");
        t1.start();
        t2.start();
    }

}
class MRThreadLocalRunnable implements Runnable {
    // 线程的局部变量
    private ThreadLocal<String> threadName = new ThreadLocal<>();
    int num;
    public MRThreadLocalRunnable(String threadName, int num) {
        this.threadName.set(threadName); // 设置线程的局部变量
        this.num = num;
    }
    @Override
    public void run() {
        for(int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"name:"+threadName.get());
            // 设置当前线程的名字
            if(i==5)
                threadName.set(Thread.currentThread().getName());
        }
    }
}
输出打印的是
爱迪生name:null
爱迪生name:null
爱迪生name:null
肖邦name:null
肖邦name:null
肖邦name:null
爱迪生name:null
肖邦name:null
爱迪生name:null
肖邦name:null
肖邦name:null
爱迪生name:null
肖邦name:肖邦
爱迪生name:爱迪生
爱迪生name:爱迪生
爱迪生name:爱迪生
爱迪生name:爱迪生
肖邦name:肖邦
肖邦name:肖邦
肖邦name:肖邦

这是因为使用线程的局部变量 ThreadLocal,你在设置名字的时候,它设置的都是属于每个线程的变量,而不是共享的变量

线程池

当频繁地创建和启动多线程,来完成一些比较小的任务,那么这样的代价是非常大的。因此,可以通过线程池来实现,将线程的创建和销毁都交给线程池来管理,而我们需要使用线程时,将任务提交给线程池即可,由线程池来为我们安排一条线程,来执行我们的任务。
jdk1.5之后就提供了相应的类,实现线程池。

1. 创建线程池的方式

  • 创建一个有多条线程的线程池,如果线程池中的线程没有可用,就新建一条线程
Executors.newCachedThreadPool()
  • 创建一个有多条线程的线程池,如果线程池中的线程没有可用,就进入到等待队列,直到有可用的线程为止
Executors.newFixedThreadPool(int nThreads)

2. 线程池的使用

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo8_threadPool {

    public static void main(String[] args) {
        // 创建一个指定线程数量的线程池
        ExecutorService pool = Executors.newFixedThreadPool(5);
        // 创建任务
        MYRunnable my = new MYRunnable();
        // 将任务交给任务池,有任务池安排线程来执行任务
        pool.submit(my);
        pool.submit(my);
        pool.submit(my);
        pool.submit(my);
        pool.submit(my);
        pool.submit(my);
        pool.submit(my);
        pool.submit(my);
        // 关闭线程池
        pool.shutdown();
    }

}
class MYRunnable implements Runnable {
    @Override
    public void run() {

        System.out.println("aaaaaaaaaabbbbbccdddeeefffggghhiizzkk");
    }
}

注意:之前创建的线程都只能执行一次,但是使用线程池的方式,可以将任务执行多次.

生产者和消费者模式

1.是否涉及到多线程? 是
2.是否涉及到共享资源? 是

  1. 是否涉及到线程安全问题? 是
  2. 是否涉及到线程的通信? 是
    生产者和消费者都是多线程,它们共享的是商店这个资源,对它进行操作,具体的操作放在共享资源中进行操作

什么时候会失去CPU

  • 时间片到了
  • 被强占了
  • 使用了 yield() 礼让线程
  • 使用了 join() 加入线程
  • 使用了sleep() 休眠线程
  • 使用了 wait() 使线程处于等待状态

是么时候会释放锁

  • wait()
  • run结束
  • 程序出现了异常

相关文章

  • 带你搞懂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/sgrfmftx.html