多线程

描述

问题描述:有4个线程和1个公共的字符数组。线程1的功能就是向数组输出A,线程2的功能就是向字符输出B,线程3的功能就是向数组输出C,线程4的功能就是向数组输出D。要求按顺序向数组赋值ABCDABCDABCD,ABCD的个数由线程函数1的参数指定。[注:C语言选手可使用WINDOWS SDK库函数]
接口说明:
void init();  //初始化函数
void Release(); //资源释放函数
unsignedint__stdcall ThreadFun1(PVOID pM)  ; //线程函数1,传入一个int类型的指针[取值范围:1 – 250,测试用例保证],用于初始化输出A次数,资源需要线程释放
unsignedint__stdcall ThreadFun2(PVOID pM)  ;//线程函数2,无参数传入
unsignedint__stdcall ThreadFun3(PVOID pM)  ;//线程函数3,无参数传入
Unsigned int __stdcall ThreadFunc4(PVOID pM);//线程函数4,无参数传入
char  g_write[1032]; //线程1,2,3,4按顺序向该数组赋值。不用考虑数组是否越界,测试用例保证

知识点 字符串,循环,链表,队列,栈,查找,搜索,排序,树,图,数组,函数,指针,枚举,位运算,结构体,联合体,文件操作,递归
运行时间限制 10M
内存限制 128
输入

输入一个int整数

输出

输出多个ABCD

样例输入 10
样例输出 ABCDABCDABCDABCDABCDABCDABCDABCDABCDABCD
由于以前对线程使用的比较少,所以这几天总结了一下,然后接触这道题的!

import java.util.Scanner;

import java.io.IOException;   

public class Main
{        
	public static int N;
	public static void main(String[] args) throws IOException 
	{
		Scanner sca = new Scanner(System.in);
		N = sca.nextInt();
		sca.close();
		
		final Test obj = new Test();            
		Thread tA = new Thread()
		{             
			public void run() 
			{                 
				obj.m1(N);              
			}          
		};
		tA.start();
		Thread tB = new Thread()
		{             
			public void run()
			{                 
				obj.m2(N);              
			}          
		};
		tB.start();
		Thread tC = new Thread() 
		{              
			public void run() 
			{                  
				obj.m3(N); 
			}          
		};
		tC.start();
		Thread tD = new Thread()
		{
			public void run()
			{
				obj.m4(N);
			}
		};
		tD.start();
		try {
			tA.join();
			tB.join();
			tC.join();
			tD.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(obj.ch);
	}    
}    
class Test 
{     
	static int count;      
	
	volatile int target = 1; 
	
	public static String ch = "";
	
