描述 | |
---|---|
知识点 | 字符串,循环,链表,队列,栈,查找,搜索,排序,树,图,数组,函数,指针,枚举,位运算,结构体,联合体,文件操作,递归 |
运行时间限制 | 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(){……}
其效果与上面的例子相同。
最后,以上的内容都是根据我的理解总结而来,如果有什么不对的地方欢迎大家指出,万分感谢!有什么需要讨论的可以在评论中留言,博主会尽快回复:)