Java学习打卡:第二十八天
Java养成计划(打卡第28天)
内容管理: 今天的分享主要是三道线程的题目,之后会再巩固一下File类
线程实例分析
Q1:线程与精灵线程的运用
题目内容:
创建一个包含实例变量i的线程。线程启动之后,首先创建并启动一个精灵线程,然后对变量i循环累加1,直到i的值等于Integer.MAX_VALUE,精灵线程每间隔1s输出一次i的值
问题分析
这个题目非常简单,直接就告诉了run中的内容,那就是要创建一个精灵线程;按照之前的经验我们这里再构造方法中创建就好了,但其实不能这样子做,因为使用构造方法只能创建一个线程,所以看起来比较方便
这里还是直接在run方法中创建第二个线程,只是麻烦一点,使用引用就好了,但是这里只让创建一个精精灵线程,所以使用单一实例模式,创建一个自包含对象,当不空时创建线程
package ThreadDemo;
public class ThreadDemo implements Runnable{
private static ThreadDemo dem = new ThreadDemo();
private int i = 0;
Thread t2 = null;
@Override
public void run() {
if(t2 == null)//使用单一实例模式来创建线程对象
{
t2 = new Thread(dem);//这段代码只能执行一次
t2.setDaemon(true);
t2.start();
}
for(;i <= Integer.MAX_VALUE;i++)
{
if(Thread.currentThread().isDaemon())
{
System.out.println(Thread.currentThread().getName()+"i = " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Thread t1 = new Thread(dem);
t1.start();
}
}
这里使用了引用,如果不使用引用,必须要有一个对象,上一个消费者中的对象就是Queue的实例q,而构造方法中的this就是指代的对象q,所以执行时就时该对象上锁,这里之前我没有创建一个对象就直接使用,就导致了混乱
输出结果
Thread-1i = 2541
Thread-1i = 521692808
Thread-1i = 1039199618
Thread-1i = 1553714050
Thread-1i = 2086683371
这里输出线程名字是为了检验是否只创建了一个精灵线程
Q2:多线程与interrput的应用
题目内容:
创建t1,t2两个线程,线程t1创建后进入沉睡,睡眠时间为10s,线程2创建后重复产生随机数,直到线程t1终止,每次产生一个随机数后睡眠1s,若产生的随机数小于0.2就向t1放松中断请求,线程t1退出睡眠后,先输出实际睡眠时间,再终止
这里我们也许会倾向于只创建一个类,但是一个类中,多线程或导致混乱,所以这里采用继承的方式创建两个线程,这样才能实现两个线程执行内容不一样,上一个题目是在线程中创建线程所以是一个类
package ThreadDemo;
public class Thread1 extends Thread{
@Override
public void run() {
System.out.println("enter t1");
//睡眠10s,记录最开始睡眠的时间
long primer_time = System.currentTimeMillis();
try {
Thread.sleep(10*1000);
} catch (InterruptedException e) {
System.out.println("t1被中断");
}
//最终睡眠时间
long end_time = System.currentTimeMillis();
System.out.println("睡眠的实际时间为 :" + (end_time - primer_time)/1000+"s");
System.out.println("t1终止");
}
}
Thread1类就是只用睡眠和记录时间,例外就是被中断
package ThreadDemo;
public class Thread2 extends Thread{//要随时向t1发送消息,所以就要包含t1
private Thread1 t1;
public Thread2(Thread1 t1) {//传入t1
this.t1 = t1;
}
@Override
public void run() {
System.out.println("enter t2");
while(t1.isAlive())
{
double num = Math.random();
System.out.println("t2产生的随机数为:"+ num);
if(num < 0.2)
{
t1.interrupt();
}
try {//每次产生一个数沉睡1s
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t2终止");
}
}
这里由于t2要对t1进行操作,所以t1要作为t2的属性,构造方法中传入t1
最后用起始类启动两个线程
package ThreadDemo;
public class ThreadDemo{
public static void main(String[] args) {
Thread1 t1 = new Thread1();
Thread2 t2 = new Thread2(t1);
t1.start();
t2.start();
}
}
一种可能的结果是
enter t1
enter t2
t2产生的随机数为:0.7462803496493212
t2产生的随机数为:0.5418461826774316
t2产生的随机数为:0.6781481265082193
t2产生的随机数为:0.7426906269821782
t2产生的随机数为:0.03640488973479594
t1被中断
睡眠的实际时间为 :4s
t1终止
t2终止
Q3 模拟买票
题目内容
模拟3个人买票,张某 李某 赵某买票,售票员只有3张5元的钱,电影票5元1张,张某拿着一张20在李某前面,李某拿着一张10在赵某前面,赵某拿着一张5元的钱
题目分析
这个题目让模拟这个过程,这里线程应该是有3个,对应三个人,和生产者,消费者有点相似
这里售票员单独为类,顾客给钱后执行操作,然后使用一个模拟器进行关联售票员类,不同的顾客给的钱不一样
package ThreadDemo;
public class Seller {//持有的钞票的张数
private int num5 = 3; //3张5元
private int num10 = 0;
private int num20 = 0;
//售票的方式,给不同的前后执行不同的操作
public synchronized void sell(int money)
{//顾客递交的钱只能时3种情况
System.out.println(Thread.currentThread().getName());
if(money == 5)//给5元正好(最简单)
{
num5 = num5 +1;
System.out.println("钱正好,请拿好你的票");
}
else if(money == 20){
while(!((num10 >= 1&&num5 >= 1)||num5 >= 3))
{
System.out.println("20,没有零钱,稍等");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(num10 >= 1)
{
num10 = num10 -1;
num5 = num5 -1;
num20 = num20 + 1;
System.out.println("你给我20,找你1张10和1张5");
}
else
{
num5 = num5-3;
num20 = num20 +1;
System.out.println("你给我20,找你3张5");
}
}
else if(money == 10) {
while(num5 < 1)
{
System.out.println("你给我10,找你的钱不够,请稍等");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num5 = num5 - 1;
num10 = num10 + 1;
System.out.println("你给我10元,我找你5元,请拿好");
}
notifyAll();//这种唤醒的可以不用加具体的前对象
System.out.println(Thread.currentThread().getName() +"结束");
}
}
//这里我之前将while的作用域放大,通过加上调试语句找出错误
之后就是一个模拟器,如果将代码直接放入非常麻烦
package ThreadDemo;
public class Simulater implements Runnable{
private Seller bc;
private int money;
public Simulater(Seller bc, int money) {
this.bc = bc;
this.money = money;
new Thread(this).start();
}
@Override
public void run() {
bc.sell(money);
}
}
最后使用一个起始类来模拟排队就好了
package ThreadDemo;
public class ThreadDemo{
public static void main(String[] args) {
Seller bc = new Seller();
new Simulater(bc,20);
new Simulater(bc,10);
new Simulater(bc,5);
}
}
运行之后一种可能的结果为
Thread-0
你给我20,找你3张5
Thread-0结束
Thread-2
钱正好,请拿好你的票
Thread-2结束
Thread-1
你给我10元,我找你5元,请拿好
Thread-1结束
这里也是一种综合运用,关于锁的应用
通过这三道题目就运用了线程基本上大部分的知识,第三个的运用整体上挺简单,但是需要注意的就是创建类的时候的思路,这里我们从题目中可以发现的是售票窗口和三个人,并且有找钱的过程,所以就应该让售票的成为一个类Seller,它的属性就是它所拥有的纸币,方法就售票,而模拟其就是要将人和售票窗口相互连接,使用的线程,每个人都是一个线程,将售票方法放入run中。
文件File
这里单独提到这个类主要是因为之前将IO时漏掉了这个部分,所以现在详细分析一下这个部分。
谈到程序开发,一定时离不开对文件的操作的,比如文件IO,FileOutputStream之类的,在过滤流中最内层一定是介质流,对于文件流就需要创建一个文件的对象。
java如何访问文件属性?
通过Java的API:java.io.File类
如何获取文件对象?
这里是利用File类的构造方法,通过传入文件的路径获取到文件对象
File file = new File("D:\\java\\wb\\Hello.txt"); //这里的路径是以字符串的形式存在
这里需要注意路径中目录是用的双斜杠,而不是单斜杠,而在盘中的路径是单斜杠,这是由于转义字符导致,要是单斜杠,就必须要用转义字符
这里如果文件存在就只是访问,如果不存在就会创建一个文件,File提供了一个方法createNewFile()来判断是否创建了新文件。还有删除文件的方法delete,指定文件是否存在的方法exists,还有打印文件的绝对路径的方法getAbsolutePath(),lenth文件大小(单位是字节),/1024就是kb ,还有很多方法,可以直接在java API中查询
由于文件的内容比较简单,就不继续分享了,之后会再详细分析一下Java中的容器类,比较如何方便操作各种数据结构~