四个线程t1,t2,t3,t4,向4个文件中写入数据,t1只能写入1,t2只能写入2,t3只能写入3,t4只能写入4
对4个文件A,B,C,D写入如下内容:
A:123412341234.....
B:234123412341....
C:341234123412....
D:412341234123....
怎么实现同步可以让线程并行工作?
从网络上看到有些人贴出的代码的答案,用一个锁来将4个文件全部锁了,然后让一个线程拥有锁,这个线程写了之后去唤醒下一个线程继续写,也就是说,这4个文件,同时只能有一个线程在写,这样跟单线程的有什么区别,这就是为了多线程而多线程。
我就尝试着来实现了一下。
感觉这个问题非常的有意思,每个线程只能写入特定的数字,这个有点麻烦,不过好歹是写出来了
如果有更好的实现方式,请大佬赐教
思路分析:
一个线程只能写一种字符,那么我们就让当前线程去遍历尝试每一个文件,判断当前文件的状态是不是能够由当前线程来进行写操作(用多个变量来来保存多个文件的状态),不能写就换下一个文件。
其中需要注意的问题就在于,我们每个文件中写的字符数量应该是一样的,如果简单的实现上面的思想,而不进行控制的话,结果就可能会导致每个文件被写入的字符数量不一致。其原因就在于多个线程可能会在一个文件中不断的尝试,不断的去改变一个文件的状态,最终导致更多的字符写入了一个文件。而且写入的字符数量也不能很好的进行控制,
那么如何来进行控制呢?
下面的代码就进行了展示
1.第一版代码,刚开始看起来没什么问题,但是后来发现了一个致命的问题,在代码中进行了说明,在第二版进行了修正
public class T {
public static void main(String[] args) throws IOException, InterruptedException {
/**
* 每一个变量控制当前文件应该写的字符是什么
* 这里使用AtomicInteger并不是线程安全考虑,而是为了使用一个可变的Integer类
* 当然也可以自己写一个,这里就图省事了
*/
AtomicInteger count1 = new AtomicInteger(1);
AtomicInteger count2 = new AtomicInteger(2);
AtomicInteger count3 = new AtomicInteger(3);
AtomicInteger count4 = new AtomicInteger(4);
FileWriter file1 = new FileWriter(new File("C:\\Users\\12130\\Desktop\\新建文件夹\\1.txt"));
FileWriter file2 = new FileWriter(new File("C:\\Users\\12130\\Desktop\\新建文件夹\\2.txt"));
FileWriter file3 = new FileWriter(new File("C:\\Users\\12130\\Desktop\\新建文件夹\\3.txt"));
FileWriter file4 = new FileWriter(new File("C:\\Users\\12130\\Desktop\\新建文件夹\\4.txt"));
Handler handler1 = new Handler(
file1, file2, file3, file4, count1, count2, count3, count4, 1000, 1);
Thread t1 = new Thread(handler1);
Handler handler2 = new Handler(
file1, file2, file3, file4, count1, count2, count3, count4, 1000, 2);
Thread t2 = new Thread(handler2);
Handler handler3 = new Handler(
file1, file2, file3, file4, count1, count2, count3, count4, 1000, 3);
Thread t3 = new Thread(handler3);
Handler handler4 = new Handler(
file1, file2, file3, file4, count1, count2, count3, count4, 1000, 4);
Thread t4 = new Thread(handler4);
t1.start();
t2.start();
t3.start();
t4.start();
t1.join();
t2.join();
t3.join();
t4.join();
file1.close();
file2.close();
file3.close();
file4.close();
}
}
class Handler implements Runnable {
private final FileWriter file1;
private final FileWriter file2;
private final FileWriter file3;
private final FileWriter file4;
private AtomicInteger count1;
private AtomicInteger count2;
private AtomicInteger count3;
private AtomicInteger count4;
private int count;
private int rtc;
public Handler(FileWriter file1, FileWriter file2, FileWriter file3, FileWriter file4, AtomicInteger count1, AtomicInteger count2, AtomicInteger count3, AtomicInteger count4, int count, int rtc) throws IOException {
this.file1 = file1;
this.file2 = file2;
this.file3 = file3;
this.file4 = file4;
this.count1 = count1;
this.count2 = count2;
this.count3 = count3;
this.count4 = count4;
this.count = count;
this.rtc = rtc;
}
@Override
public void run() {
try {
/**
* 依次去尝试让每一个文件中写,如果当前文件的状态不能由当前文件来写
* 那么当前线程将会阻塞,等到其他线程将当前文件的状态写成当前线程能写的状态
* 当前线程就被唤醒,继续往下写。
* 这样就是为了控制字符的写入,保证了每一次循环都会先每一个文件写一个字符
* 最终每个文件的字符数量都是相同的,且写入的字符的数量也是受我们控制的。
*
* 存在的bug就是如果1文件当前最后一个字符写的是1,当前1线程来尝试写当前文件,1线程进入等待状态
* 同时2文件当前最后一个字符是2,那么2线程来参数写,2线程也会进入等待状态
* 3线程和4线程同理,最终导致每一个线程都处于了等待状态。
*/
while (count > 0) {
write(file1, count1, rtc);
count--;
write(file2, count2, rtc);
count--;
write(file3, count3, rtc);
count--;
write(file4, count4, rtc);
count--;
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
private void write(final FileWriter file, AtomicInteger count, int c) throws IOException, InterruptedException {
synchronized (file) {
if (count.get() != c) {
/**
*
*/
file.wait();
}
file.write(c + "");
int i = count.incrementAndGet();
if (i == 5) {
count.set(1);
}
file.notify();
}
}
}
2.第二版,应该没有bug了吧,且每个线程之间是并行的,感觉效率还不错
public class T {
public static void main(String[] args) throws IOException, InterruptedException {
/**
* 每一个变量控制当前文件应该写的字符是什么
*/
AtomicInteger count1 = new AtomicInteger(1);
AtomicInteger count2 = new AtomicInteger(2);
AtomicInteger count3 = new AtomicInteger(3);
AtomicInteger count4 = new AtomicInteger(4);
FileWriter file1 = new FileWriter(new File("C:\\Users\\12130\\Desktop\\新建文件夹\\1.txt"));
FileWriter file2 = new FileWriter(new File("C:\\Users\\12130\\Desktop\\新建文件夹\\2.txt"));
FileWriter file3 = new FileWriter(new File("C:\\Users\\12130\\Desktop\\新建文件夹\\3.txt"));
FileWriter file4 = new FileWriter(new File("C:\\Users\\12130\\Desktop\\新建文件夹\\4.txt"));
Handler handler1 = new Handler(
file1, file2, file3, file4, count1, count2, count3, count4, 1000000, 1);
Thread t1 = new Thread(handler1);
Handler handler2 = new Handler(
file1, file2, file3, file4, count1, count2, count3, count4, 1000000, 2);
Thread t2 = new Thread(handler2);
Handler handler3 = new Handler(
file1, file2, file3, file4, count1, count2, count3, count4, 1000000, 3);
Thread t3 = new Thread(handler3);
Handler handler4 = new Handler(
file1, file2, file3, file4, count1, count2, count3, count4, 1000000, 4);
Thread t4 = new Thread(handler4);
t1.start();
t2.start();
t3.start();
t4.start();
t1.join();
t2.join();
t3.join();
t4.join();
file1.close();
file2.close();
file3.close();
file4.close();
}
}
class Handler implements Runnable {
private final FileWriter file1;
private final FileWriter file2;
private final FileWriter file3;
private final FileWriter file4;
/**
* 指示某一个文件当前应该写入的字符
*/
private AtomicInteger count1;
private AtomicInteger count2;
private AtomicInteger count3;
private AtomicInteger count4;
/**
* count表示当前线程需要给每个文件写多少个rtc
*/
private int count;
/**
* 当前线程应该写入的字符
*/
private int rtc;
public Handler(FileWriter file1, FileWriter file2, FileWriter file3, FileWriter file4, AtomicInteger count1, AtomicInteger count2, AtomicInteger count3, AtomicInteger count4, int count, int rtc) throws IOException {
this.file1 = file1;
this.file2 = file2;
this.file3 = file3;
this.file4 = file4;
this.count1 = count1;
this.count2 = count2;
this.count3 = count3;
this.count4 = count4;
this.count = count;
this.rtc = rtc;
}
@Override
public void run() {
try {
/**
* 每一次循环都尝试向每一个文件中写入一次当前线程的rtc
*/
while (count > 0) {
int i = 0;
/**
* 必须要保证每一个文件在当前都写入了1。
* 不会进行阻塞,一直循环判断哪个文件当前线程能写
*/
while (i != 15) {
if ((i | 14) != 15 && write(file1, count1)) {
i |= 1;
}
if ((i | 13) != 15 && write(file2, count2)) {
i |= 2;
}
if ((i | 11) != 15 && write(file3, count3)) {
i |= 4;
}
if ((i | 7) != 15 && write(file4, count4)) {
i |= 8;
}
}
count--;
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 尝试向文件中写入字符
*
* @param file 文件
* @param count 当前文件当前状态应该写入的字符
* @return 写入是否成功
* @throws IOException
*/
private boolean write(final FileWriter file, AtomicInteger count) throws IOException {
if (count.get() != rtc) {
/**
* 如果当前文件不能写当前字符,直接退出,而不是阻塞在这个。
* 前文件不能写入当前rtc,不代表其他文件不能写入
* 不能阻塞在这里,否则可能会导致每一个线程都处于等待状态
*/
return false;
}
file.write(rtc + "");
int i = count.incrementAndGet();
if (i == 5) {
count.set(1);
}
return true;
}
}