精华内容
下载资源
问答
  • 多线程执行完释放
    千次阅读
    2021-02-26 19:43:36

    多线程中的wait与sleep到底谁释放了锁

    首先,多线程中会使用到两个延迟的函数,wait和sleep。

    wait是Object类中的方法,而sleep是Thread类中的方法。

    sleep是Thread类中的静态方法。无论是在a线程中调用b的sleep方法,还是b线程中调用a的sleep方法,谁调用,谁睡觉。

    最主要的是sleep方法调用之后,并没有释放锁。使得线程仍然可以同步控制。sleep不会让出系统资源;

    而wait是进入线程等待池中等待,让出系统资源。

    调用wait方法的线程,不会自己唤醒,需要线程调用 notify / notifyAll 方法唤醒等待池中的所有线程,才会进入就绪队列中等待系统分配资源。sleep方法会自动唤醒,如果时间不到,想要唤醒,可以使用interrupt方法强行打断。

    Thread.sleep(0) // 触发操作系统立刻重新进行一次CPU竞争。

    使用范围:

    sleep可以在任何地方使用。而wait,notify,notifyAll只能在同步控制方法或者同步控制块中使用。

    sleep必须捕获异常,而wait,notify,notifyAll的不需要捕获异常。

    释放CPU时间片

    sleep()方法:

    当程序运行到Thread.sleep(100L);时,休眠100毫秒,同时交出CPU时间片,100毫秒后,重新进入可运行状态,等待CPU重新分配时间片,而线程交出时间片时,CPU拿到时间片,由操作系统负责在客运行状态的线程中选中并分配时间片

    wait()方法:程序在运行时,遇到wait()方法,这时线程进入当前对象的等待队列并交出CPU,等待其他线程notifyALL()时,才能重新回到可运行状态,等待OS分配CPU

    java wait()notify释放锁

    调用obj.wait()会立即释放锁,,以便其他线程可以执行obj.notify(),但是notify()不会立刻立刻释放sycronized(obj)中的obj锁,必须要等notify()所在线程执行完synchronized(obj)块中的所有代码才会释放这把锁.

    //而 yield(),sleep()不会释放锁。

    java多线程什么时候释放锁—wait()、notify()

    由于等待一个锁定线程只有在获得这把锁之后,才能恢复运行,所以让持有锁的线程在不需要锁的时候及时释放锁是很重要的。在以下情况下,持有锁的线程会释放锁:

    1. 执行完同步代码块。

    2. 在执行同步代码块的过程中,遇到异常而导致线程终止。

    3. 在执行同步代码块的过程中,执行了锁所属对象的wait()方法,这个线程会释放锁,进行对象的等待池。

    除了以上情况外,只要持有锁的此案吃还没有执行完同步代码块,就不会释放锁。因此在以下情况下,线程不会释放锁:

    1. 在执行同步代码块的过程中,执行了Thread.sleep()方法,当前线程放弃CPU,开始睡眠,在睡眠中不会释放锁。

    2. 在执行同步代码块的过程中,执行了Thread.yield()方法,当前线程放弃CPU,但不会释放锁。

    3. 在执行同步代码块的过程中,其他线程执行了当前对象的suspend()方法,当前线程被暂停,但不会释放锁。但Thread类的suspend()方法已经被废弃。

    避免死锁的一个通用的经验法则是:当几个线程都要访问共享资源A、B和C时,保证使每个线程都按照同样的顺序去访问他们,比如都先访问A,再访问B和C。

    java.lang.Object类中提供了两个用于线程通信的方法:wait()和notify()。需要注意到是,wait()方法必须放在一个循环中,因为在多线程环境中,共享对象的状态随时可能改变。当一个在对象等待池中的线程被唤醒后,并不一定立即恢复运行,等到这个线程获得了锁及CPU才能继续运行,又可能此时对象的状态已经发生了变化。

    # 调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) {...} 代码段内。

    1

    2

    # 调用obj.wait()后,线程A就释放了obj的锁,否则线程B无法获得obj锁,也就无法在synchronized(obj) {…} 代码段内唤醒A。

    # 当obj.wait()方法返回后,线程A需要再次获得obj锁,才能继续执行。

    # 如果A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中的一个(具体哪一个由JVM决定)。

    # obj.notifyAll()则能全部唤醒A1,A2,A3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,A1,A2,A3只有一个有机会获得锁继续执行,例如A1,其余的需要等待A1释放obj锁之后才能继续执行。

    # 当B调用obj.notify/notifyAll的时候,B正持有obj锁,因此,A1,A2,A3虽被唤醒,但是仍无法获得obj锁。直到B退出synchronized块,释放obj锁后,A1,A2,A3中的一个才有机会获得锁继续执行。

    wait()/sleep()的区别

    前面讲了wait/notify机制,Thread还有一个sleep()静态方法,它也能使线程暂停一段时间。sleep与wait的不同点是:sleep并不释放锁,并且sleep的暂停和wait暂停是不一样的。obj.wait会使线程进入obj对象的等待集合中并等待唤醒。

    但是wait()和sleep()都可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。

    如果线程A希望立即结束线程B,则可以对线程B对应的Thread实例调用interrupt方法。如果此刻线程B正在wait/sleep/join,则线程B会立刻抛出InterruptedException,在catch() {} 中直接return即可安全地结束线程。

    需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到wait()/sleep()/join()后,就会立刻抛出InterruptedException。

    更多相关内容
  • C#多线程之所有线程执行完成后

    千次阅读 2017-07-28 10:24:09
    多线程的应用开发过程中,我们可能会遇到某些任务需要等待所有线程执行完成后,再进行处理。搜了下关于这方面的解决方案,在这里不做细说。 今天主要是想说下C#多线程中,可以利用ManualResetEvent对象,结合...
    在多线程的应用开发过程中,我们可能会遇到某些任务需要等待所有线程执行完成后,再进行处理。搜了下关于这方面的解决方案,在这里不做细说。
    今天主要是想说下C#多线程中,可以利用ManualResetEvent对象,结合WaitHandle的WaitAll(...)方法,来实现所有线程执行完毕后的处理工作。
    大家可以进一步的了解下ManualResetEvent对象。

    List<ManualResetEvent> list = new List<ManualResetEvent>();
         for ( int i = 0; i < 10; i++)
         {
             ManualResetEvent mre = new ManualResetEvent( false );
             System.Threading.Thread thread = new Thread((o) =>
             {
                 Console.WriteLine( "线程:{0}" , System.Threading.Thread.CurrentThread.ManagedThreadId);
                 ((ManualResetEvent)o).Set();
             });
             list.Add(mre);
             thread.Start(mre);
         }
         WaitHandle.WaitAll(list.ToArray());
         Console.WriteLine( "所有线程完成任务!" );
    展开全文
  • 多线程(并发执行

    千次阅读 2020-08-02 15:06:54
    但并发不是真正意义上的“同时进行”,只是将CPU划分成好几个时间片段,每个片段内执行一个任务,然后在这几个片段之间来回切换,由于CPU处理速度快,让用户感觉像是个任务在同时执行。 区别: 并行是某一时刻,...

    一、概念区分

    1、并行与并发

    并行

    ​ 当系统有一个以上CPU时,同一时刻,当一个CPU在执行一个任务时,另一个CPU在执行另一个任务,两个任务互不抢占CPU资源,可以同时进行(多核CPU,一个CPU执行一个进程)

    并发

    ​ 一个CPU,同一时间,有多个任务在执行。但并发不是真正意义上的“同时进行”,只是将CPU划分成好几个时间片段,每个片段内执行一个任务,然后在这几个片段之间来回切换,由于CPU处理速度快,让用户感觉像是多个任务在同时执行。

    区别:

    • 并行是某一时刻,真正有多个程序在运行;并发是在一段时间内,宏观上多个程序同时运行。

    • 并发,指多个事情,在同一时间段内同时发生了;多个任务之间是相互抢占资源的

      并行,指多个事情,在同一时间点上同时发生了;多个任务之间是不相互抢占资源的

    • 只有在多个CPU或CPU多核时,才会发生并行,否则看似同时发生的事情,都是并发的

    2、进程与线程

    进程

    ​ 指系统中正在运行的一个应用程序;是资源分配的最小单位

    线程

    ​ 是进程内独立执行的一个单一顺序的控制流;是系统分配处理器时间资源的基本单位;是程序执行的最小单位

    在这里插入图片描述

    区别

    • 进程之间数据不共享
    • 线程之间可以共享资源

    二、线程的生命周期

    ​ 生命周期:在程序开发中,一个对象从被实例化完成,到这个对象使用结束并销毁的整个过程,类似于人的一生

    线程的生命周期:一个线程被实例化,到这个线程销毁的整个过程

    线程的状态

    • 新建:New

    ​ 一个线程被实例化完成,但是还没有做任何动作

    • 就绪:Ready

    ​ 一个线程已经被启动 (调用start()方法),开始争抢CPU的时间片

    • 运行:Run

    ​ 一个线程抢到了CPU的时间片,开始执行这个线程中的逻辑

    • 阻塞:Interrupt

    ​ 一个线程在运行的过程中,受到某些操作的影响,放弃已经获取的CPU时间片,并且不再参与CPU时间片的争抢,此时线程处于挂起状态

    • 死亡:Dead

    ​ 一个线程对象需要被销毁
    在这里插入图片描述

    三、开启线程的方式

    1、继承Thread类,实现其run()方法

    //要自定义一个线程类,并且该类要继承Thread类
    class MyThread extends Thread{
    	//重写run方法
    	@Override
    	public void run() {
    		for(int i=0;i<5;i++) {
    			System.out.println("子线程逻辑:"+i);
    		}
    	}
    }
    public class ThreadClass {
    	public static void main(String[] args) {
    		MyThread mt=new MyThread();  //新建
    		mt.start();    //就绪
    		System.out.println("主线程逻辑执行结束");
    	}
    }
    /*输出结果:
    主线程逻辑执行结束
    子线程逻辑:0
    子线程逻辑:1
    子线程逻辑:2
    子线程逻辑:3
    子线程逻辑:4
    */
    

    如果是串行运行,则“主线程逻辑执行结束”这句话应该最后执行。但由于并发执行的多线程存在,使得主程序逻辑先执行完毕,在执行子线程

    注意:只有调用start方法才会启动线程,并且使该线程执行run方法;如果直接调用run方法,则并没有开启线程,即线程不会进入就绪状态。

    2、实现Runnable接口,实现其run()方法

    /*
     * Runnable接口是一个函数式接口,可以采用Lambda表达式实现其run方法
     */
    public class ThreadClass {
    	public static void main(String[] args) {
    		Runnable r1=()->{
    			for(int i=0;i<5;i++) {
    				System.out.println("子线程中的逻辑:"+i);
    			}
    		};
    		Thread t=new Thread(r1);  //新建
    		t.start();    //就绪
    		System.out.println("主线程逻辑执行结束");
    	}
    }
    //输出结果同上
    

    3、实现Callable接口

    与Runnable接口类似,只是该方式有返回值,但Runnable没有返回值

    需要使用一个中介FutureTask

    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    public class Test {
    	public static void main(String[] args) {
    		//返回值是int类型
    		Callable callable=()->{
    			int result=0;
    			for(int i=0;i<100;i++) {
    				result+=i;
    			}
    			return result;
    		};
    		//Thread thread=new Thread(callable); 不能直接像创建Runnable接口一样
    		//知道返回值是int性。使用泛型约束
    		FutureTask<Integer> task=new FutureTask<> (callable);
    		
    		Thread thread=new Thread(task);
    		thread.start();
    		
    		//获取计算结果
    		Integer integer = null;
    		try {
    			integer = task.get();  //该方法会抛出两个异常,需要手动处理
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		} catch (ExecutionException e) {
    			e.printStackTrace();
    		}  
    		System.out.println(integer);
    	}
    }
    

    4、异同点

    • 继承Thread类,可读性更高,但是如果某个类类继承了Thread类,那么该类将不能再继承其他类,这有可能会破坏原有的继承结构
    • 使用Runnable接口,程序可对象降低,但不会破坏继承结构,一般多使用这种方式

    四、线程的常用方法

    1、线程的命名setName

    • 实例化一个线程,使用setName()方法
    • 实例化一个线程的同时,通过构造方法对线程进行命名
    • 3、使用用户自定义的线程类,在实例化的同时,进行名字的赋值
      需要给自定义线程类添加对应的构造方法
    class MyThread extends Thread{
    	public MyThread() {}
    	public MyThread(String name) {
    		this.setName(name);     //使用setName()方法
    		//super(name);  //直接调用父类的构造方法
    	}
    }
    public class ThreadClass {
    	public static void main(String[] args) {
    		//1、实例化一个线程,使用setName()方法
    		Thread t=new Thread();
    		t.setName("用户线程1");
    		System.out.println(t.getName());
    
    		//2、实例化一个线程的同时,通过构造方法对线程进行命名
    		//  构造方法:Thread(Runnable r,String name);
    		Thread t2=new Thread(()->{},"用户线程2"); 
    		System.out.println(t2.getName());
    		
    		//3、使用用户自定义的线程类,在实例化的同时,进行名字的赋值
    		//   需要给自定义线程类添加对应的构造方法
    		MyThread t3=new MyThread("用户线程3");
    		System.out.println(t3.getName());
    	}
    }
    

    2、线程休眠sleep(Run->Interrupt)

    1. 调用**sleep()**方法,参数:以毫秒为单位的时间差
    2. 会抛出InterruptedException异常,需要处理
    3. 使得线程由运行状态变为阻塞状态,当休眠时间到达时,才会重新变为就绪状态。即使此时系统中没有其他可执行的线程,处于sleep的线程也依然不会执行
    class MyThread extends Thread{
    	//重写run方法
    	@Override
    	public void run() {
    		for(int i=0;i<5;i++) {
    			System.out.println(+i);
    			//线程休眠
    			//参数:以毫秒为单位
    			//需要捕获异常
    			try {
    				Thread.sleep(1000);  //休眠1秒
    			} 
    			catch (InterruptedException e) {
    				e.printStackTrace();
    			}   
    		}
    	}
    }
    public class ThreadClass {
    	public static void main(String[] args) {
    		//调用threadSleep方法
    		threadSleep();
    	}
        /****线程休眠****/
    	public static void threadSleep() {
    		//实例化一个线程
    		MyThread mt=new MyThread();
    		mt.start();
    	}
    }
    //输出形式:每隔1秒输出一个i值
    

    3、线程的优先级setPriority

    1. 调用**setPriority()**方法,参数:[0,10]范围内的一个整数,默认是5
    2. 设置优先级,只是设置这个线程可以抢到CPU时间片的概率,并不是优先级高的线程一定能抢到CPU时间片(不是优先级高的线程一定先执行,也不是优先级高的线程执行完再执行其他线程)
    3. 设置优先级必须要放在线程开始(start)之前
    public class ThreadClass {
    	public static void main(String[] args) {
    		threadPriority();
    	}
        /****设置线程的优先级***/
    	public static void threadPriority() {
    		Runnable r=()->{
    			for(int i=0;i<5;i++){
    					System.out.println(Thread.currentThread().getName()+":"+i);
    			}
    		};
            //1、线程实例化
    		Thread t1=new Thread(r,"Thread-1");
    		Thread t2=new Thread(r,"Thread-2");
    		
    		//2、设置优先级, 必须要将该操作放在线程开始(start)之前
    		t1.setPriority(10);
    		t2.setPriority(1);
    		
            //3、线程启动
    		t1.start();
    		t2.start();
    	}
    }
    //输出结果:交替执行
    

    4、线程的礼让yield(Run->Ready)

    1. 调用**yield()**方法,类方法
    2. 线程礼让是指让当前运行的线程释放自己的CPU资源,由运行状态,回到就绪状态。**但并不意味着一定去执行另一个线程,**此时依然是两个线程进行CPU时间片的抢夺
    public class ThreadClass {
    	public static void main(String[] args) {
    		threadYield();
    	}
    	/***线程的礼让***/
    	public static void threadYield() {
    		Runnable r=()->{
    			for(int i=0;i<10;i++) {
    				System.out.println(Thread.currentThread().getName()+":"+i);
    				//线程礼让
    				if(i==3) {
    					Thread.yield();
    				}
    			}
    		};
    		Thread t1=new Thread(r,"Thread-1");
    		Thread t2=new Thread(r,"Thread-2");
    		
    		t1.start();
    		t2.start();
    	}
    }
    /*输出结果:
    Thread-2:0
    Thread-2:1
    Thread-2:2
    Thread-2:3   //Thread-2礼让,CPU被Thread-1抢到
    Thread-1:0
    Thread-1:1
    Thread-1:2
    Thread-1:3  //Thread-1礼让,但是CPU还是被Thread-1抢到,Thread-1继续执行
    Thread-1:4
    Thread-1:5
    Thread-1:6
    Thread-1:7
    Thread-1:8
    Thread-1:9   //Thread-1执行完毕,Thread-2接着执行
    Thread-2:4
    Thread-2:5
    Thread-2:6
    Thread-2:7
    Thread-2:8
    Thread-2:9
    */
    

    5、线程合并join

    1. 执行join的线程,在该过程中,其他线程阻塞,待此线程执行完毕,再执行其他线程。(插队)
    2. 抛出InterruptException异常
    public class JoinTest {
    
    	public static void main(String[] args) {
    		Runnable runnable=()->{
    			for(int i=0;i<100;i++) {
    				System.out.println("vip线程"+i);
    			}
    		};
    		
    		Thread thread=new Thread(runnable);
    		thread.start();
    		//主线程输出100次
    		for(int i=0;i<100;i++) {
    			/*
    			 * 当主线程运行到第50次时,调用join方法,那么此时会等join方法加入的线程执行完毕,在执行主线程
    			 * */
    			if(i==50) {
    				try {
    					thread.join();
    				} 
    				catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    			System.out.println("main"+i);
    		}
    	}
    }
    /*输出:在50之前,主线程和子线程交替执行,但是等到主线程为50时,此时子线程会执行直到100结束,然后主线程才执行
    */
    

    6、守护线程setDaemon

    1. 如果所有的用户线程结束,那么守护线程会自动死亡;虚拟机不需要等待守护线程执行结束
    2. setDaemon默认是false,如果要设置一个线程为守护线程,则改为true即可
    public class DaemonTest {
    	public static void main(String[] args) {
    		Runnable r1=()->{
    			while(true) {
    				System.out.println("守护线程");
    			}
    		};
    		
    		for(int i=0;i<10;i++) {
    			System.out.println("主线程"+i);
    		}
    		
    		Thread thread=new Thread(r1);
    		thread.setDaemon(true);  //默认是false,表示用户线程
    		thread.start();
    	}
    }
    //守护线程是一个死循环,但是等待主线程执行结束后,该线程会自动停止
    

    五、线程安全问题

    临界资源:多个线程共享的资源。当多个线程同时去访问这个共享资源时,会出现线程安全问题

    1、产生的原因

    当一个线程在访问并操作某个资源的过程中,还没来得及完全修改该资源,CPU时间片就被其他线程抢走

    //用四个线程模拟四个售票员卖票,仓库中的余票即为临界资源
    class TicketCenter{
    	//描述剩余票的数量
    	public static int restCount=100;
    }
    public class SourseProblem {
    	public static void main(String[] args) {
    		Runnable r=()->{
    			//当余票大于0时,可以继续售票
    			while(TicketCenter.restCount>0) {
    				System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+ --TicketCenter.restCount+"张");
    			}
    		};
    		//四个线程模拟四个售票员,线程名模拟售票员名
    		Thread t1=new Thread(r,"Thread-1");
    		Thread t2=new Thread(r,"Thread-2");
    		Thread t3=new Thread(r,"Thread-3");
    		Thread t4=new Thread(r,"Thread-4");
    		
    		t1.start();
    		t2.start();
    		t3.start();
    		t4.start();
    	}
    }
    

    输出结果:

    在这里插入图片描述

    出现临界资源问题,这是因为一个线程在计算余票的过程中,还没来的及将计算、或计算后的结果还没来得及赋给restCount,CPU就被其他线程抢走,此时其他线程中的余票是当前抢到时刻的余票值。

    2、解决方法

    • JVM实现的synchronized
    • JDK实现的ReentrantLock

    方式一:使用同步代码块

    用synchronized修饰多线程需要访问的代码

    class TicketCenter{
    	//描述剩余票的数量
    	public static int restCount=100;
    }
    public class SourseProblem {
    	public static void main(String[] args) {
    		Runnable r=()->{
    			//当余票大于0时,可以继续售票
    			while(TicketCenter.restCount>0) {
    				//同步监视器
    				synchronized("") {
    					if(TicketCenter.restCount<=0) {
    						return;
    					}
    					System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+ --TicketCenter.restCount+"张");
    				}
    			}
    		};
    		//四个线程模拟四个售票员,线程名模拟售票员名
    		Thread t1=new Thread(r,"Thread-1");
    		Thread t2=new Thread(r,"Thread-2");
    		Thread t3=new Thread(r,"Thread-3");
    		Thread t4=new Thread(r,"Thread-4");
    		
    		t1.start();
    		t2.start();
    		t3.start();
    		t4.start();
    	}
    }
    

    方法二:同步方法:使用关键字synchronized修饰的方法

    将上面的同步代码段用一个方法实现

    1. 静态方法:同步监视器就是:当前类.class
    2. 非静态方法:同步监视器是 this
    class TicketCenter{
    	//描述剩余票的数量
    	public static int restCount=100;
    }
    public class SourseProblem {
    	public static void main(String[] args) {
    		Runnable r=()->{
    			while(TicketCenter.restCount>0) {
    				soldTicket();
    			}
    		};
    		Thread t1=new Thread(r,"Thread-1");
    		Thread t2=new Thread(r,"Thread-2");
    		Thread t3=new Thread(r,"Thread-3");
    		Thread t4=new Thread(r,"Thread-4");
    	
    		t1.start();
    		t2.start();
    		t3.start();
    		t4.start();
    	}
    	//同步方法
    	public synchronized static void soldTicket(){
    		if(TicketCenter.restCount<=0) {
    			return;
    		}
    		System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+ --TicketCenter.restCount+"张");
    	}
    }
    

    方式三:同步锁

    显式定义同步锁对象来实现同步

    class TicketCenter{
    	//描述剩余票的数量
    	public static int restCount=100;
    }
    public class SourseProblem {
    	public static void main(String[] args) {
    		//实例化一个锁对象
    		ReentrantLock rt=new ReentrantLock();
    		
    		Runnable r=()->{
    			while(TicketCenter.restCount>0) {
    				//对临界资源上锁
    				rt.lock();
    				
    				if(TicketCenter.restCount<=0) {
    					return;
    				}
    				System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+ --TicketCenter.restCount+"张");
    				
    				//对临界资源解锁
    				rt.unlock();
    			}
    		};
    		Thread t1=new Thread(r,"Thread-1");
    		Thread t2=new Thread(r,"Thread-2");
    		Thread t3=new Thread(r,"Thread-3");
    		Thread t4=new Thread(r,"Thread-4");
    		
    		t1.start();
    		t2.start();
    		t3.start();
    		t4.start();
    	}
    }
    

    3、死锁

    多个线程彼此持有对方所需要的锁,而不释放自己的锁

    //线程A、B互相等待对方释放拥有的锁
    public class DeadLock {
    	public static void main(String[] args) {
            
    		Runnable runnable1=()->{
    			synchronized("A"){
    				System.out.println("A线程持有了A锁,等待B锁");
    				//此时A线程已经持有A锁了,让它继续持有B锁
                    /*为了确保产生死锁
                    try {
    					Thread.sleep(1000);
    				} 
    				catch (InterruptedException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}*/
                    
    				synchronized("B"){
    					System.out.println("A线程持有了A锁和B锁");
    				}
    			}
    		};
    		
    		Runnable runnable2=()->{
    			synchronized("B"){
    				System.out.println("B线程持有了B锁,等待A锁");
    				//此时B线程已经持有B锁了,让它继续去持有A锁
    				synchronized("A"){
    					System.out.println("B线程持有了A锁和B锁");
    				}
    			}
    		};
    		
    		Thread t1=new Thread(runnable1);
    		Thread t2=new Thread(runnable2);
    		
    		t1.start();
    		t2.start();
    	}
    }
    /*输出结果:
    B线程持有了B锁,等待A锁
    A线程持有了A锁,等待B锁
    (程序未结束)
    */
    

    上述代码其实不能完全产生死锁,如果在A线程获取B锁之前,B线程都没有获得执行机会,那么B线程就不会获取到B锁,此时程序依然会执行,不会产生死锁。为了一定产生死锁情况,可以在A线程执行过程中调用一个sleep方法。

    4、线程通信:解决死锁的办法

    方式1:synchronized下的通信

    • wait():等待,当前的线程释放对同步监视器的锁定,并且让出CPU资源,使得当前的线程进入等待队列中
    • notify():通知,唤醒在此同步监视器上等待的一个线程(具体哪一个由CPU决定),使这个线程进入锁池
    • notifyAll():通知,唤醒在此同步监视器上等待的所有线程,使这些线程进入锁池
    public class DeadLock {
    	public static void main(String[] args) {
            
    		Runnable runnable1=()->{
    			synchronized("A"){
    				System.out.println("A线程持有了A锁,等待B锁");
    				//A线程释放A锁(捕获异常)
    				try {
    					"A".wait();
    				} 
    				catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    				
    				synchronized("B"){
    					System.out.println("A线程持有了A锁和B锁");
    				}
    			}
    		};
    		
    		Runnable runnable2=()->{
    			synchronized("B"){
    				System.out.println("B线程持有了B锁,等待A锁");
    				
    				synchronized("A"){
    					System.out.println("B线程持有了A锁和B锁");
    					//此时B线程已经执行完成了,但是A线程任然还在等待,因此需要唤醒A线程
    					"A".notify();
    				}
    			}
    		};
    		
    		Thread t1=new Thread(runnable1);
    		Thread t2=new Thread(runnable2);
    		
    		t1.start();
    		t2.start();
    	}
    }
    /*输出结果:
    A线程持有了A锁,等待B锁
    B线程持有了B锁,等待A锁
    B线程持有了A锁和B锁
    A线程持有了A锁和B锁
    */
    

    方式2:Lock锁下的通信,采用Condition控制通信。JUC中的类(java.util.comcurrent类)

    • await():等价于wait()
    • signal():等价于notify()
    • signalAll():等价于notifyAll()

    4、多线程下的单例类

    懒汉式单例类会出现问题

    //定义一个单例类
    class Boss{
    	//构造器私有化
    	private Boss() {
    		System.out.println("一个Boss对象被实例化了");
    	}
    	private static Boss instance=null;
    	//外部类只能通过该方法获取Boss类的实例
    	public static Boss getBoss() {
    		if(instance==null) {
    			instance=new Boss();
    		}
    		return instance;
    	}
    }
    public class SingletonTest {
    	public static void main(String[] args) {
    		Runnable runnable=()->{
    			Boss.getBoss();
    		};
    		//开辟了100条线程去获取这Boss实例
    		for(int i=0;i<100;i++) {
    			new Thread(runnable).start();
    		}
    	}
    }
    

    当多线程去执行这个单例类时,还是希望只产生一个实例对象,但程序输出结果明显不是,这是由于多线程导致的。

    修改方式1:对临界资源上锁,使用同步代码

    //定义一个单例类
    class Boss{
    	//构造器私有化
    	private Boss() {
    		System.out.println("一个Boss对象被实例化了");
    	}
    	private static Boss instance=null;
    
    	public static Boss getBoss() {
            //同步代码段
    		synchronized("") {
    			if(instance==null) {
    				instance=new Boss();
    			}
    		}
    		return instance;
    	}
    
    }
    
    public class SingletonTest {
    	public static void main(String[] args) {
    		Runnable runnable=()->{
    			Boss.getBoss();
    		};
    		//开辟了100条线程去获取这Boss实例
    		for(int i=0;i<100;i++) {
    			new Thread(runnable).start();
    		}
    	}
    }
    

    修改方式2:对临界资源上锁,使用同步方法

    class Boss{
    	//构造器私有化
    	private Boss() {
    		System.out.println("一个Boss对象被实例化了");
    	}
    	private static Boss instance=null;
    	//同步方法
    	public static synchronized Boss getBoss() {
    		if(instance==null) {
    			instance=new Boss();
    		}
    		return instance;
    	}
    }
    
    public class SingletonTest {
    	public static void main(String[] args) {
    		Runnable runnable=()->{
    			Boss.getBoss();
    		};
    		//开辟了100条线程去获取这Boss实例
    		for(int i=0;i<100;i++) {
    			new Thread(runnable).start();
    		}
    	}
    }
    

    六、线程池

    线程池在系统启动时就创建大量空闲的线程。提前创建多个线程,放入线程池,使用时直接从线程池中获取,使用完放回池中

    1、作用

    可以避免频繁创建销毁线程的过程,实现充分利用

    • corePoolSize:核心池的大小(可以放多少个线程)
    • maximumPoolSize:最大线程数(一次可以同时运行的线程数量)
    • keepAliveTime:线程没有任务时最多保持多长时间后会终止

    2、创建方式

    • ExecutorService接口:线程池真正的接口
    • Executor:创建线程的工具类,调用该类的newFixedThreadPool(corePoolSizesize)方法来创建线程池
    • execute:执行Runnable接口的,无返回值
    • Future submit:执行Callable接口的,有返回值
    • shutdown:关闭连接
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class ThreadPoolTest {
    	public static void main(String[] args) {
    		Runnable r=()->{
    			System.out.println(Thread.currentThread().getName());
    		};
    		//创建线程池,设置大小为10
    		ExecutorService service=Executors.newFixedThreadPool(10);
    		//执行
    		service.execute(r);
    		service.execute(r);
    		service.execute(r);
    		service.execute(r);
            //关闭连接
    		service.shutdown();
    	}
    }
    /*输出结果:
    pool-1-thread-3
    pool-1-thread-4
    pool-1-thread-2
    pool-1-thread-1
    */
    

    七、JUC组件

    1、未来任务FutureTask

    利用Callable创建线程时,有返回值,该值由Future进行封装,FutureTask实现了RunnableFuture接口,而该接口继承自Runnable和Future接口,因此FutureTask既可以当做一个任务执行,也可以有返回值。

    当计算一个任务需要很长时间时,可使用FutureTask来封装这个任务,使得主线程在完成自己的任务后在去获取这个计算结果

    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    public class FutureTaskTest {
    	public static void main(String[] args) {
    		//创建一个Clallable接口,有返回值,给子线程执行
    		Callable<Integer> cla=()->{
    			int result=0;
    			for(int i=0;i<100;i++) {
    				Thread.sleep(10);  //每一次计算时都让让主线程执行一段时间
    				result+=i;
    			}
    			return result;
    		};
    		//新建一个FutureTask实例
    		FutureTask<Integer> futureTask=new FutureTask<>(cla);
    		//执行计算任务的线程
    		Thread t1=new Thread(futureTask);
    		t1.start();
    		
            //创建Runnable接口,给主线程执行
    		Runnable runnable=()->{
    			System.out.println("主线程任务正在执行");
    			try {
    				Thread.sleep(10);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		};
    		
    		Thread t2=new Thread(runnable);
    		t2.start();
    		
    		//得到有返回值的输出
    		try {
    			System.out.println(futureTask.get());
    		} catch (InterruptedException | ExecutionException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    }
    /*输出结果:
    另一个线程任务正在执行
    4950
    */
    //如果将Callable执行体中的Thread.sleep(10);去掉,则执行结果为:4950  另一个线程任务正在执行。
    

    2、阻塞队列BlockingQueue

    利用BlockingQueue作为线程同步的工具,主要用来实现消费者生产者设计模式。详见《生产者消费者设计模式》

    3、叉链接ForkJoin

    主要用于并行计算中,将大的任务分成小的任务进行计算,再把小任务的结果合并成总的计算结果

    展开全文
  • 多线程,我们更多是用来让不同的线程做相同的事情,如果让不同的线程执行不同的任务,并且每个线程任务的数量也不一样,还可以自由的添加任何数量的任务,那么下面的教程解决了这个问题,包括多线程分配单个不同的...

    多线程,我们更多是用来让不同的线程做相同的事情,如果让不同的线程执行不同的任务,并且每个线程任务的数量也不一样,还可以自由的添加任何数量的任务,那么下面的教程解决了这个问题,包括多线程分配单个不同的任务,多线程分配多个不同的任务,让每个线程根据游戏角色不同的级别自动选择不同的多个任务。

    易语言大漠多线程让不同的线程账号执行不同的多个任务

     

    功能说明

    1.每个线程分配单个不同的任务
    2.每个线程分配多个不同的任务
    3.每个线程根据游戏账号等级自动调用不同的任务
    重要说明
    **既然是每个线程执行的不同任务,为何什么不用多进程对于多任务呢?(资源占用厉害)
    采用多线程并发,这样可以节约系统资源,这也是本套模板的价值所在。
    **当然也可以每个线程分配相同的任务!!!

    实现步骤:

    1.课程功能演示说明
    2.易语言快速插入大漠类模块和免注册调用大漠
    3.辅助界面组件设计
    4.读取游戏信息到超级列表框
    5.给不同的线程分配不同的单个任务
    6.启动脚本主线程实现任务流程控制
    7.分配任务子程序自动判断任务名称开始单任务线程(多线程不同单任务)
    8.为每个线程分配不同的多个任务
    9.为不同的线程分割多个任务实现多线程分别执行多个不同任务
    10.读取游戏等级给线程信息等级赋值
    11.根据角色等级给不同的线程分配多个不同的任务
    12.读取等级文本到超级列表框并为不同等级添加任务
    13.线程角色等级自动读取分配预定任务
    14.为超级列表框置状态图片
    15.不同线程时时显示任务状态
    16.判断任务全部完成销毁线程释放大漠优化脚本

    媒体预览

    https://v.qq.com/x/page/e32769m4hw3.html
    https://www.bilibili.com/video/BV1TU4y1A7ub
    https://www.zhihu.com/zvideo/1422120896434180096
    https://v.youku.com/v_show/id_XNTgwNjcwNTY0OA==.html
    https://www.ixigua.com/7008701474788082188
    https://haokan.baidu.com/v?vid=11231566348991221483

    展开全文
  • Java利用多线程执行SQL减少执行时间提高效率 首先这是个人在实际的项目中使用的代码,部分敏感代码没有上,不过不影响对代码的理解: 1.实现自己的线程 /** * * @author * @date */ package ...
  • 多线程轮流交替执行三种实现方法

    千次阅读 2020-07-01 09:12:33
    A线程拿到锁,执行代码 B线程拿到锁,执行代码 A线程拿到锁,执行代码 ...... public class FairLockDemo { public static void main(String[] args) { Runnable target = new Runnable(){ int i = 10; ...
  • 我们都知道, 在使用多线程编程的时候,每个线程运行的顺序都是随机的, 它由CPU的线程调度机制决定执行哪个线程; 我们可以看看正常使用多线程编程时程序的运行顺序: import java.util.ArrayList; import java.util....
  • Python中的线程终止与内存释放

    千次阅读 2020-11-26 03:19:30
    但我注意到,内存并没有释放(gc.get_objects()不断增长)。实际上,这些对象是列表、dict等,而不是文件。在有没有办法手动释放资源?代码:import ctypesdef terminate_thread(thread):"""Terminates a python ...
  • 线程结束资源释放

    千次阅读 2016-01-13 16:28:28
    线程创建时,系统会分配给线程一些资源,我们可以看到的就是线程描述符,线程堆栈,在系统内部还会有更复杂的系统维护一些信息,...线程执行结束后释放资源的三种方法: 利用这些方法,我们可以避免线程退出时,系
  • java多线程什么时候释放

    千次阅读 2019-05-12 20:52:47
    由于等待一个锁定线程只有在... 1、当前线程的同步方法、代码块执行结束的时候释放 2、当前线程在同步方法、同步代码块中遇到break 、 return 终于该代码块或者方法的时候释放。 3、当前线程出现未处理的erro...
  • 线程 ** 学习目标 ** 能够知道线程的作用 1. 线程的介绍 在Python中,想要实现多任务除了使用进程,还...多线程可以完成多任务 4. 小结 线程是Python程序中实现多任务的另外一种方式,线程的执行需要cpu调度来完成。
  • 多线程控制线程的执行顺序

    千次阅读 2018-04-19 00:19:20
    如何控制线程执行的顺序?...先来一段多线程执行的代码。public class Test { public static void main(String[] args) { Thread t1 = new Thread(new MyThread1()); Thread t2 = new Thread(...
  • 在学习java多线程这一块知识的时候,遇到了很多个关于线程锁什么时候释放的问题,现总结几种释放锁和不释放锁的操作如下
  • 多线程中实现线程串行执行

    千次阅读 2018-03-06 22:16:06
    转自http://my.oschina.net/mingyuanwang/blog/493281?p=1为了控制线程执行的顺序,如ThreadA-&gt;ThreadB-&gt;ThreadC-&gt;ThreadA循环执行三个线程,我们需要确定唤醒、等待的顺序。这时我们可以同时...
  • } //线程任务开始执行 System.out.println(Thread.currentThread().getName() + "开始执行"); try { //模拟线程任务执行 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); break; } ...
  • 题目描述:如何保证个线程的顺序执行? join方式实现 join方式即使用Thread.join方法来实现。Thread.join含义当前线程...上面源码就是主线程join的实现方式,其原理就是保证执行线程执行完毕再start后续线程,从而.
  • 万字图解Java多线程

    万次阅读 多人点赞 2020-09-06 14:45:07
    java多线程我个人觉得是javaSe中最难的一部分,我以前也是感觉学会了,但是真正有多线程的需求却不知道怎么下手,实际上还是对多线程这块知识了解不深刻,不知道多线程api的应用场景,不知道多线程的运行流程等等,...
  • Java多线程之线程安全问题

    千次阅读 多人点赞 2022-03-31 11:02:50
    本篇文章介绍的内容为Java多线程中的线程安全问题,此处的安全问题并不是指的像黑客入侵造成的安全问题,线程安全问题是指因多线程抢占式执行而导致程序出现bug的问题。
  • } } } 执行结果如下:等待插队线程执行完,才继续执行主线程! 路漫漫其修远兮,吾必将上下求索~ 如果你认为i博主写的不错!写作不易,请点赞、关注、评论给博主一个鼓励吧**转载请注明出处哦** Java多线程扩展:...
  • 对于在校学习期间的计算机、软件工程的学生来说,只要学到 Java 多线程,就开始犯迷糊了! 刚知道咋打开 IDEA,费劲扒拉的写个 HelloWorld,就要上手搞多线程绝对是史诗级理解难度。这东西怎么跑起来的、怎么还有一...
  • Python多线程并发编程

    千次阅读 多人点赞 2021-07-27 21:50:51
    GIL使得同一时刻一个CPU只能有一个线程执行字节码, 无法将个线程映射到个CPU上执行。 GIL会根据执行的字节码行数以及时间释放GIL,GIL在遇到IO的操作时候会主动释放 # GIL会释放,最后的结果不定。释放的位置...
  • 由于等待一个锁定线程只有在获得这把锁之后,才能恢复运行,所以让持有锁... 在执行同步代码块的过程中,执行了锁所属对象的wait()方法,这个线程释放锁,进行对象的等待池。 除了以上情况外,只要持有锁的此案吃...
  • 架构师:『试试使用多线程优化』 第二天 头发很多的程序员:『师父,我已经使用了多线程,为什么接口还变慢了?』 架构师:『去给我买杯咖啡,我写篇文章告诉你』 ……吭哧吭哧买咖啡去了 在实际工作中,错误...
  • 在编程工作中,我们经常...接下来我们会映照上图介绍多线程执行过程中经历的五种状态: 1. 新建状态: 新建状态就是我们通过new关键字实例化出一个线程类的对象时的状态。 public class IsAThread extends Thre...
  • C++多线程详细讲解

    万次阅读 多人点赞 2021-03-19 20:33:26
    C++多线程基础教程 目录 1 什么是C++多线程? 2 C++多线程基础知识 2.1 创建线程 2.2 互斥量使用 lock()与unlock(): lock_guard(): unique_lock: condition_variable: 2.3 异步线程 async与future: shared_future ...
  • 14 如何线程按顺序执行

    千次阅读 2020-08-12 21:48:06
    如何控制多线程执行顺序? 方法一:使用join()方法让一个线程强制运行 调用一个线程的join()方法就可以让这个线程强制运行,并且它会阻塞主线程的运行。 原理:调用join方法,会调用join(0)方法,当参数为0时,会...
  • Java多线程超详解

    万次阅读 多人点赞 2019-06-11 01:00:30
    随着计算机的配置越来越高,我们需要将进程进一步优化,细分为线程,充分提高图形化界面的多线程的开发。这就要求对线程的掌握很彻底。 那么话不多说,今天本帅将记录自己线程的学习。 线程的相关API //获取当前...
  • Java多线程学习(吐血超详细总结)

    万次阅读 多人点赞 2015-03-14 13:13:17
    本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。
  • python的多线程

    千次阅读 2022-05-28 19:16:07
    python多线程 一、线程的概念 线程是CPU分配资源的基本单位。当一程序开始运行,这个程序就变成了一个进程...多线程的程序设计的特点就是能够提高程序执行效率和处理速度。python程序可以同时并行运行多个相对独立的线
  • 多线程 什么是线程和进程?他们是什么关系? 进程:在操作系统中能够独立运行,并且作为资源分配的基本单位。它表示运行中的程序。系统运行一个程序就是一个进程从创建、运行到消亡的过程。 线程:是一个比进程更小的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 517,392
精华内容 206,956
关键字:

多线程执行完释放