java高级---线程、网络、文件、流、序列化等代码示例

来源:互联网 发布:字幕视频合成软件 编辑:程序博客网 时间:2024/05/18 20:09
public class A{
    public static void main(String[] args) throws IOEception{
    RandomAccessFile raf=new RandomAccessFile(file,"rw");
    System.out.println(raf.getFilePointer());
    raf.write('A');
    System.out.println(raf.getFilePointer());
    raf.write(max>>>24);
    raf.write(max>>>16);
    raf.write(max>>>8);
    raf.write(max);
    raf.seek(0);
    char a=(char)raf.read();
    System.out.println(a);
    raf.write('C');//游标指向AC
    raf.close();
    /*文件读操作,RandomAccessFile的读写操作是基于指针的,只会读写当前指针指向的字节。
    移动游标调用seek(int p)  参数P:需要游标移动得位置的
    
    */File file=new File("out.dat");
    RandomAccessFile raf=new RandomAccessFile(file,"rw");
    //read="00000000 00000000 00000000 01010101"
    //a="00000000 01010101"
    char a=(char)raf.read();
    char b=(char)raf.read();
    System.out.println(a);
    System.out.println(b);
    //读int值
    int data=raf.read();//读取一个字节
    int num=0;//读取的int值

    num=num|data<<24;
    data=raf.read();
    num=num|data<<16;
    data=raf.read();
    num=num|data<<8;
    data=raf.read();
    num=num|data;
    data=raf.read();

    raf.close();
    }
}

/*RandomAccessFile写操作:
void write(int d):写一个字节,int的低八位
    void writeInt(int d):写四个字节
    void writeLong(long l):写八个字节
    void writeDouble(double d)
读操作:int read():读一个字节,返回的int只有低八位有效
    int readInt():读一个int值,连续读四个字节,转化为int
    int readLong():
    int readDouble


*/
File file=new File("o.dat");
if(!file.exists()){
    file.createNewFile();
}
RandomAccessFile raf=new RandomAccessFile(file,"rw");
int max=Integer.MAX_VALUE;
raf.writeInt(max);
System.out.println(raf.getFilePointer());
raf.seek(0);
int num=raf.readInt();
System.out.println(num);
raf.close();

/*读写字符串,以UTF编码读写字符串
void writeUTF(String str):将字符串以utf转化为字节写出
String readUTF():将字节读取后转为UTF
*/
File file=new File("out.txt");
if(!file.exists()){
file.createNewFile();
}
RandomAccessFile raf=new RandomAccessFile(file,"rw");
String s="发动机";
raf.writeUTF(s);
//将游标移到文件开始  $:ranDomAccessFile的占位符

raf.seek(0);
String info=raf.readUTF();
System.oout.println(info);
raf.close();
/*字节数组
*/
File file=new File("buf.dat");
if(file.exists()){
file.createNewFile();
}
RandomAccessFile raf=new RandomAccessFile(file,"rw");
byte[] data={0,1,2,3,4,5,6,7,8,9};
raf.write(data);//全部输出
//只写字节数组的1-5,从数组下标1开始。连续写5个
raf.write(data,1,5);
raf.close();

File file=new File("buf.dat");
if(file.exists()){
file.createNewFile();
}
RandomAccessFile raf=new RandomAccessFile(file,"rw");
//读取文件所有字节输出int read()读一个字节,并以int返回,只有低八位有效
//int 装byte的-1, 返回255   int的-1是32个1.-1表示到文件末尾了
int d=-1;
//读一个字节
while((d=raf.read())!=-1){
    System.out.println(d+" ");
}
raf.seek(0);
byte[] buf=new byte[30];
//批量读取数据,创建一个可以保存30个字节的数组。尝试读30个。返回实际读取的字节数
int sum=raf.read(buf);
System.out.println(sum);
System.out.println(Arrays.toString(buf));
raf.close();


//使用RandomAccessFile进行拷贝
File src=new File("pack.rar");//待复制的文件
File des=new File("pack_copy.rar");//拷贝后的文件
if(!des.exists()){
des.createNewFile();
}
//用于读取待拷贝文件的
RandomAccessFile srcRaf=new RandomAccessFile(src,"rw");
//创建用于拷贝后的文件的
RandomAccessFile desRaf=new RandomAccessFile(des,"rw");
//从源文件读字节写到目标文件
/*int d=-1;
while((d=srcRaf.read())!=-1){
    desRaf.write(d);
}
*/
//效率高
byte[] buf=new byte[1024*10];
int sum=0;
while((sum=srcRaf.read(buf))>0){
    desRaf.write(buf,0,sum);
}
srcRaf.close();
desRaf.close();