	synchronized void m1(int n)
	{         
		for (int i = 0; i < n; i++)
		{              
			while (target != 1)
			{                 
				try
				{
					wait();
				}
				catch (InterruptedException e)
				{
					e.printStackTrace(); 
				}
			}
			ch += "A";
			target = 2;              
			notifyAll();         
		}      
	}        
	synchronized void m2(int n)
	{
		for (int i = 0; i < n; i++)
		{
			while (target != 2)
			{
				try
				{
					wait();
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();             
				}
			}
			ch += "B";
			target = 3;
			notifyAll();
		}
	}
	synchronized void m3(int n)
	{
		for (int i = 0; i < n; i++)
		{
			while (target != 3)
			{
				try
				{
					wait();
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
			ch += "C";
			target = 4;
			notifyAll();
		}     
	} 
	synchronized void m4(int n)
	{
		for (int i = 0; i < n; i++)
		{
			while (target != 4)
			{
				try
				{
					wait();
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
			ch += "D";
			target = 1;
			notifyAll();
		}     
	} 
}
我承认这个不是很符合题意,但是大体上来说还是达到了题中所说的,提交正确以后,我看了一下别人的正确代码,我失望了,都是直接输出,除了main线程意外没有其他线程,也就是骗了机器……

好了,下面就我所理解的java线程总结一下:

一、关于线程的一些概念:

首先,进程与线程是不同的,一般来说,一个程序有一个进程,而有多个线程。在单核CPU中,这些线程在宏观上讲是并行的,而在微观上来说每一时刻都只有一个线程在运行。在多核CPU中同一时刻会有多个线程运行,起数量取决于CPU数量。
其次,来说一下线程的生命周期:
线程的生命周期总的来说可以分为新建状态、就绪状态、运行状态、等待/阻塞状态和消亡状态(也有的书上会说有新建状态、就绪状态、运行状态、等待状态、休眠状态、阻塞状态和消亡状态),叫法会有不同,但实际所指的是一样的。
生命周期状态图:

二、线程的定义方式:

第一种:

public class Main extends Thread
{
	public void run()
	{
		System.out.println("继承Thread定义线程!");
	}
	public static void main(String[] args)
	{
		Main main = new Main();
		main.start();
	}
}
run()函数为Thread的内部函数,重写的run()函数的功能就是希望让线程完成的任务。
第二种:

public class Main implements Runnable
{
	public void run()
	{
		System.out.println("实现接口Runnable定义线程!");
	}
	public static void main(String[] args)
	{
		Main test = new Main();
		Thread t = new Thread(test);
		t.start();
	}
}
run()函数同上,这种方法比较灵活,首先可以在已经有继承类的情况下使用线程,而且可以创建多个线程来完成任务。

注意,其实main也是一个线程,它的启动的动作由JVM来控制,其他线程必须由自己来控制。

三、线程操作方法:

线程的休眠:

try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

sleep()方法的基本语法格式是:

public static void sleep(long millis) throws InterruptedException;
public static void sleep(long millis ,int nanos) throws InterruptedException;
millis表示线程睡眠的毫秒数,nanos表示线程睡眠的纳秒数。同时注意sleep方法是静态方法,其使用位置比较随意,在线程内调用就是当前线程进入睡眠状态。

线程的加入:

try {
			t.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
在一个线程中调用其他线程的join方法意味着先执行其他线程,而当前线程进入就绪状态,在其他线程执行完毕后再执行当前线程。
看一下这个例子:
public class Main
{
	public static void main(String[] args)
	{
		Thread t1 = new Thread(new Runnable(){
			public void run()
			{
				for(int i = 0; i < 20; i++)
				{
					System.out.println(Thread.currentThread().getName()+" "+i);
				}
			}
		},"t1");
				
		Thread t2 = new Thread(new Runnable(){
			public void run()
			{
				for(int i = 0; i < 20; i++)
				{
					System.out.println(Thread.currentThread().getName()+" "+i);
					if(i == 10)
					{
						try {
							t1.join();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
			}
		},"t2");
		
		t1.start();
		t2.start();
	}
}
它的运行结果为:
t2 0
t1 0
t2 1
t1 1
t2 2
t1 2
t2 3
t1 3
t2 4
t1 4
t2 5
t2 6
t1 5
t1 6
t1 7
t1 8
t2 7
t1 9
t1 10
t1 11
t1 12
t2 8
t2 9
t1 13
t2 10
t1 14
t1 15
t1 16
t1 17
t1 18
t1 19
t2 11
t2 12
t2 13
t2 14
t2 15
t2 16
t2 17
t2 18
t2 19

可以看出,在t2线程运行到i==10的时候,调用了t1的join方法,从这时候开始就先执行t1,知道t1执行完毕才执行t2线程。

线程的中断:

以前可以使用stop()方法来中断线程,但是stop()方法是直接中断,线程来不及回收资源等行为,所以现在不建议使用stop()方法。现在中断可以使用标记变量来中断:
public class Main implements Runnable
{
	private static boolean symbol = true;
	public void run()
	{
		while(symbol)
		{
			System.out.println(0);
		}
	}
	public static void main(String[] args)
	{
		Main test = new Main();
		Thread t1 = new Thread(test);
		t1.start();
		symbol = false;
	}
}
当需要中断的时候直接赋值给symbol为false,t1线程就会中断(t1线程运行完毕 )。

线程的礼让:

线程礼让使用yield()方法,这也是一个静态方法,所以可以Thread.yield()直接使用,功能就是让当前线程进入就绪状态,CPU重新选择具有同样优先级的线程进入运行状态。yield()方法只是一种暗示,当前线程是否进入就绪状态还是由CPU决定。

线程的优先级

在线程调用start()方法之前可以设置线程优先级,使用setPriority()方法设置,参数为1-10(main线程的优先级是Thread.NORM_PRIORITY(5)),系统始终会选择就绪状态下优先级较高的进入运行状态,高优先级的运行几率比较大,低优先级的运行几率比较小。

线程同步

首先比较经典的是卖火车票:
public class Main implements Runnable
{
	int count = 10;//火车票数量
	public void run()
	{
		while(true)
		{
			if(count > 0)
			{
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"卖出"+count--);
			}
		}
	}
	public static void main(String[] args)
	{
		Main test = new Main();
		Thread t1 = new Thread(test,"窗口1");
		Thread t2 = new Thread(test,"窗口2");
		Thread t3 = new Thread(test,"窗口3");
		t1.start();
		t2.start();
		t3.start();
	}
}
运行结果是:
窗口2卖出10
窗口1卖出9
窗口3卖出9
窗口1卖出8
窗口2卖出7
窗口3卖出6
窗口1卖出4
窗口2卖出5
窗口3卖出3
窗口2卖出2
窗口1卖出1
窗口3卖出0
窗口2卖出-1
可以看到最后两行的票号为0和负数,这是因为count变成1的时候,三个线程都对其有存储功能,当线程1执行run()方法时,还没有来得及递减count,就指定它调用sleep()方法进入就绪状态,这时其他线程进入run()方法,发现count仍然大于0,而线程1休眠时间又到了,将count递减,同时其他线程也对count完成递减操作,所以就出现了负值。
此时需要对共享资源上锁,当一个线程进入run()方法时禁止其他线程进入run()方法,其他线程阻塞,当线程释放共享资源以后其他线程才能进入就绪状态,由CPU选择一个线程进入run()方法,并上锁,其它线程再进入阻塞状态。
修改以后为:
<pre name="code" class="java">public class Main implements Runnable
{
	volatile int count = 10;//火车票数量
	public void run()
	{
		while(true)
		{
			synchronized("")
			{
				if(count > 0)
				{
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"卖出"+count--);
				}
			}
			
		}
	}
	public static void main(String[] args)
	{
		Main test = new Main();
		Thread t1 = new Thread(test,"窗口1");
		Thread t2 = new Thread(test,"窗口2");
		Thread t3 = new Thread(test,"窗口3");
		t1.start();
		t2.start();
		t3.start();
	}
}
 使用了synchronized关键字,执行结果是: 
窗口1卖出10
窗口1卖出9
窗口3卖出8
窗口3卖出7
窗口2卖出6
窗口2卖出5
窗口2卖出4
窗口2卖出3
窗口3卖出2
窗口1卖出1
synchronized关键字也可以放在方法前面,比如说:
public synchronized void doit(){……}
其效果与上面的例子相同。


最后,以上的内容都是根据我的理解总结而来,如果有什么不对的地方欢迎大家指出,万分感谢!有什么需要讨论的可以在评论中留言,博主会尽快回复:)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值