【Java基础总结】多线程
1. 实现多线程的两种方式
- //第一种:继承Thread类,重写run()方法
- class ThreadTest1 extends Thread{
- public void run(){
- String threadName = Thread.currentThread().getName();
- for(int i=0;i<10;i++){
- System.out.println("ThreadTest1 "+threadName+" running ... "+i);
- }
- }
- }
- //第二种:实现Runnable接口,重写run()方法
- class ThreadTest2 implements Runnable{
- public void run(){
- String threadName = Thread.currentThread().getName();
- for(int i=0;i<10;i++){
- System.out.println("ThreadTest2 "+threadName+" running ... "+i);
- }
- }
- }
实现方式不同,使用方式也不同
- public class Demo1{
- public static void main(String[] args){
- ThreadTest1 t1 = new ThreadTest1();
- ThreadTest1 t2 = new ThreadTest1();
- Thread t3 = new Thread(new ThreadTest2());
- Thread t4 = new Thread(new ThreadTest2());
- t1.start();
- t2.start();
- t3.start();
- t4.start();
- }
- }
运行结果大致如下:
- ThreadTest1 Thread-0 running ... 0
- ThreadTest2 Thread-3 running ... 0
- ThreadTest2 Thread-2 running ... 0
- ThreadTest1 Thread-1 running ... 0
- ThreadTest2 Thread-2 running ... 1
- ThreadTest2 Thread-3 running ... 1
- ThreadTest1 Thread-0 running ... 1
- ThreadTest2 Thread-3 running ... 2
- ThreadTest2 Thread-2 running ... 2
- ThreadTest1 Thread-1 running ... 1
- ThreadTest2 Thread-2 running ... 3
- ThreadTest2 Thread-3 running ... 3
- ThreadTest1 Thread-0 running ... 2
- ThreadTest2 Thread-3 running ... 4
- ThreadTest2 Thread-2 running ... 4
- ThreadTest1 Thread-1 running ... 2
- ThreadTest2 Thread-2 running ... 5
- ThreadTest2 Thread-3 running ... 5
- ThreadTest1 Thread-0 running ... 3
- ThreadTest2 Thread-3 running ... 6
- ThreadTest2 Thread-2 running ... 6
- ThreadTest1 Thread-1 running ... 3
- ThreadTest2 Thread-2 running ... 7
- ThreadTest2 Thread-3 running ... 7
- ThreadTest2 Thread-3 running ... 8
- ThreadTest2 Thread-3 running ... 9
- ThreadTest1 Thread-0 running ... 4
- ThreadTest1 Thread-0 running ... 5
- ThreadTest2 Thread-2 running ... 8
- ThreadTest2 Thread-2 running ... 9
- ThreadTest1 Thread-1 running ... 4
- ThreadTest1 Thread-0 running ... 6
- ThreadTest1 Thread-0 running ... 7
- ThreadTest1 Thread-0 running ... 8
- ThreadTest1 Thread-1 running ... 5
- ThreadTest1 Thread-0 running ... 9
- ThreadTest1 Thread-1 running ... 6
- ThreadTest1 Thread-1 running ... 7
- ThreadTest1 Thread-1 running ... 8
- ThreadTest1 Thread-1 running ... 9
2. 线程共享资源
建议使用 实现Runnable接口,重写run方法 的方式来实现多线程,它有如下优点:
1. 线程和代码分离,多线程间可以共享资源
2. 避免了单继承带来的局限性
3. 多线程之间可以共享资源
tip
- Thread.currentThread().getName() 获得当前线程的名称
- threadObj.setName() 设置线程名称
案例:售票
- class ThreadTest4 implements Runnable{
- private int ticket=20;
- public void run(){
- String threadName = Thread.currentThread().getName();
- while(ticket>0){
- System.out.println("ThreadTest4 "+threadName+" 售出 "+ticket+" 号票");
- ticket--;
- }
- }
- }
使用情况1:
- (new Thread(new ThreadTest4(), "窗口a")).start();
- (new Thread(new ThreadTest4(), "窗口b")).start();
- (new Thread(new ThreadTest4(), "窗口c")).start();
- (new Thread(new ThreadTest4(), "窗口d")).start();
运行情况说明A、B、C、D四个窗口也没用共享count这个资源
- 主线程名称:main
- ThreadTest4 窗口a 售出 20 号票
- ThreadTest4 窗口a 售出 19 号票
- ThreadTest4 窗口b 售出 20 号票
- ThreadTest4 窗口b 售出 19 号票
- ThreadTest4 窗口b 售出 18 号票
- ThreadTest4 窗口b 售出 17 号票
- ThreadTest4 窗口b 售出 16 号票
- ThreadTest4 窗口b 售出 15 号票
- ThreadTest4 窗口a 售出 18 号票
- ThreadTest4 窗口b 售出 14 号票
- ThreadTest4 窗口d 售出 20 号票
- ThreadTest4 窗口c 售出 20 号票
- ThreadTest4 窗口d 售出 19 号票
- ThreadTest4 窗口b 售出 13 号票
- ThreadTest4 窗口a 售出 17 号票
- ThreadTest4 窗口b 售出 12 号票
- ThreadTest4 窗口d 售出 18 号票
- ThreadTest4 窗口c 售出 19 号票
- ThreadTest4 窗口d 售出 17 号票
- ThreadTest4 窗口b 售出 11 号票
- ThreadTest4 窗口a 售出 16 号票
- ThreadTest4 窗口b 售出 10 号票
- ThreadTest4 窗口d 售出 16 号票
- ThreadTest4 窗口c 售出 18 号票
- ThreadTest4 窗口d 售出 15 号票
- ThreadTest4 窗口b 售出 9 号票
- ThreadTest4 窗口a 售出 15 号票
- ThreadTest4 窗口b 售出 8 号票
- ThreadTest4 窗口d 售出 14 号票
- ThreadTest4 窗口c 售出 17 号票
- ThreadTest4 窗口d 售出 13 号票
- ThreadTest4 窗口b 售出 7 号票
- ThreadTest4 窗口a 售出 14 号票
- ThreadTest4 窗口b 售出 6 号票
- ThreadTest4 窗口d 售出 12 号票
- ThreadTest4 窗口c 售出 16 号票
- ThreadTest4 窗口d 售出 11 号票
- ThreadTest4 窗口b 售出 5 号票
- ThreadTest4 窗口a 售出 13 号票
- ThreadTest4 窗口b 售出 4 号票
- ThreadTest4 窗口d 售出 10 号票
- ThreadTest4 窗口c 售出 15 号票
- ThreadTest4 窗口d 售出 9 号票
- ThreadTest4 窗口b 售出 3 号票
- ThreadTest4 窗口a 售出 12 号票
- ThreadTest4 窗口b 售出 2 号票
- ThreadTest4 窗口d 售出 8 号票
- ThreadTest4 窗口c 售出 14 号票
- ThreadTest4 窗口d 售出 7 号票
- ThreadTest4 窗口b 售出 1 号票
- ThreadTest4 窗口a 售出 11 号票
- ThreadTest4 窗口a 售出 10 号票
- ThreadTest4 窗口d 售出 6 号票
- ThreadTest4 窗口c 售出 13 号票
- ThreadTest4 窗口d 售出 5 号票
- ThreadTest4 窗口a 售出 9 号票
- ThreadTest4 窗口d 售出 4 号票
- ThreadTest4 窗口c 售出 12 号票
- ThreadTest4 窗口d 售出 3 号票
- ThreadTest4 窗口a 售出 8 号票
- ThreadTest4 窗口d 售出 2 号票
- ThreadTest4 窗口c 售出 11 号票
- ThreadTest4 窗口d 售出 1 号票
- ThreadTest4 窗口a 售出 7 号票
- ThreadTest4 窗口c 售出 10 号票
- ThreadTest4 窗口a 售出 6 号票
- ThreadTest4 窗口a 售出 5 号票
- ThreadTest4 窗口a 售出 4 号票
- ThreadTest4 窗口a 售出 3 号票
- ThreadTest4 窗口a 售出 2 号票
- ThreadTest4 窗口a 售出 1 号票
- ThreadTest4 窗口c 售出 9 号票
- ThreadTest4 窗口c 售出 8 号票
- ThreadTest4 窗口c 售出 7 号票
- ThreadTest4 窗口c 售出 6 号票
- ThreadTest4 窗口c 售出 5 号票
- ThreadTest4 窗口c 售出 4 号票
- ThreadTest4 窗口c 售出 3 号票
- ThreadTest4 窗口c 售出 2 号票
- ThreadTest4 窗口c 售出 1 号票
使用情况2:
- ThreadTest4 t2 = new ThreadTest4();
- (new Thread(t2,"窗口1")).start();
- (new Thread(t2,"窗口2")).start();
- (new Thread(t2,"窗口3")).start();
- (new Thread(t2,"窗口4")).start();
运行情况说明A、B、C、D四个窗口共享count这个资源(但发生了访问冲突)
- 主线程名称:main
- ThreadTest4 窗口1 售出 20 号票
- ThreadTest4 窗口2 售出 20 号票
- ThreadTest4 窗口2 售出 19 号票
- ThreadTest4 窗口2 售出 18 号票
- ThreadTest4 窗口2 售出 17 号票
- ThreadTest4 窗口2 售出 16 号票
- ThreadTest4 窗口2 售出 15 号票
- ThreadTest4 窗口2 售出 14 号票
- ThreadTest4 窗口2 售出 13 号票
- ThreadTest4 窗口2 售出 12 号票
- ThreadTest4 窗口2 售出 11 号票
- ThreadTest4 窗口2 售出 10 号票
- ThreadTest4 窗口2 售出 9 号票
- ThreadTest4 窗口2 售出 8 号票
- ThreadTest4 窗口2 售出 7 号票
- ThreadTest4 窗口3 售出 7 号票
- ThreadTest4 窗口1 售出 5 号票
- ThreadTest4 窗口2 售出 6 号票
- ThreadTest4 窗口1 售出 3 号票
- ThreadTest4 窗口4 售出 4 号票
- ThreadTest4 窗口3 售出 4 号票
- ThreadTest4 窗口1 售出 1 号票
- ThreadTest4 窗口2 售出 2 号票
3. 线程同步
多线程中涉及到共享数据时,会出现线程安全问题。就上面的售票案例来说,若没有加 synchronized 关键字,在多个线程同时使用ticket这个共享数据时,会出现同一个ticket被使用两次这样的看似不可能的情况。另外还有一种情况,事实上ticket这个共享数据是类ThreadTest4对象t2中的变量,所以若是在主线程中添加t2.run();语句的话,也是会发生线程安全问题的。
在Java里面,同步锁的概念就是这样的。任何一个Object Reference都可以作为同步锁。我们可以把Object Reference理解为对象在内存分配系统中的内存地址。
- class ThreadTest5 implements Runnable{
- private int ticket=20;
- public void run(){
- while(ticket>0){
- String threadName = Thread.currentThread().getName();
- //同步代码块(越小越好)
- synchronized(this){
- if(ticket>0){
- System.out.println(threadName + " sales ticket "+ticket);
- ticket--;
- }
- }
- }
- }
- }
- public class Demo3{
- public static void main(String[] args){
- ThreadTest5 t = new ThreadTest5();
- (new Thread(t, "窗口A")).start();
- (new Thread(t, "窗口B")).start();
- (new Thread(t, "窗口C")).start();
- (new Thread(t, "窗口D")).start();
- }
- }
运行结果
- 窗口A sales ticket 20
- 窗口A sales ticket 19
- 窗口A sales ticket 18
- 窗口A sales ticket 17
- 窗口A sales ticket 16
- 窗口A sales ticket 15
- 窗口A sales ticket 14
- 窗口A sales ticket 13
- 窗口A sales ticket 12
- 窗口A sales ticket 11
- 窗口A sales ticket 10
- 窗口D sales ticket 9
- 窗口D sales ticket 8
- 窗口D sales ticket 7
- 窗口D sales ticket 6
- 窗口D sales ticket 5
- 窗口D sales ticket 4
- 窗口D sales ticket 3
- 窗口C sales ticket 2
- 窗口B sales ticket 1
(1)同步代码块
- synchronized(类或对象){
- //需要同步的代码段
- }
(2)同步函数
(非static的情况)
- public synchronized void fun(){
- //代码段
- }
等价于(调用此同步函数的对象作为此同步函数的同步锁)
- public void fun() {
- synchronized(this) {
- //代码段
- }
- }
(static的情况)
- public static synchronized void fun() {
- //代码段
- }
静态变量或静态方法加载到内存中时,内存中没有本类对象,但一定有了该类对应的字节码文件(类名.class),该对象的类型是class。静态同步函数使用的同步锁是所在类的字节码文件。
线程同步的前提:
1)2个或2个以上的线程
2)使用同一把锁
保证同步中只有一个线程在运行。
好处:解决线程安全问题
弊端:多个线程需要判断锁,消耗资源
4. 线程通信
经典的生产者和消费者问题
假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中的产品取走消费。如果仓库中没有产品,则生产者可以将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止。如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止。显然,这是一个同步问题,生产者和消费者共享同一资源,并且,生产者和消费者之间彼此依赖,互为条件向前推进
4.1 synchronized和wait、notify、notifyAll
wait() 使得当前线程必须要等待,并释放对锁的拥有权,等到另外一个线程调用notify()或者notifyAll()方法
notify() 会唤醒一个等待当前对象的锁的线程
notifyAll() 唤醒所有一个等待当前对象的锁的线程
一个小比较
当线程调用了wait()方法时,它会释放掉对象的锁。
另一个会导致线程暂停的方法:Thread.sleep(millisecond),它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的。
- class Repository{
- private int count=0; //当前仓库存放商品数量
- private int capacity=5; //仓库容量
- private String goodsName; //商品名称
- public Repository(String goodsName){
- this.goodsName = goodsName;
- }
- public void store(String threadName){
- synchronized(this){
- while(this.count>=this.capacity){
- System.out.println("[" + threadName + "]仓库已达到最大容量 " + this.capacity + " 个 !!");
- try{this.wait();}
- catch(Exception e){}
- }
- this.count++;
- System.out.println("[" + threadName + "]仓库增加了一个"+this.goodsName+",现有"+this.goodsName+"["+this.count+"] 个");
- this.notifyAll();
- }
- }
- public void fetch(String threadName){
- synchronized(this){
- while(this.count<1){
- System.out.println("[" + threadName + "]仓库没有"+this.goodsName+"!!");
- try{
- this.wait();
- }catch(Exception e){
- }
- }
- this.count--;
- System.out.println("[" + threadName + "]仓库减少了一个"+this.goodsName+",现有"+this.goodsName+"["+this.count+"] 个");
- this.notifyAll();
- }
- }
- }
- //生产者
- class Producter implements Runnable{
- private Repository repository ;
- public Producter(Repository repository){
- this.repository = repository;
- }
- public void run(){
- String threadName = Thread.currentThread().getName();
- while(true){
- try{
- //sleep 2秒模拟生产过程
- Thread.sleep(1000);
- }catch(Exception e){
- }
- //把生产的商品存放到仓库
- repository.store(threadName);
- }
- }
- }
- //消费者
- class Consumer implements Runnable{
- private Repository repository ;
- public Consumer(Repository repository){
- this.repository = repository;
- }
- public void run(){
- String threadName = Thread.currentThread().getName();
- while(true){
- //将商品从仓库取出来
- repository.fetch(threadName);
- try{
- //sleep 4秒模拟消费过程
- Thread.sleep(2000);
- }catch(Exception e){
- }
- }
- }
- }
- public class Thread06{
- public static void main(String[] args){
- Repository repository = new Repository("馒头");
- Producter pro1 = new Producter(repository);
- Consumer con1 = new Consumer(repository);
- //生产者和消费者各2个
- (new Thread(pro1, "生产者A")).start();
- (new Thread(pro1, "生产者B")).start();
- (new Thread(con1, "消费者1")).start();
- (new Thread(con1, "消费者2")).start();
- }
- }
其中一种运行结果:
- [消费者1]仓库没有馒头!!
- [消费者2]仓库没有馒头!!
- [生产者A]仓库增加了一个馒头,现有馒头[1] 个
- [消费者2]仓库减少了一个馒头,现有馒头[0] 个
- [消费者1]仓库没有馒头!!
- [生产者B]仓库增加了一个馒头,现有馒头[1] 个
- [消费者1]仓库减少了一个馒头,现有馒头[0] 个
- [生产者A]仓库增加了一个馒头,现有馒头[1] 个
- [生产者B]仓库增加了一个馒头,现有馒头[2] 个
- [生产者A]仓库增加了一个馒头,现有馒头[3] 个
- [消费者2]仓库减少了一个馒头,现有馒头[2] 个
- [生产者B]仓库增加了一个馒头,现有馒头[3] 个
- [消费者1]仓库减少了一个馒头,现有馒头[2] 个
- [生产者A]仓库增加了一个馒头,现有馒头[3] 个
- [生产者B]仓库增加了一个馒头,现有馒头[4] 个
- [消费者2]仓库减少了一个馒头,现有馒头[3] 个
- [生产者A]仓库增加了一个馒头,现有馒头[4] 个
- [消费者1]仓库减少了一个馒头,现有馒头[3] 个
- [生产者B]仓库增加了一个馒头,现有馒头[4] 个
- [生产者A]仓库增加了一个馒头,现有馒头[5] 个
- [生产者B]仓库已达到最大容量 5 个 !!
- [消费者2]仓库减少了一个馒头,现有馒头[4] 个
- [生产者B]仓库增加了一个馒头,现有馒头[5] 个
- [生产者A]仓库已达到最大容量 5 个 !!
- [消费者1]仓库减少了一个馒头,现有馒头[4] 个
- [生产者A]仓库增加了一个馒头,现有馒头[5] 个
- [生产者B]仓库已达到最大容量 5 个 !!
- [生产者A]仓库已达到最大容量 5 个 !!
(运行结果分析)
- 1)线程“消费者1”调用repositoy.fetch()方法去仓库取馒头。进入synchronized块,刚一执行while语句,结果 this.count<1 为真,直接就wait()进入等待队列,最后释放了锁
- (就绪队列[消费者2,生产者A,生产者B],等待队列[消费者1])
- 2)线程“消费者2”同“消费者1”的遭遇是相同的(对此,我们深表同情)
- (就绪队列[生产者A,生产者B], 等待队列[消费者1,消费者2])
- 3)线程“生产者A”生产完商品后,调用repository.store()方法把商品存到了仓库中。在store方法里,使用notifyAll方法唤醒了所有在沉睡wait的线程,最后释放了锁
- (就绪队列[生产者A,生产者B,消费者1,消费者2],等待队列[])
- 4)线程“消费者2”从上次wait的地方开始执行(从哪里跌倒,就从哪里爬起来)。同样是while语句,但这次 this.count<1 不为真了,于是乎顺利脱坑,接着执行 this.count--,把仓库仅有的一个馒头给拿走了,走之前还不忘大喊一句“仓库减少了一个馒头,现有馒头[0] 个”。同样是notifyAll方法唤醒所有沉睡wait的线程,释放锁
- (就绪队列[生产者A,生产者B,消费者1,消费者2],等待队列[])
- 5)线程“消费者1”辛辛苦苦抢到了锁,终于能执行syschronized块代码了。和“消费者2”一样,也是从上次wait的地方接着执行,也是while语句,但不同的是 this.count<1 为真,线程“生产者A”生产存放到仓库的仅有的一个馒头被“消费者2”给吃了,可以想象此时的“消费者1”心里是万念俱灰的。啥也别说了,接着沉睡wait吧。(释放了锁)
- (就绪队列[生产者A,生产者B,消费者2],等待队列[消费者1])
**** 在接下来的几十个回合中,时而生产者线程夺得仓库锁,称霸武林,时而消费者线程夺得,笑傲江湖,仓库商品也是时增时减,但总体上还是生产者线程抢到的次数多,因为生产者够快,当消费者还在花2秒钟费劲的消化时,生产者早就1秒生产完毕,参与仓库锁的再次争夺了,可见“天下武功,唯快不破” *****- “仓库里没有馒头为什么不通知我?”,线程“消费者2”不满“消费者1”对仓库情况的隐瞒不报,“你要是早告诉我,我也就不用争抢仓库锁,抢到了也没用,里面根本就没有馒头,去了也是wait”。(第1、2步)
- “你还有脸说我,你把生产者A生产存放在仓库仅有的一个馒头吃了,你明知道仓库再也没有馒头了,也不告诉我,还让我傻了吧唧抢到仓库锁,去了也白搭”。“消费者1”反驳道,它同样也很委屈。(第4、5步)
- “管我什么事,抢仓库锁的有不只你一个,我也去抢了,白费了劲,还没抢到....”,线程“消费者2”道。
- “要是仓库没有馒头的时侯,只唤醒那帮生产者就好了”,线程“消费者1”和“消费者2”异口同声的说道。
处理线程通信必须遵循一种原则:对于生产者,在生产者没有生产之前,要通知消费者等待;在生产者生产之后,马上又通知消费者消费;对于消费者,在消费者消费之后,要通知生产者已经消费结束,需要继续生产新的产品以供消费。
4.2 Lock和Condition
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。
Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用
5. 停止线程的方法
- interrupt() //停止线程
- isInterrupt() //判断线程是否停止
6. 守护线程和join方法
守护线程是为其他线程提供便利服务的,当全部的用户线程结束后,守护线程才会随JVM结束工作。 thread.setDaemon(true);
- public static void main(String[] args){
- Thread t3 = new Thread(test2, "线程t3");
- t3.start();
- t3.join(); //主线程就此陷入等待,直到t3线程结束。
- }
7. 线程优先级和yield方法
线程的优先级从低到高:1-10,优先级高的的优先执行,每个新线程都继承了父线程的优先级,常量:Thread.MIN_PRIORITY 值为1,Thread.MAX_PRIORITY 值为10,Thread.NORM_PRIORITY 值为5
- void setPriority(priority) //设置线程优先级
- int getPriority() //获取线程优先级
yield() 线程从执行状态变成就绪状态。
【Java基础总结】多线程的更多相关文章
- Java基础技术多线程与并发面试【笔记】
Java基础技术多线程与并发 什么是线程死锁? 死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去,我们就可以称 ...
- Java基础之多线程篇(线程创建与终止、互斥、通信、本地变量)
线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public ...
- 关于java基础、多线程、JavaWeb基础、数据库、SSM、Springboot技术汇总
作者 : Stanley 罗昊 本人自行总结,纯手打,有疑问请在评论区留言 [转载请注明出处和署名,谢谢!] 一.java基础 1.多态有哪些体现形式? 重写.重载 2. Overriding的是什么 ...
- Java基础之多线程框架
一.进程与线程的区别 1.定义: 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比 ...
- Java基础之多线程详细分析
在了解多线程之前,先来了解一下进程与线程之间的关系. 进程和线程: 进程是指在系统中正在执行的一个程序,每个进程之间是独立的. 线程是进程的一个基本执行单元.一个进程要想执行任务,必须得有线程(每1个 ...
- java基础知识 多线程
package org.base.practise9; import org.junit.Test; import java.awt.event.WindowAdapter; import java. ...
- Java基础之多线程
1.进程和线程: 进程:正在进行的程序.每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元. 线程:进程内部的一条执行路径或者一个控制单元. 两者的区别: 一个进程至少有一个线程 ...
- 黑马程序员——【Java基础】——多线程
---------- android培训.java培训.期待与您交流! ---------- 一.概述 (一)进程 正在执行中的程序,每一个进程执行都有一个执行顺序.该顺序是一个执行路径,或者叫一个控 ...
- 黑马程序员——JAVA基础之多线程的线程间通讯等
------- android培训.java培训.期待与您交流! ---------- 线程间通讯: 其实就是多个线程在操作同一个资源,但是动作不同. wait(); 在其他线程调用此对象的notif ...
- 黑马程序员——JAVA基础之多线程的安全问题
------- android培训.java培训.期待与您交流! ---------- 导致多线程出现问题的一个特殊的状态:就绪.具备了执行资格,但是还没有获取资源. 导致安全问题的出现的原因: 1. ...
随机推荐
- supersockets命令过滤器
关键字: 命令过滤器, 命令, 过滤器, OnCommandExecuting, OnCommandExecuted SuperSocket 中的命令过滤器看起来有些像 ASP.NET MVC 中的 ...
- lrj 9.4.1 最长上升子序列 LIS
p275 d(i)是以Ai为结尾的最长上升子序列的长度 <算法竞赛入门经典-训练指南>p62 问题6 提供了一种优化到 O(nlogn)的方法. 文本中用g(i)表示d值为i的最小状态编号 ...
- H3C 虚拟模板方式配置PPP MP
- 如何检查linux是否安装了php
方法一.在终端通过php -v命令来查看一下当前php的版本.如果没有安装php,一般会提示没有php这个命令的. 2 方法二.在终端查询安装的包中是否有php,以redhat为例,则可以执行如下命令 ...
- sorted排序算法
- H3C Basic NAT配置示例
- html(四)数据库curd操作与分页查询
数据库操作curd : 1.首先要建立项目处理好自己逻辑包: 其中util工具包中建立两个工具类 jdbc连接和page分页 DBUtil.java: db工具类就是用于连接数据库的jdbc架包,里面 ...
- Ubuntu Kylin 14.04安装
早听说Ubuntu Kylin对中国本地做了很多定制的工作,想搜狗输入法.WPS,还有中国日历等.昨天没事就下载了一个Kylin试用了下,使用的方法还是使用EasyBCD软件做了个硬盘安装启动,关于E ...
- ASP.NET MVC4.0+EF+LINQ+bui+bootstrap+网站+角色权限管理系统(3)
接下来完成用户.角色的增删查改,以及用户角色.权限的设置 对用户表.角色表做了一些扩展如下[可以更加自己需要增减字段] 相应的M_UserProfile.cs.M_Roles.cs进行扩展 using ...
- vue-cli 3.0 eslint
1.关闭eslint module.exports = { configureWebpack: { devtool: 'source-map' }, lintOnSave: false } 2.修改e ...