//删除目录
public static delete(File file){
    if(file==null||!file.exists()){
        throw new RuntimeExcetion("文件或目录不存在");
    }
    if(file.isDirectory()){
        for(File sub:file.listFiles()){
            delete(sub);
        }    
    }
    file.delete();
}

public static void main(String [] arg){
    File die=new File("a");
    delete(dir);
    System.out.println("删除成功");
}


/*低级流:数据有明确的来源和去向,高级流:(过滤流,处理流):不能独立存在,必须依托于另一个流工作
文件字节输入输出流:FileInputStream文件输入流  FileOutputStream:文件输出流,节点流,字节流
FileOutputStream 写方法:
write(int d)  write(bytr[] d)  write(byte[]d,int offset,int len)

*/

File file=new File("output.dat");
if(!file.exists()){
    file.createNewFile();
}
FileOutputStream fos=new FileOutputStream(file);
//写一个字符a  write(int d):一次写一个字节,写得是int的低八位
int a=1234455;
//FileOutputStream不支持writeInt方法
fos.write(a>>>24);//写int值的最高8位
fos.write(a>>>16);
fos.write(a>>>8);
fos.write(a);


FileOutputStream fos=new FileOutputStream(file,true);
String s="我的方法将诶接";
byte[] data=s.getBytes("utf-8");
fos.write(data);
fos.close();

//读,文件输入流 int read()  int read(byte[] d)  int read(byte[] d,int offset,int len)
File file=new File("output.txt");

FileInputStream fis=new FileInputStream(file);
int d=-1;
//while((d=fis.read()!=-1){
    //System.out.println(Integer.toHexString(d)+" ");
//}String(byte[] data,int offset,int len,String charset)
//byte[] b=new byte[100];
//int sum=fis.read(b);
/*String s=new String(b,0,sum,"utf-8");
System.out.println(s);
*///根据文件对象,获取文件大小并创建字节数组
byte[] b=new byte[(int)file.length()];
fis.read(b);
String s=new String(b,"utf-8");
System.out.println(s);
fis.close();

//文件输出的重载     int available():返回当前输入流允许读取的总字节量
FileOutputStream fos=new FileOutputstream("output.txt");
byte[] b=new byte[fis.avaiable()];
fis.read(b);
String s=new String(b,"utf-8");
System.out.println(s);
fis,close();


