一、基本知识
1.定义:
线程:是进程中的一个独立的控制单元,线程控制着进程的执行;线程之间共享进程中的数据。
进程:是单独的内存空间,进程之间不能共享数据。一个进程中至少会有一个线程。
2.延伸:
jvm启动就不止一个线程,还有垃圾回收机制的线程。
二、创建线程的方法
方法一):继承Thread类(java.lang.Thread)
1.步骤:
1)继承Thread类
2)重写run方法
3)调用线程的start方法(作用是启动线程调用run方法,且申请一块内存空间,进行新线程的执行)
2.简单范例1:
/*利用继承的方法实现新线程
*/
//创建线程
public class MyThread extends Thread{
@Override
public void run(){
System.out.println("我是一个新的线程!");
}
}
//测试:
/*这里包括新创建的线程和主线程。*/
public class TestThread {
public static void main(String[] args) {
MyThread mt = new MyThread();
System.out.println("线程开始!");
mt.start(); //一定不用调用run方法
try {
mt.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("线程结束!");
}
}
/*
结果:(顺序不唯一)
线程开始!
我是一个新的线程!
线程结束!
*/
3.范例2:实现售票:
public class ThreadTicket extends Thread{
private int ticketNum = 200;
public ThreadTicket(){
}
public ThreadTicket(String name){
super(name); //由于要传参,调用父类Thread类中传参数的构造方法
}
@Override
public void run() {
while(ticketNum>0){
//利用getName方法获得线程名称,因为用的传参构造器,在创建对象可以输入名称。
System.out.println(getName()+"卖出第"+ticketNum--+"张票");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
//测试:
public class TestTicket {
public static void main(String[] args) {
ThreadTicket t1 = new ThreadTicket("售票口1");
ThreadTicket t2 = new ThreadTicket("售票口2");
ThreadTicket t3 = new ThreadTicket("售票口3");
ThreadTicket t4 = new ThreadTicket("售票口4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
/*部分结果:(可以发现一张票多次被卖出,与缺陷,下面将介绍用实现Runnable的方法)
售票口2卖出第200张票
售票口3卖出第200张票
售票口1卖出第200张票
售票口4卖出第200张票
售票口2卖出第199张票
售票口1卖出第199张票
售票口4卖出第199张票
售票口3卖出第199张票
售票口4卖出第198张票
售票口3卖出第198张票
售票口2卖出第198张票
售票口1卖出第198张票
*/
方法二)实现Runnable接口(java.lang.Runnable)
1.好处:
避免了单继承的局限性,定义线程时,建议使用实现的方式。
2.方法:也要实现run方法。
3.注:
构建对象时,只需建一个自定义的线程对象:
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr,”名字”);
4.范例3:
/*
要求:利用实现Runnable实现售票功能,四个窗口,20张票。
*/
/*买票线程:
步骤:
1.实现run方法
2.利用同步代码块,防止同一张票被多次卖出
3.同步代码块中,需再次判断ticketNum>0,避免多个线程都在等锁,进入后数值减到负值。
*/
public class MyRunnable implements Runnable{
private int ticketNum = 20;
private String mutex="qw";
@Override
public void run() {
while(ticketNum>0){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//同步代码块
//注:当1234线程都在等时,一旦锁被释放,线程就都进入买票,这时容易出现负值。
synchronized (mutex) { //加锁
if(ticketNum>0){ //保证不会产生负数值。
System.out.println(Thread.currentThread().getName()+"卖出第"+ticketNum+"张票");
ticketNum--;
}
}
}
}
}
//测试:
public class TestRunnable {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable(); //只建立了一个MyRunnable,然后里面的线程共享资源
Thread t1 = new Thread(runnable,"售票口1");
Thread t2 = new Thread(runnable,"售票口2");
Thread t3 = new Thread(runnable,"售票口3");
Thread t4 = new Thread(runnable,"售票口4");
t3.setPriority(10);//t3线程的优先级设为最高,但并不表示t3一定每次都先抢到。
t1.start();
t2.start();
t3.start();
t4.start();
}
}
/*
结果:
售票口3卖出第20张票
售票口1卖出第19张票
售票口4卖出第18张票
售票口2卖出第17张票
售票口1卖出第16张票
售票口3卖出第15张票
售票口4卖出第14张票
售票口2卖出第13张票
售票口3卖出第12张票
售票口4卖出第11张票
售票口1卖出第10张票
售票口2卖出第9张票
售票口1卖出第8张票
售票口4卖出第7张票
售票口3卖出第6张票
售票口2卖出第5张票
售票口1卖出第4张票
售票口4卖出第3张票
售票口3卖出第2张票
售票口2卖出第1张票
*/
5.方法介绍:
1)同步代码块
同步方法:同步方法的锁调用该方法的对象。
public synchronized void methodA(){}
表示执行这个方法的线程互斥。
2)优先级:t3.setPriority(10);//并不绝对
1-10(默认为5)
3)join()方法:暂停当前正在执行的线程对象,并执行其他线程,用来“临时释放”(不用建立对象,直接Thread.yield())
范例4:
public class TestRunnble2 {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr,"售票1");
t1.start();
try {
t1.join();
//t1.join(500);//t1线程先运行0.5ms,以后再运行别的程序。
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("主线程售票结束!");
}
}
6范例5:
/*
要求:两人同时从同一个账户取钱,每次取100,一共账户存了1000;
*/
//线程
public class RunnableBank implements Runnable{
private int moneyNumALL=1000;
private String mutex="ad";
@Override
public void run() {
while(moneyNumALL>0){
try {
Thread.sleep(50); //仅控制取钱的速度。
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (mutex) {
if(moneyNumALL>=0){
System.out.println(Thread.currentThread().getName()+"取走钱后剩下-->"+moneyNumALL+"元");
moneyNumALL=moneyNumALL-100;
}
}
}
}
}
//测试:
public class TestBank {
public static void main(String[] args) {
RunnableBank runnable = new RunnableBank();
Thread t1 = new Thread(runnable,"张三"); //规范形式,强记
Thread t2 = new Thread(runnable,"王五");
t1.start();
t2.start();
}
}
/*结果:
王五取走钱后剩下-->1000元
张三取走钱后剩下-->900元
王五取走钱后剩下-->800元
张三取走钱后剩下-->700元
张三取走钱后剩下-->600元
王五取走钱后剩下-->500元
张三取走钱后剩下-->400元
王五取走钱后剩下-->300元
王五取走钱后剩下-->200元
张三取走钱后剩下-->100元
王五取走钱后剩下-->0元
*/
7.死锁
注:
锁不可以是int类型。
范例6:
注:这里在两个线程定义的锁都是相同的,因为他们字符串相同,在内存中缓存区原存入了值,下一个相同的字符创建立时就不再开辟新的区域。
//线程1
public class RunnableSisuo1 implements Runnable{
String mutex1 = "a";
String mutex2 = "b";
@Override
public void run() {
synchronized (mutex1) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("我在等mutex2锁");
synchronized (mutex2) {
System.out.println(Thread.currentThread().getName()+"正在运行");
}
}
}
}
//线程2:
public class RunnableSisuo2 implements Runnable{
String mutex1 = "a";
String mutex2 = "b";
@Override
public void run() {
synchronized (mutex2) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("我在等mutex1锁");
synchronized (mutex1) {
System.out.println(Thread.currentThread().getName()+"正在运行");
}
}
}
}
//测试:
public class TestSisuo {
public static void main(String[] args) {
RunnableSisuo1 runnable1 = new RunnableSisuo1();
RunnableSisuo2 runnable2 = new RunnableSisuo2();
Thread t1 = new Thread(runnable1,"线程1");
Thread t2 = new Thread(runnable2,"线程2");
t1.start();
t2.start();
}
}
/*结果:
我在等mutex1锁
我在等mutex2锁
*/
范例7:
/*死锁的解决:
通过object中的wait方法先释放锁,待到其他线程先使用完锁后,在notify唤醒锁即可。结构: lock.wait();
*/
//线程1:
public class MyRunnable1 implements Runnable{
String lock1 = "q";
String lock2 = "w";
@Override
public void run() {
while(true){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("run1:我在等锁2");
try {
lock1.wait();//释放锁1,锁被run2使用后,唤醒锁,run1才能使用
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} //释放锁1,
synchronized (lock2) {
System.out.println("run1:我在使用锁2");
}
}
}
}
}
//线程2:
public class MyRunnable2 implements Runnable{
String lock1 = "q";
String lock2 = "w";
@Override
public void run() {
while(true){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("run2:我在等锁1");
synchronized (lock1) {
System.out.println("run2:我在使用锁1");
lock1.notify();
System.out.println("run2:我使用完了锁1");
}
}
}
}
}
//测试:
public class TestRunnable {
/*lock1.wait():锁1释放后,run2使用锁1;且注意run2唤醒锁后还需要将程序执行完后才会释放掉锁1,并不是一notify就释放锁。
* wait():在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待
* */
public static void main(String[] args) {
MyRunnable1 runnable1 = new MyRunnable1();
MyRunnable2 runnable2 = new MyRunnable2();
Thread t1 = new Thread(runnable1);
Thread t2 = new Thread(runnable2);
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t2.start();
}
}
/*结果:
run1:我在等锁2
run2:我在等锁1
run2:我在使用锁1
run2:我使用完了锁1
run1:我在使用锁2
run2:我在等锁1
run1:我在等锁2
run2:我在使用锁1
run2:我使用完了锁1
run1:我在使用锁2
run2:我在等锁1
run1:我在等锁2
run2:我在使用锁1
run2:我使用完了锁1
*/
三、利用线程的经典范例:
范例8:
/*生产者与消费者问题:
要求:生产者生产一个产品,消费者消费一个产品。
(注意:
1)生产者和消费者是两个不同的线程。
2)由于两个线程共用产品资源,所以这里将产品另创一个类。
3)以产品作为锁即可。当产品数为0时,生产者生产;当产品数为1时,消费者消费。
4)避免使生产者或者消费者重复抢到锁,再退出(方法:生产者生产完后,休眠一段时间,并释放锁,消费者消费完后,休眠一段时间并唤醒锁)
)
思路:(休眠的时间问题)
1.生产者和消费者如果都先休眠等同时间再抢,且在主线程中也是中间没有休眠暂停时间,,那么可能出现一个线程多次抢到锁。所以在主线程中可以让两者中间隔一段时间。
*/
//产品类:
public class Protuction {
private int proNum;
public int getProNum() {
return proNum;
}
public void setProNum(int proNum) {
this.proNum = proNum;
}
}
//生产者:
public class Produce implements Runnable{
private Protuction pro;
public Produce(Protuction pro){ //通过构造器获得产品类型
this.pro = pro;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(1000); //先休眠1s再抢锁
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized(pro){
System.out.println("生产者抢到锁");
if(pro.getProNum()==0){
System.out.println("生产者生产");
pro.setProNum(1);
try {
pro.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
//消费者:
public class Consumer implements Runnable{
private Protuction pro;
public Consumer(Protuction pro){
this.pro = pro;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);//先休眠1s再抢锁
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (pro) {
System.out.println("消费者抢到锁");
if(pro.getProNum()!=0){
System.out.println("消费者消费");
pro.setProNum(0);
pro.notify();
}
}
}
}
}
//测试:
public class Test01 {
public static void main(String[] args) {
Protuction pro = new Protuction();
Thread produce = new Thread(new Produce(pro));
Thread consumer = new Thread(new Consumer(pro));
produce.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
consumer.start();
}
}
/*结果:
生产者抢到锁
生产者生产
消费者抢到锁
消费者消费
生产者抢到锁
生产者生产
消费者抢到锁
消费者消费
生产者抢到锁
生产者生产
消费者抢到锁
消费者消费
*/
范例9:
/*聊天程序:
要求:利用TCP和多线程的方式,客户端和服务端聊天,发送和接收不限次数。
思路:
1.分别建立客户端、服务端,以及读线程和写线程。
2.客户端和服务端:1)各自分别建立socket和serversocket对象,注意客户端参数是发送到的目的ip及端口,服务器端设好监听的端口。2)都建立读和写两个线程对象,且都start即可。
3.由于客户端和服务器端在读和写的操作是一样的,且需要不同步,所以将读和写操作分别写入两个线程中。
4.在两个线程中获得socket的方式都是通过构造器传入。
*/
代码:
//客户端:
public class Client {
public static void main(String[] args) {
try {
System.out.println("客户端启动:");
Socket socket = new Socket(InetAddress.getByName("192.168.0.74"), 8080);
Thread read = new Thread(new MyRead(socket));
Thread write = new Thread(new MyWrite(socket));
read.start();
write.start();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//服务器端:
public class Server {
public static void main(String[] args) {
try {
System.out.println("服务器端启动:");
ServerSocket serversocket = new ServerSocket(8080);
Socket socket = serversocket.accept();
Thread read = new Thread(new MyRead(socket));
Thread write = new Thread(new MyWrite(socket));
read.start();
write.start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//读线程:
public class MyRead implements Runnable{
private Socket socket;
public MyRead(Socket socket){ //传入socket
this.socket = socket;
}
@Override
public void run() {
BufferedReader br = null;
try {
InputStream is = socket.getInputStream();//获取读输入流
br = new BufferedReader(new InputStreamReader(is));
while(true){
String s = br.readLine();
System.out.println(s);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//写线程:
public class MyWrite implements Runnable{
private Socket socket;
public MyWrite(Socket socket){ //传入socket
this.socket = socket;
}
@Override
public void run() {
BufferedWriter bw = null;
try {
OutputStream os = socket.getOutputStream();//获取读输入流
bw = new BufferedWriter(new OutputStreamWriter(os));
Scanner sanner = new Scanner(System.in);
while(true){
String s = sanner.nextLine();
bw.write(s+"\n");
bw.flush();
System.out.println(s);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
结果演示:
范例10:
/*图形界面java聊天设计:
思路:
1.共有客户端和服务器端和两个端的读方法在不同的线程中
2.首先,导入software,使可以进行图形化设计,方法:
help–>install software–>添加压缩包,将下面所有对勾勾掉,上面打上勾,next安装,重启。
3.MyClient和MyServer都是通过创建JFram文件,通过拖拉元件直接获得界面,当双击进入按钮时,按钮获得点击事件,可以通过修改java代码进行完善。
4.MyClient和MyServer都写入read方法和write方法。
5.由于写即发送时通过按钮点击,所以只要在按钮的点击事件中调用write方法即可。而读方法,是在一直都在等着读,所以讲读方法写在线程中,且在建立连接按钮的监听事件中创建好线程。
6.将读方法调到线程的方法:利用构造器。注:read方法一定写到while循环中。
7.涉及几个用法:1)通过model将发送或读到的数据显示到list中:
DefaultListModel model = new DefaultListModel<>();
list.setModel();
model.addElement(“服务器返回说:”+line);
2)获取text中的值:
String words= textArea.getText();
*/
//客户端:
package com.day0805_swing;
/*注释:有MyClient和MyServer两个端,在其中的连接事件分别建立了线程,线程只使用了在端中定义的读和写的方法。
* 注:将读的方法定义到线程中去。
*
* */
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JEditorPane;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JList;
import javax.swing.JTextArea;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class MyClient extends JFrame {
private JPanel contentPane;
private Socket socket;//1.定义一个socket
DefaultListModel<String> model;
JTextArea textArea;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
MyClient frame = new MyClient();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public MyClient() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
JButton btnNewButton = new JButton("建立连接");
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {//5.点击事件,建立连接
System.out.println("连接服务器:");
try {
socket =new Socket("192.168.0.74", 8080);
System.out.println("连接成功");
//连接成功后建立线程
Thread t = new Thread(new MyClientRead(MyClient.this));
t.start();
} catch (UnknownHostException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}//去连接8080端口
}
});
btnNewButton.setBounds(275, 36, 93, 44);
contentPane.add(btnNewButton);
JButton btnNewButton_1 = new JButton("发送");
btnNewButton_1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {//4、点击事件,发送
write();
}
});
btnNewButton_1.setBounds(294, 208, 74, 44);
contentPane.add(btnNewButton_1);
JList list = new JList();
list.setBounds(10, 10, 255, 188);
model = new DefaultListModel<>(); //3.建立一个model,让数据可以添加进去
list.setModel(model);
contentPane.add(list);
textArea = new JTextArea();//4.使textArea可获取
textArea.setBounds(20, 208, 232, 44);
contentPane.add(textArea);
}
/*2、1)定义一个读方法:客户端读是读取流中数据,即服务器传过来的,且读入后需要将数据显示到list中。(这是与服务器的读不同的地方,)
服务器读完不用写入list,因为客户端发送会在list中显示。
2)写到list中采用modle方式
*
*/
public void read() {
try {
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line = br.readLine();
System.out.println("接收到服务器的返回信息:"+line);//控制台输出一遍
//先做3步骤
model.addElement("服务器返回说:"+line);//将返回值加到model中
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/*3.定义写方法:1)将写到text中的数据写到服务器,以及list中。
* */
public void write(){
try {
OutputStream os=socket.getOutputStream();
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(os));
String words= textArea.getText(); //先进行4步骤,将textArea可获取
System.out.println("客户端发送信息:"+words);
model.addElement(words+"\n");//将发送的数据写到list中
bw.write(words+"\n");
textArea.setText("");
bw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//服务器端:
package com.day0805_swing;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.awt.event.ActionEvent;
import javax.swing.JTextArea;
public class MyServer extends JFrame {
private JPanel contentPane;
private Socket socket; //1.定义一个Socket
private boolean isRunning=true;//3定义一个连接的判断并定义get,set方法
JTextArea textArea;//5.使textArea可以被访问到
public boolean isRunning(){
return isRunning;
}
public void setRunning(boolean isRunning) {
this.isRunning = isRunning;
}
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
MyServer frame = new MyServer();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public MyServer() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
JButton btnNewButton = new JButton("启动服务!");
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {//7.监听事件,点击:服务器启动
try {
ServerSocket server = new ServerSocket(8080);
System.out.println("服务器已启动!");
socket = server.accept(); //等待连接
System.out.println("有客户机连接!");
//连接完成后就可以建立线程进行读写。
Thread t = new Thread(new MyServerRead(MyServer.this));//注:传入的对象?
t.start();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
btnNewButton.setBounds(46, 24, 179, 56);
contentPane.add(btnNewButton);
textArea = new JTextArea();
textArea.setBounds(24, 170, 289, 82);
contentPane.add(textArea);
JButton btnNewButton_1 = new JButton("发送给客户机");
btnNewButton_1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
write(); //6.调用写方法发送值,按钮的监听事件
}
});
btnNewButton_1.setBounds(326, 182, 86, 48);
contentPane.add(btnNewButton_1);
}
//2.定义读方法:
public void read(){
try {
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line= br.readLine();
System.out.println("服务器接收到的信息:"+line);//读到的数据显示在控制台
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//4.定义写方法
public void write(){
try {
OutputStream os = socket.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
System.out.println("服务器器发送信息:");
//从text窗口获得写入的值,修改上面textArea的访问范围,使其可以访问
String word = textArea.getText();
bw.write(word+"\n");//1)将值发给客户机
textArea.setText("");//将窗口清空
bw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//客户端读线程:
public class MyClientRead implements Runnable{
private MyClient client;
public MyClientRead(MyClient client){
this.client = client;
}
@Override
public void run() {
client.read();
}
}
//服务器端读线程:
public class MyServerRead implements Runnable{//需要获取到在类MyServer中的读方法
private MyServer myserver; //通过结构体获得,对象一构建,便有一个MyServer
public MyServerRead(MyServer myserver) {
this.myserver = myserver;
}
@Override
public void run() {
myserver.read();
}
}
演示: