Java多线程常用函数使用

本文介绍了Java中多线程同步的方法,包括使用`join()`函数确保线程执行顺序,利用`Lock`接口实现线程间的精确控制,以及`synchronized`关键字保证变量可见性。通过示例代码详细解析了如何在并发场景下确保线程安全,同时展示了在售票窗口场景下的应用。
摘要由CSDN通过智能技术生成

1:sleep(n):让线程等待n毫秒后开始运行

2:join()函数,让使用该函数的线程执行完之后,整个程序才能继续往下执行。可谓是优先级拉满了。

例题:

编程要求

请仔细阅读右侧代码,根据方法内的提示,在Begin - End区域内进行代码补充,具体任务如下:

  • 创建自定义线程,实现求第num项斐波那契数列的值num0开始,并且在main函数中获取子线程最终计算的结果。

测试说明

补充完代码后,点击测评,平台会对你编写的代码进行测试,当你的结果与预期输出一致时,即为通过。

输入:5 输出:子线程计算结果为:5

输入:8 输出:子线程计算结果为:21

输入:10 输出:子线程计算结果为:55

package step2;

import java.util.Scanner;

public class Task {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int num = sc.nextInt();
		//请在此添加实现代码
     /********** Begin **********/
		MyThread t=new MyThread(num);
		t.start();
		try {
			t.join();
		}catch(Exception e)
		{
			e.printStackTrace();
		}
		System.out.print("子线程计算结果为:"+t.jieguo);

    /********** End **********/
		
		
	}
}


		//请在此添加实现代码
   /********** Begin **********/
class MyThread  extends Thread   {
	private int num;
	protected int jieguo=0;
	int a=1,b=1;
	public MyThread(int num)
	{
		this.num=num;
	}
	
	public void run()
	{
		int a=1,b=1,c=0;
		if(this.num==1||this.num==2)
		{
			this.jieguo=1;
		}
		else
		{
			num=num-2;
		    while(num!=0)
		    {
		    	num=num-1;
		    	c=a+b;
		    	a=b;
		    	b=c;
		    }
		    this.jieguo=c;
		}

	}
	
	

}

   /********** End **********/

这个题只用join就足够了,主线程想要执行最后的输出语句,就必须等待着子线程执行完,这就是join函数的魅力,一般的sleep函数主要用于让整个程序执行放慢,比如我做过那个C++炸弹人小游戏,我就用了sleep函数,就是为了避免电脑执行太快,敌人可以瞬间到达你的身边。

二、通过Lock类来实现多线程的同步

lock是一个接口,:lock()、tryLock()、tryLock(long time, TimeUnit unit)和lockInterruptibly()方法是用来获取锁的,unlock()方法是用来释放锁的。

package step3;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Task {

	public static void main(String[] args) {
		final Insert insert = new Insert();
		Thread t1 = new Thread(new Runnable() {
			public void run() {
				insert.insert(Thread.currentThread());
			}
		});

		Thread t2 = new Thread(new Runnable() {
			public void run() {
				insert.insert(Thread.currentThread());
			}
		});

		Thread t3 = new Thread(new Runnable() {
			public void run() {
				insert.insert(Thread.currentThread());
			}
		});
		// 设置线程优先级
		t1.setPriority(Thread.MAX_PRIORITY);
		t2.setPriority(Thread.NORM_PRIORITY);
		t3.setPriority(Thread.MIN_PRIORITY);

		t1.start();
		t2.start();
		t3.start();

	}
}

class Insert {

	public static int num;
	Lock lock = new ReentrantLock();
	// 在这里定义Lock


	public void insert(Thread thread) {
		/********* Begin *********/
        lock.lock(); 如果使用(lock.tryLock()则会导致仅有一个线程使用完锁)

            System.out.println(thread.getName()+"得到了锁");
         try {
		       for (int i = 0; i < 5; i++) {
			    num++;
			    System.out.println(num);
		        }

        }catch(Exception e)
         {
        	
         }finally {
        	 System.out.println(thread.getName()+"释放了锁");
		     lock.unlock();
         }

		
		
		/********* End *********/

}
	}