//复制文件
public class A{
    public static void main(String[] args){
        FileInputStream fis=null;
        FileOutputStream fos=null;
        try{
            fis=new FileInputStream("pack.rar");
            fos=new FileOutputStream("pack_copy2.rar");

            byte[] b=new byte[1024*10];
            int sum=0;
            while((sum=fis.read(b))>0){
                fos.write(b,0,sum);
            }
        System.out.println("复制完毕");
        }catch(FileNotFoundException e){
            System.out.println("文件没有找到");
            e.printStackTrace();
        }catch(IOException e){
            System.out.println("读写失败");
            e.printStackTrace();
        }finally{
            try{
                if(fis!=null){
                    fis.close();
                }
                if(fos!=null){
                    fos.close();
                }
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    }
}


/*高级流:不能独立存在。缓冲字节输入输出流  BufferedInputStream

BufferedOutputStream   bis.close();flush();bos.close();
DataOutputStream dos.writeInt(Integer.MAX_VALUE) dos.writeUTF("")  dos.writeDouble(2.5);

DataInputStream
dos.flush();dos.close();
*/
FileInputStream fis=new FileInputStream("data.txt");
BufferedInputStream bis=new BufferedinputStream(fis);
DataInputStream dis=new DataInputStream(bis);
int num=dis.readInt();
String s=dis.readUTF();
double dnum=dis.readDouble();
System.out.println(s);
System.out.println(num);
System.out.println(dnum);


/*字符流:低级流  writer reader  OutputStreamReader  InputStreamReader  OutputStreamWriter   InputStreamWriter
write(char[] c):一次读写一个字符数组
write(int c):一次写一个字符
write(String s):一次写字符串

重载构造方法:new OutputStreamWriter(fos,"utf-8");
*/FileInputStream fis=new FileInputStream("data.txt");
OutputStreamWriter writer=new OutputStreamWriter(fos);
writer.write('你');//低16位
writer.write("好端端的");
writer.close();
//将字节输入流转为字符输入流
FileInputStream fis=new FileInputStream("char.dat");
InputStreamReader reader=new InputStreamReader(fis,"utf-8");
int c=-1;
while((c=reader.read())!=-1){
    System.out.print((char)c);
}
reader.close();

//字符高级流BufferedReader  BufferedWriter 缓冲字节输入流,按行读取字符串

String path="";
FileInputStream fis=new FileInputStream("char.txt");
InputStreamReader reader=new InputStreamReader(fis);
BufferedReader br=new BufferedReader(reader);
String s=null;
while((s=br.readLine())!=null){
    System.out.println(s);
}
br.close();

File file=new file("char.txt");
FileReader reader=new FileReader(file);
BufferedReader br=new BufferedReader(reader);
String line=null;

while((br.readLine())!=null){
System.out.println(line);
}
br.close();

FileWriter writer=new FileWriter("fw.txt");
writer.write("是的得分");
writer.close();


public class A{
    public static void main(String[] args){

    //PrintWriter writer=new PrintWriter("pw.txt");
    FileOutputStream fos=new FileOutputStream("pw.txt");
    PrintWriter writer=new PrintWriter(fos);
    writer.println("密集的");
    writer.println("密集的");
    writer.println("密集的");
    writer.close();
    }
}
//数据源是键盘的输入流
public class B{
    public static void main(String[] args){
        InputStream in=System.in;//字节输入流,数据源来源于键盘
        InputStreamReader reader=new InputStreamReader(in);
        BufferedReader br=new BufferedReader(reader);
        String s=null;
        //循环读取键盘输入的信息,回车读取出来
        while((s=br.readLine())!=null){
            if("exit".equals(s)){
            break;
            }
            System.out.println(s);
        }
    }
}


public class C{
    public static void main(String[] args){
    //System.out输出流输出的目标是控制台
    PrintStream out=System.out;
    //创建一个向文件里输出数据的PrintStream
    File file=new FIle("out.txt");
    PrintStream ps=new PrintStream(file);
    System.setOut(ps);//System.out设置新的输出流
    System.out.println("控制台。。。");
    System.out.println("写多少你也看不见");
    System.out.flush();
    System.out.close();//将文件的输出流关了
    System.setOut(out);//还原向控制台输出的输出流
    System.out.println("这句话可以看见");
    }
}

public class C{
    public static void main(String[] args){
        //获取当前java虚拟机运行环境
        Runtime runtime=new Runtime.getRuntime();
        runtime.exec("mspaint");//启动操作系统命令
        System.out.println("画图,走你");
    
    }
}

//设计模式:装饰模式,在不改变源程序的前提下,额外添加一些逻辑
public interface Person{
    public void say();
}

public class Teacher implements Person{
    public void say(){
        System.out.println("我是一名老师");
    }
}
//添加装饰器
public class Decorator implements Person{
    private Person person;
    //通过构造方法将需要修饰的对象传进来
    public Decorator(Person person){
        this.person=person;
    }
    public void say(){
        System.out.println("大家好");
        person.say();
        System.out.println("请多多关照");
    }
}

//测试装饰模式
public class TestDecorator{
    public static void main(String[] args){
        Person person=new Teacher();
        person.say();
        //在不修改Teacher的前提下,对其添加额外的逻辑
        person=new Decorator(person);
        person.say();
    }
}//spring IOC:工厂模式引申来的  AOP:从装饰模式引申而来

/*DataOutputStream writeInt(int d):将基本数据类型int转成4个字节并写入文件中、
将基本数据类型转化成字节的形式为【序列化】,
从字节转换为其对应的基本类型数据称为【反序列化】
将数据保存到磁盘中称为【持久化】
对象序列化与反序列化:ObjectInputStream   ObjectOutputStream
*/

public class Person implements Serializable{
    private int age;
    private String name;
    public Person(String name,int age){
        this.name=name;
        this.age=age;
    }
    public String toString(){
    return "名字"+name+"年龄"+age;
    }
}

public class TestObjectOutputStream{
    public void mai(String[] args){
        Person p=new Person("zhang",22);
        FileOutputStream fos=new FileOutputStream("onject.dat");
        ObjectOutputStream oos=new ObjectOutputStream(fos);
        //将给的对象序列化后写出,通过文件字节输出流将序列化后的字节写入到文件中,同时做了持久化
        //所有通过ObjectOutputStream进行序列化的对象,必须实现序列化接口,否则会报错Serializable
        oos.writeObject(person);
        oos.close();
    }

}

FileInputStream fis=new FileInputStream("object.dat");
ObjectInputStream ois=new ObjectInputStream(fis);
Person person=(Person)ois.readObject();
System.out.println(person);
ois.cloae();

/*对象序列化,会将该对象的所有属性转化为字节后再输出。
对象的属性有两类:
1、基本类型属性:将基本类型数据的值直接转化为字节
2、引用类型属性:递归,现将对象转化为字节。

使用对象序列化与反序列化进行对象的深度复制*/
public class CloneArrayList{
    public static void main(String[] args){
        ArrayList<Person> list=new ArrayList<Person>();
        list.add(new Person("张三",11));
        list.add(new Person("李四",11));
        //对集合进行序列化再进行反序列化
        FileOutputStream fos=new FileOutputStream("array.dat");
        ObjectOutputStream oos=new ObjectOutputStream(fos);
        //ArrayList已经实现了序列化接口
        oos.writeObject(list);
        oos.close();

        FileInputStream fis=new FileInputStream("array.dat");
        ObjectInputStream ois=new ObjectInputStream(fis);
        ArrayList<Person> array=(ArrayList<Person>)ois.readObject();
        System.out.println(list.equals(array));//true
        System.out.pritnln(list==array);//false
        System.out.println(list.get(0).equals(array.get(0)));//true
        System.out.println(list.get(0)==(array.get(0)));//false
    }
}

ByteArrayInputStream:字节数组输入流
ByteArrayOutputStream:字节 数组输出流
对操作系统而言,并发执行的任务叫进程,对程序而言,并发执行的任务叫线程。
线程:Thread类其每个实例代表一个线程。  其内部可以定义并发操作的任务
每个Thread的子类都是一个线程。创建子类要重写run方法。
start():启动线程,并发执行run方法中的逻辑。run方法是会被自动调用的。

线程:模板模式。Thread封装了线程启动的复杂过程,所有线程启动的方式都是一样的,不同在于线程要并发执行的任务。要求子
类重写run方法。
public class ThreadSayWho extends Thread{
    //重写run方法,将需要并发执行的逻辑定义在该方法中
    public void run(){
        for(int i=0;i<10000;i++){
            System.out.println("谁啊?");
        }
    }    
}

public class ThreadSayMe extends Thread{
    //重写run方法,将需要并发执行的逻辑定义在该方法中
    public void run(){
        for(int i=0;i<10000;i++){
            System.out.println("我是修水管的。");
        }
    }    
}

public class TestThread{
    public static void main(String[] args){
        Thread t1=new ThreadSayMe();
        Thread t2=new ThreadSayWho();

        t1.start();
        t2.start();
    }
}

Runnable 接口:该接口规定了可以运行的任务。使用此接口就是将线程和任务解耦。
public class TestRunnable{
    public static void main(String[] args){
        Runnable run1=new RunnableSayWho();
        Runnable run2=new RunnableSayMe();
        
        Thread t1=new Thread(run1);
        Thread t2=new Thread(run2);
        //匿名类创建Runnable
        Thread t3=new Thread(new Runnable(){
            public void run(){
                for(int i=0;i<10000;i++){
                    System.out.println("我是打酱油的。");
                }
            }
        });
        
        t1.start();
        t2.start();
        t3.start();
    }
}

/*时间片  线程:需要并发运行的任务   CPU:中央处理器
线程的start()方法:将线程纳入到线程调度机制,得到并发操作告诉经纪人,想同时拍。
线程中和生命周期相关的几个方法:
static void sleep(long time):使当前线程进入睡眠阻塞。休眠指定
time毫秒,休眠结束后重新回到 runnable状态,等待再次获取CPU时间片并发运行。
IO阻塞:

*/

//睡眠阻塞
public class SleepBlockDemo{
    public static void main(String[] args){
    //每秒钟输出一次当前系统时间
    SimpleDateFormat sdf=new SimpleDateFormat("HH:mm:ss");
    while(true){
        Date now=new Date();//java.util.Date
        System.out.println(sdf.format(now));
        for(int i=0;i<8;i++){
            System.out.println();
        }
        //停顿一秒钟
        Thread.sleep(1000);//这里要捕获中断异常
    }

    }
}
/*
void interrupt():中断线程,若线程处于睡眠阻塞时将该线程中断,会引发中断异常
main方法实际上也是一个线程在运行的。当我们运行一个java程序时,操作系统会启动一个进程来运行这个程序,而
虚拟机启动起来后,会创建第一个线程去执行main方法。
*/
//中断睡眠阻塞
public class InterruptSleepBlockDemo{
    public static void main(String[] args){
        final Thread lin=new Thread(new Runnable(){
            public void run(){
                System.out.println(":睡觉了");
                try{
                    //在当前进程进入睡眠阻塞后,若被其他线程中断,这里就会引发中断异常
                    Thread.sleep(1000000);
                }catch(InterruptedException e){
                e.printStackTrace();
                System.out.println("破相了");
                }
            }
        });

        Thread huang=new Thread(new Runnable(){
            public void run(){
                for(int i=0;i<5;i++){
                    System.out.println("黄:80!");
                    try{
                        Thread.sleep(1000);
                    }catch(InterruptedException e){
                        e.printStackTrace();                
                    }
                }
                System.out.println("huang:搞定");
                //中断lin线程
                lin.interrupt();
            }
        });
        lin.start();
        hunag.start();
    }
}
//一个方法中的局部内部类中若想引用该方法的其他局部变量,那么该变量应该是final的。
/*final void setPriority(int p):设置线程的优先级。优先级越高的线程理论上被分配时间片的机会越多。
优先级的取值范围:
    常量:MAX_PRIORITY   最高优先级,值为10
    常量:MIN_PRIORITY   最低优先级,值为1
    常量:NORM_PRIORITY   默认优先级,值为5
*/
//线程优先级
public class PriorityDemo{
    public static void main(String[] args){
        Thread max=new Thread(){
            public void run(){
                for(int i=0;i<10000;i++){
                System.out.println("我应该是最快的");
                }
            }
        };
        Thread min=new Thread(){
            public void run(){
                for(int i=0;i<10000;i++){
                System.out.println("我应该是最慢的");
                }
            }
        };
    
        Thread nor=new Thread(){
                public void run(){
                    for(int i=0;i<10000;i++){
                    System.out.println("我应该是不快也不慢的");
                    }
                }
        };
        //使用最高优先级
            max.setPriority(Thread.MAX_PRIORITY);
        //最低优先级
            min.setPriority(Thread.MIN_PRIORITY);
        //默认优先级
            nor.setPriority(Thread.NORM_PRIORITY);
            min.start();
            nor.start();
            max.start();
    }
}

/*
线程优先级不能保证一定按照优先级的方式执行。线程并发运行时是靠线程调度机制分配时间片做到的。分配时间片是不可控的。
使用线程,不应纠结线程执行的先后顺序,因为是不确定的。

System.gc():通知虚拟机尽快做一次回收工作,不是说调用就回收,只能是最快。
*/
//后台线程演示
public class TitanicDemo{
    public static void main(String[] args){
        Thread rpse=new Thread(){
            public void run(){
                for(int i=0;i<10;i++){
                    System.out.println("rose:let me go!");
                    try{
                    Thread.sleep(1000);
                    }catch(InterruptedException e){
                    e.printSackTrace();
                    }
                }
                System.out.println("rose:AAAAAaaaaaaaaaaaa..");
                System.out.println("扑通。。");
            }
        };

        Thread jack=new Thread(){
            public void run(){
                while(true){
                    System.out.println("jack:you jump!i jump!");
                    try{
                    Thread.sleep(1000);
                    }catch(InterruptedException e){
                    e.printSackTrace();
                    }
                }
            }        
        };
        //设置jack为守护线程,该方法必须要在该线程调用start方法前调用。
        jack.setDaemon(true);
        rose.start();
        jack.start();
        //当main方法运行完毕,运行main方法的线程就死亡了。main先死。
    //前台线程不死,后台线程也不死。
    while(true){}
    }
}

/*
同步和异步
同步:执行有先后顺序
异步:执行没有顺序,各执行各的。

线程异步操作的安全问题,当多线程访问同一个资源时,可能产生线程安全问题。这时,需要将异步的访问操作变成同步的。
换句话,对于这段资源的引用多线程要有先后顺序的访问。一个线程访问结束,另一个线程才可以访问。*/
public class SyncDemo{
    public static void main(String[] args){
        Table table=new table();
        Person p1=table.new Person();
        Person p2=table.new Person();
        //两个线程同时访问bable的getBean()方法,而table的
        //getBean方法又对bean这个属性进行了bean操作。两个线程同时访问了table的数据bean,这时可能产生线程安全问题。
        p1.start();
        p2.start();
    }
}
class Table{
    int bean=20;
    //同步方法,多线程不能同时进入到方法内部,保证bean这个数据,只能被一个线程修改
    //public synchronized int getBean(){
        public int getBean(){
            synchronized(this){
                if(bean==0){
                    throw new RuntimeException("没有豆子了");
                }
                return bean--;
            }
        }

    //Table的内部类是一个线程,该线程并发获取桌子上的豆子
    class Person extends Thread{
        public void run(){
            while(true){
                /*线程的getName()方法,获取当前线程的名字。
                通常当一个线程启动后,会分配一个名字
                通过该方法可以获取系统分配给当前线程的名字。
                当前run方法出现异常,没有解决,那么该线程终止并死亡。
                但不会影响当前进程中的其他线程。
                */
                System.out.println(getName()+":bean="+getBean());
            }
        }
    }
}

/*解决线程安全问题
多线程在访问同一段数据时,可能产生线程安全问题。解决:对访问数据的逻辑加锁,使其访问变成同步操作。
关键字synchronized:可以修饰方法,该方法在同一段时间内只可能有一个线程执行这个方法的逻辑。

synchronized(同步监视器){
需要同步的代码块
}
同步监视器:就是一个对象,只要保证多个线程看到的是同一个对象即可。
*/
/*
线程安全类与不安全类
不安全                安全
StringBuilder        StringBuffer
ArrayList            Vector
HashMap                Hashtable

List list=Collections.synchronizedList();//返回一个线程安全的集合实例
*/
/*
Object关于线程的方法:wait()/notify()要求在哪个对象上等,就在哪个对象上锁。
当一个线程调用了某个对象的wait()方法后,该线程进入等待阻塞。就是在调用wait()方法的对象上等待。
只有调用了这个对象的notify()方法后,该线程才能解除等待阻塞,进入runnable状态继续执行。
*/


public class WaitNotifyDemo{
    public static boolean finish=false;
    public static void main(string[] args){
        //下载图片的线程
        final Thread download=new Thread(){
            public void run(){
                System.out.println("正在下载图片。。");
                try{
                    Thread.sleep(1000);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println("下载完毕!");
                finish=true;
                //通知在download上等待的线程,可以继续运行了
                synchronized(this){
                    this.notify();
                }
            }
        };


        //显示图片的线程
        Thread show=new Thread(){
            public void run(){
                try{
                    synchronized(download){
                        download.wait();//在download对象上等待。show线程进入等待阻塞,直到download对象的notify() 方法被执行,才能解除。
                    }
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                //若图片还没下载完毕就报错
                if(!finish){
                throw new RuntimeException("图片显示错误!");
                }
                System.out.println("显示图片");
            }
        };
    }
}

/*Socket技术
网络连接模式:C/S模式 客户端-服务端  client-server
C/S中,客户端程序要独立开发,server也要独立开发
C/S的优势:效果好,易操作性好。
C/S的劣势:维护难度高,需要用户有额外的操作。

B/S结构 浏览器-服务器
B/S优势:客户端统一,通信协议统一(http协议),维护难度低。
B/S的劣势:效果差一些

Socket和ServerSocket
ServerSocket用于打开服务端口,等待客户端连接,运行在服务端。
Socket用于连接指定服务器的指定端口,运行在客户端。
*/
//Server端
public class Server{
    
    private ServerSocket serverSocket;
    //线程池
    private ExecutorService threadPool;
    
    //双缓冲队列,用于保存客户端发送到服务器的信息
    private BlockingQueue<String> msgQueue;

    //创建一个集合,用于保存所有客户端的输出流,因为多线程要访问这个集合,它应该是安全的
    private Vector<PrintWriter> allOut=new Vector<PrintWriter>();

    public Server(){
    
        try{
            //最常见异常是申请的端口号被其他程序占用了。错误信息中会出现JVM_BIND
            serverSocket=new ServerSocket(8088);
            //创建线程池
            threadPool=Executors.newCachedThreadPool();
            //初始化双缓冲队列
            msgQueue=new LinkedBlockingQueue<String>();

            SendInfoClientHandler handler=new SendInfoClientHandler();
            Thread thread=new Thread(handler);
            thread.start();

            System.out.println("服务器启动了");
        }catch(IOException e){
            e.printStackTrace();
        }
    }
    public void  start(){
        try{
            while(true){
                System.out.println("等待客户端连接");
                Socket socket=serverSocket.accept();//用于在8088端口进行等待,等待客户端的连接,当一个客户端通过ip与端口号连接上时,会返回这个客户端的Socket与其开始通信。阻塞方法            
                System.out.println("一个客户端连接了");
                
                allOut.add(new PrintWriter(socket.getOutputStream()));

                Handler handler=new Handler(socket);//启动一个线程,用于和刚连上的这个客户端交流
                //Thread t=new Thread(handler);
                //t.start();

                /*并发执行的任务交给线程池,
                让线程池分配空线程来运行。
                线程池在运行完任务后会自动回收线程
                等待再次分配任务去执行*/
                threadPool.execute(handler);    
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    public static void main(String[] args){
        //因为构造方法中创建了ServerSocket,所有这里相当于启动了服务端
        Server server=new Server();
        //调用start方法后,会监听ServerSocket注册的端口号8088,等待客户端的连接。
        server.start();
    }
    //定义一个线程体,该线程用来并发操作,每一个线程用来与一个客户端交流,获取客户端发送过来的信息
    private class Handler implements Runnable{
        
        private Socket client;//当前线程要处理的客户端Socket
        public Handler(Socket client){
            this.client=client;
        }

        public void run(){
            try{
                InputStream in=client.getInputStream();//通过刚刚连接的客户端的Socket获取输入流,读取客户端发过来的信息
                InputStreamReader ir=new InputStreamReader(in);//将字节输入流转换为缓冲字符输入流,以便以行为单位读取字符串
                BufferedReader reader=new BufferedReader(ir);
                String info=null;
                while((info=reader.readLine())!=null){
                    //System.out.println("客户端说:"+info);
                    //将这个客户端的信息转发给所有客户端
                    
                    /*for(int i=0;i<allOut.size();i++){
                        allOut.get(i).println(info);
                        allOut.get(i).flush();
                    }*/

                    msgQueue.offer(info);//把从客户端发送来的信息放入双缓冲队列,等待被转发
                }
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }

    private class SendInfoClientHandler implements Runnable{
        public void run(){
            try{
                //1循环读取双缓冲队列
                while(true){
                    int size=msgQueue.size();
                    for(int i=0;i<size;i++){
                        //2从队列中获取一条信息
                        String info=msgQueue.poll();
                        //3转发给所有客户端
                        for(PrintWriter writer:allOut){
                            writer.println(info);
                            writer.flush();
                        }
                    }
                    //每次将所有信息都转发后停顿一下、
                    Thread.sleep(500);
                }
            }catch(Exception e){
                e.printStackTrace();
            }
        }
        

    }
}

//客户端
public class Client{
    private Socket socket;
    public Client(){
        try{
            //客户端Socket用于连接服务器构造方法参数(服务器IP地址,服务端程序申请的端口号8088);
            socket=new Socket("127.0.0.0",8088);
            
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    public void start(){
        try{

            GetServerInfoHandler handler=new GetServerInfoHandler(socket);
            Thread thread=new Thread(handler);
            thread.start();
            OutputStream out=socket.getOutputStream();//通过Socket获取输出流,通过该输出流写出的内容就发送到服务器去了
            PrintWriter writer=new PrintWriter(out);.//将字节输出流转换为缓冲流,高级流。可以以行为单位写出字符。
            //创建一个输入流,用于读取键盘写入的内容,然后将这些内容发送到服务端去
            BufferedReader reader=
            new BufferedReader(
                new InputStreamReader(System.in));
            //循环按行读取键盘输入的信息,并通过输出流写到服务端去
            String info=null;
            while((info=reader.readLine())!=null){
                writer.println(info);
                writer.flush();//清空缓冲区,将字符串写到服务端    
            }    
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    public static void main(String[] args){
        Client client=new Client();
        client.start();
    }
//客户端也要启动一个线程,用于循环读取服务器发送过来的信息,单独启动一个线程
    private class GetServerInfoHandler{
        private Socket socket;

        public GetServerInfoHandler(Socket socket){
            this.socket=socket;
        }

        public void run(){
            try{
            //通过socket获取输入流,获取的输入流是服务器发过来的信息
            InputStream in=socket.getInputStream();
            BufferedReader reader=
                new BufferedReader(new InputStreamReader(in));
            String info=null;
            //循环读取服务器发送过来的字符串并输出到控制台
            while((info=reader.readLine())!=null){
                System.out.println(info);
            }
            
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
}

//线程的并发操作
1.Java编程思想 2.Effective Java中文版
3.深入理解java虚拟机-JVM高级特性与最佳实践
4.深入java虚拟机 5.数据结构 6.算法导论
7.Head First设计模式 8.Head First Java
9.鸟哥的linux私房菜
10.快学Scala 11.第一行Android代码。
Kafka, zookeeper,memcache, summingibrd, sbt, maven

/*
线程池:当我们需要并发执行任务时,会频繁的创建线程,并在执行任务后销毁线程。
首先创建一批空线程,当有任务需要执行时,取出一个空线程执行任务,当任务执行完毕后,将线程回收,等待下次执行任务,提高
程序的运行效率。
java中的线程池:ExecutorService实现了线程池的功能。
创建线程池的方法需要使用线程池工具类Executors
Executors.newCachedThreadPool()创建一个可以根据需要创建新的线程的线程池。
Executors.newFixedThreadPool(int size):指定一个指定大小的线程。
Executors.newScheduledThreadPool(int size):创建一个指定大小的线程池,可以指定任务一个延迟时间,在超过这个时间后运行 任务。
Executors.newSinglethreadExecutor():创建一个只有一个线程的Executor

*/
/*
java 中的双缓冲队列
在存取同步的基础上,可以保证存和取可以异步操作。效率高。
BlockingDeque类
双缓冲队列

实现类:
ArrayBlockingQueue:规定大小的双缓冲队列,构造方法要求传入一个int值,指定队列长度。存取数据本着先进先出原则

LinkedBlockingQueue:不定长的双缓冲队列,int最大值。提供一个int的构造方法,可以变成定长的队列。

PriorityBlockingQueue:类似于LinkedBlockingQueue,但存取数据不是本着先进先出原则,而是本着自然顺序原则或在构造方法中传入比较器。按比较规则存取。

SynchronousQueue:必须满足存一次取一次。
*/

四、如果线程不能获得锁会怎么样

如果线程试图进入同步方法,而其锁已经被占用,则线程在该对象上被阻塞。实质上,线程进入该对象的的一种池中,必须在哪里等待,直到其锁被释放,该线程再次变为可运行或运行为止。

当考虑阻塞时,一定要注意哪个对象正被用于锁定:
1、调用同一个对象中非静态同步方法的线程将彼此阻塞。如果是不同对象,则每个线程有自己的对象的锁,线程间彼此互不干预。
2、调用同一个类中的静态同步方法的线程将彼此阻塞,它们都是锁定在相同的Class对象上。
3、静态同步方法和非静态同步方法将永远不会彼此阻塞,因为静态方法锁定在Class对象上,非静态方法锁定在该类的对象上。
4、对于同步代码块,要看清楚什么对象已经用于锁定(synchronized后面括号的内容)。在同一个对象上进行同步的线程将彼此阻塞,在不同对象上锁定的线程将永远不会彼此阻塞。

1、线程同步的目的是为了保护多个线程访问一个资源时对资源的破坏。
2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他同步方法。
3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。
4、对于同步,要时刻清醒在哪个对象上同步,这是关键。
5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。
6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。
0 0
原创粉丝点击