Учебная страница курса биоинформатики,
год поступления 2010
Многопоточность. Афинные преобразования.
Треды
Как вообще выполняется наша программа? Рассмотрим простейшую программу: {{#!Java public class T{
- public static void main(String[] a){
- System.out.println(«Hello»);
} }} её выполнение можно схематично описать так: ->main
->println
->print
->write
->.... <-
<- ->newLine <-
<-
<-
<- Стрелочки соединяющие инструкции которые выполняет java-машина являются нитью, или Thread выполнения (на русский однако это слово обычно переводят как поток, но чтобы не путаться с потоками ввода-вывода я буду использовать слова Thread или нить). В Java можно управлять тредами, для этой цели есть класс Thread. Любая программа имеет хотя бы один тред, посмотрим что можно сделать с ним:
1 public class Test1 {
2 public static void main(String[] args) {
3 Thread t = Thread.currentThread();//получить текущий тред
4 System.out.println(t);//напечатать информацию о нем
5 t.setName("main thread");//поменять имя треда
6 System.out.println("next name: "+t);
7 for(int i=0;i<5;i++){
8 try {
9 Thread.sleep(1000);//поспать минутку
10 } catch (InterruptedException e) {//если другой поток попробует разбудить этот
11 System.out.println("thread is interrupted");
12 }
13 System.out.println(i);
14 }
15 }
16 }
Теперь попробуем сделать программу с несколькими тредами, для этого нам нужно создать новый Thread. А потом запустить его. Для того чтобы определить что будет делать данный тред надо переопределить метод run в Thread, а чтобы начать его выполнение надо вызвать метод start():
1 class MyThread extends Thread{
2 public MyThread(String name) {
3 super(name);
4 }
5
6 public void run() {
7 for(int i=0;i<5;i++){
8 System.out.println(getName()+": "+i);
9 try {
10 Thread.sleep(1);
11 } catch (InterruptedException e) {
12 System.out.println("thread "+this+"is interrupted");
13 }
14 }
15 }
16 }
17
18 public class Test2 {
19 public static void main(String[] args) {
20 MyThread t1 = new MyThread("thread1");
21 MyThread t2 = new MyThread("thread2");
22 MyThread t3 = new MyThread("thread3");
23 t1.start();
24 t2.start();
25 t3.start();
26
27 System.out.println("The end!");
28
29 }
30 }
В чем проблема написанного кода? дело в том что главный тред стартует все 3 дочерних треда потом напечатает The end и кончится. при этом дочернии потоки скорее всего еще не завершаться и the end окажется напечатан в середине программы. Чтобы это исправить надо попросить главный поток подождать пока дочернии не закончат выполнение:
Когда два разных потока обращаются к одному объекту может возникнуть конфликт. Например вы не можете читать и менять текст одновременно, если вы попробуете это сделать, то возможно вы прочитаете гибрид из старого и нового текста. Рассмотрим простой пример:
1 class Text{ //класс храняший текст
2 char[] text;
3
4 public Text(String t){
5 text = t.toCharArray();
6 }
7
8
9 public /*synchronized*/ void replace(String s){
10 for(int i=0;i<Math.min(text.length, s.length());i++){
11 text[i] = s.charAt(i);
12 try {
13 Thread.sleep(9);
14 } catch (InterruptedException e) {
15 System.out.println("thread is interrupted");
16 }
17 }
18 }
19
20 public /*synchronized*/ void print(){
21 for(char c : text){
22 try {
23 System.out.print(c);
24 Thread.sleep(10);
25 } catch (InterruptedException e) {
26 System.out.println("thread is interrupted");
27 }
28 }
29 System.out.println();
30 }
31 }
32
33 class PrinterThread extends Thread{//класс обеспечивающий печать текста в отдельном треде
34 Text t;
35
36 public PrinterThread(Text t) {
37 this.t = t;
38 start();
39 }
40
41 public void run() {
42 t.print();
43 }
44 }
45 public class Test3 {
46 public static void main(String[] args) {
47 Text t = new Text("Маша любит куклу Дашу");
48 PrinterThread p1 = new PrinterThread(t);
49 t.replace("Вышел месяц из тумана");
50 PrinterThread p2 = new PrinterThread(t);
51 try {
52 p1.join();
53 p2.join();
54 } catch (InterruptedException e) {
55 System.out.println("thread is interrupted");
56 }
57
58 }
59 }
Если запустить написанный пример несколько раз можно получить самые разные результаты, большинство из которых будут бессмысленны. Чтобы все стало хорошо надо чтобы методы replace и print нельзя было вызвать одновременно. Для этого есть ключевое слово synchronized, если некий тред вошел в синхронизированный метод некоторого объекта, то никакой другой тред не сможет войти ни в один синхронизированный метод данного объекта, он будет ждать, пока первый не тред не покинет синхронизированный метод. Т.е. чтобы спасти ситуацию надо исправить:
Хочется еще отметить тонкость работы с коллекциями. Если ваша программа имеет несколько тредов, которые обращаются к одной и той же коллекции (добавляют и удаляют ее элементы), то нужно использовать синхронизованные коллекции: Vector (синхронизованный аналог ArrayList) Hashtable (синхронизованный аналог HashMap)
Афинные преобразования
Вернемся к графике. В конце будет понятно, где нам пригодятся и треды В графич. системе Java можно задать преобразование координат плоскости так, чтобы мы работали в исходных координатах, а рисовалось все в новых координатах. Преобразования, которые разрешены при этом, имеют вид:
Это означает, что сохраняется прямолинейность и параллельность линий. Вектор {m00,m10} показывает направление оси x после преобразования, соответственно {m01,m11} – направление оси y. Точка (m02,m12) задает новое начало координат. Конструктор для такого преобразования имеет вид
Самый простой способ использовать функции Graphics2D Полезные функции у Graphics2D:
1 abstract AffineTransform getTransform()// возвращает текущее преобразование (систему координат)
2 abstract void rotate(double theta)//поворот системы координат на угол
3 abstract void rotate(double theta, double x, double y)//поворот системы координат на угол вокруг точки
4 abstract void scale(double sx, double sy)//растяжение
5 abstract void translate(int x, int y) // сдвиг
6
Делаем свое приложение с поворотами:
1 package gui;
2
3 import java.awt.Color;
4 import java.awt.Graphics;
5 import java.awt.Graphics2D;
6 import java.awt.geom.AffineTransform;
7
8 import javax.swing.*;
9
10 public class RevolvingSquares extends JFrame
11 {
12 private JPanel my_panel;
13
14 public int x1 = 200;
15
16 public int y1 = 200;
17
18 double angle1 = 0;
19
20 public int x2 = 100;
21
22 public int y2 = 100;
23
24 double angle2 = 0;
25
26 public RevolvingSquares()
27 {
28
29 super();
30
31 setSize(500, 400);
32
33 my_panel = new JPanel()
34 {
35 public void paintComponent(Graphics g)
36 {
37 super.paintComponent(g);
38 Graphics2D gr = (Graphics2D) g;
39 AffineTransform old_at = gr.getTransform();
40
41 gr.rotate(angle1, x1, y1);
42 gr.setColor(Color.green);
43 gr.fillRect(x1 - 50, y1 - 50, 50, 50);
44 gr.setColor(Color.black);
45 gr.drawString("Крутящийся квадрат1", x1, y1 + 10);
46 gr.setTransform(old_at);
47
48 gr.rotate(angle2, x2, y2);
49 gr.setColor(Color.red);
50 gr.setColor(Color.black);
51 gr.drawString("Крутящийся квадрат2", x2, y2 + 10);
52 gr.fillRect(x2 - 50, y2 - 50, 50, 50);
53
54 gr.setTransform(old_at);
55 }
56 };
57 getContentPane().add(my_panel);
58 }
59
60 private void startThread()
61 {
62 Thread t = new Thread()
63 {
64 public void run()
65 {
66 for (;;)
67 {
68 try
69 {
70 Thread.sleep(50);
71 }
72 catch (Exception ex)
73 {
74 }
75 if (isVisible())
76 {
77 angle1 += 2.0 * Math.PI / 360.0;
78 angle2 += 3.0 * Math.PI / 360.0;
79 my_panel.repaint();
80 }
81 }
82 }
83 };
84 t.start();
85 }
86 public static void main(String[] args)
87 {
88 RevolvingSquares m = new RevolvingSquares();
89
90 m.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
91 m.setVisible(true);
92 m.startThread();
93 }
94 }