如果为了让线程挨个完成,可以用join()函数,因此也可以这样写:

package mytest;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class task {

	
	public static void main(String[] args) {
		final Insert insert = new Insert();
		Thread t1 = new Thread(new Runnable() {
			public void run() {
				insert.insert(Thread.currentThread());
			}
		});

		Thread t2 = new Thread(new Runnable() {
			public void run() {
				insert.insert(Thread.currentThread());
			}
		});

		Thread t3 = new Thread(new Runnable() {
			public void run() {
				insert.insert(Thread.currentThread());
			}
		});
		// 设置线程优先级
		t1.setPriority(Thread.MAX_PRIORITY);
		t2.setPriority(Thread.NORM_PRIORITY);
		t3.setPriority(Thread.MIN_PRIORITY);

		t1.start();
		try {
			t1.join();
		}catch(Exception e)
		{
			
		}
		
		t2.start();
		try {
			t2.join();
		}catch(Exception e)
		{
			
		}
		
		t3.start();
		try {
			t3.join();
		}catch(Exception e)
		{
			
		}

	}
}

class Insert {

	public static int num;
	// 在这里定义Lock


	public void insert(Thread thread) {
		/********* Begin *********/


            System.out.println(thread.getName()+"获得了锁");
         try {
		       for (int i = 0; i < 5; i++) {
			    num++;
			    System.out.println(num);
		        }

        }catch(Exception e)
         {
        	
         }finally {
        	 System.out.println(thread.getName()+"释放了锁");
         }

		
		
		/********* End *********/

}
	}

此处并未讨论synchronized同步锁的使用,因为是java内置的很难看到过程,所以非必要用lock就好。

三、synchronized+volatile实现变量的可见性

package step4;

public class Task {
	public volatile int inc = 0;
//请在此添加实现代码
/********** Begin **********/
	public synchronized void  increase() {  //这里加上synchronized就能保证
一段时间内只有一个线程访问该方法
		inc++;
	}


/********** End **********/

	public static void main(String[] args) {
		final Task test = new Task();
		for (int i = 0; i < 10; i++) {
			new Thread() {
				public void run() {
					for (int j = 0; j < 1000; j++)
						test.increase();
				};
			}.start();
		}
		while (Thread.activeCount() > 1) // 保证前面的线程都执行完
			Thread.yield();
		System.out.println(test.inc);
	}
}

四、

请仔细阅读右侧代码,在 Begin-End 区域内进行代码补充,使线程依照先后顺序依次输出JavaThread+线程名

提示:我们知道线程的执行结果是随机的,什么时候执行线程是看哪一个线程抢占到了CPU的资源,现在请你利用所学知识使多个线程开启之后依照先后顺序执行。

package step1;

public class Task {
	public static void main(String[] args) throws Exception {
		/********* Begin *********/
		//在这里创建线程, 开启线程
		Object a=new Object();
		Object b=new Object();
		Object c=new Object();
		
		MyThread AA= new MyThread("AA",c,a); 
		MyThread BB= new MyThread("BB",a,b);
		MyThread CC= new MyThread("CC",b,c);
		
		AA.start();
		
		Thread.sleep(10);
		
		BB.start();
		
		Thread.sleep(10);
		
		CC.start();
		
		Thread.sleep(10);

		System.exit(0);   //一定要加,需要终止进程,终止线程,不然程序一直不结束
		/********* End *********/
	}
}

class MyThread extends Thread {
	/********* Begin *********/
	
	String threadName;
	Object prev;
	Object self;
	
	public  MyThread(String threadName,Object prev,Object self)
	{
		this.threadName=threadName;
		this.prev=prev;
		this.self=self;
	}
	
	
	
	public void run() {
		int count = 5;
		while(count > 0){
			synchronized(prev)
			{
				synchronized(self)
				{
					System.out.println("Java Thread" + this.threadName);
					count--;
					self.notify();
				}
			try
			{
				prev.wait();
			}catch(Exception e)
			{
				e.printStackTrace();
			}
			}
		}
	}

	/********* End *********/
}

这一段多线程java代码的使用需要带一点操作系统的理解,阻塞线程与释放线程。

为了使用Object类的wait()方法与notify方法,创建了三个对象类并且创建了三个线程,并将三个Object类引入其中。

 重头戏在run方法的定义这里,synchronized(prev)与synchronized(self),这里就是就是将三个对象类的对象作为锁的对象,首先是AA线程,它对应的是synchronized(c)与synchronized(a),当最里面的synchronized(self)代码块结束后执行prev.wait(),于是对象c被锁住了,然后再是BB线程对应的是synchronized(a)与synchronized(b),然后同上对象a被锁住了,但是c通过notify被释放了,因此,此时由于仅有a被锁住,所以线程AA与线程BB均不能调用run方法,仅有线程CC可以运行,因为,它需要的对象是b和c,最后运行完再锁住b,然后仅有线程AA可以运行,就这样在相互牵制的作用下,三个线程实现了往复运行。

如果学了操作系统的信号量一章,则对这段代码理解起来就比较容易,因为信号量的互斥原理就是这段代码的原理。

五、第2关:售票窗口

创建一个站台类Station,继承Thread,重写run方法,在run方法里面执行售票操作(即如果票没卖完就一直卖)!售票要使用同步锁:即有一个站台卖这张票时,其他站台要等这张票卖完!

相关知识可以参考实训:Java高级特性 - 多线程基础(3)线程同步

测试说明

本关执行代码已经提前写好如下,不需要你重新编写运行代码:

 
  1. public static void main(String[] args) {
  2. //实例化站台对象,并为每一个站台取名字
  3. Station station1=new Station();
  4. Station station2=new Station();
  5. Station station3=new Station();
  6. // 让每一个站台对象各自开始工作
  7. station1.start();
  8. station2.start();
  9. station3.start();
  10. }

测试输入:; 预期输出:

卖出了第20张票 卖出了第19张票 卖出了第18张票 卖出了第17张票 卖出了第16张票 卖出了第15张票 卖出了第14张票 卖出了第13张票 卖出了第12张票 卖出了第11张票 卖出了第10张票 卖出了第9张票 卖出了第8张票 卖出了第7张票 卖出了第6张票 卖出了第5张票 卖出了第4张票 卖出了第3张票 卖出了第2张票 卖出了第1张票 票卖完了

package step2;

/********* Begin *********/
//定义站台类,实现卖票的功能。
public class Station extends Thread  {
    private static int ticket = 20;
    static Object a= new Object();
	public void run() {  
    while(ticket>=0)
    {
        synchronized(a)
        {
          if(ticket>0)
          {System.out.println("卖出了第"+ticket+"张票");          
            ticket--;}
           else if(ticket==0)
           {
             System.out.println("票卖完了"); 
              System.exit(0);
           }
        }
    }
      
	}
  
	
}
/********* End *********/
public class task
{
	public static void main(String[] args) {
	    //实例化站台对象,并为每一个站台取名字
		Mythread task1=new Mythread();
		Mythread task2=new Mythread();
		Mythread task3=new Mythread();
	    // 让每一个站台对象各自开始工作
	     task1.start();
	     task2.start();
	     task3.start();

	}
	/********* End *********/

}

这个题分析可得到,ticket为20张,共有三个线程对它进行修改,因此它必须被看成共享变量,所以ticket必须用static修饰,不然的话,每个线程都读一个20就是60了,这就是static的魅力,为了及时可见ticket的变化,因此必须让三个线程对其进行同步访问修改,这里我没有采用lock,因为没有必要那么复杂,synchronized足够应付,只需要创建一个对象类对其加锁即可,而且必须是static类型,不然的话就是三个对象a,每个线程都有一个独自的对象类a,根本锁不住,必须用static才能让三个线程共用一个对象类a,才能达到互斥访问的目的,唯一的a给哪个线程用,它才能执行该方法。

 System.exit(0);这个是必须加的,不然程序一直不停止,线程没被杀死。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值