Java 进阶(多线程和网络编程)

多线程

基本概念

为了让程序更完整,且能很好的运行
1. 程序
“程序”是一个 静态的概念,一般对应于操作系统中的一个可执行文件。程序的运行从而产生了“进程”

2. 进程
执行中的 “程序”叫做 “进程”,是一个动调概念。
特点:

  • 进程是程序的一次动态执行过程, 占用特定的地址空间。
  • 每个进程由3部分组成:cpu、data、code。每个进程都是独立的,保有自己的cpu时间,代码和数据
  • 多任务(Multitasking)操作系统将CPU时间动态地划分给每个进程,操作系统同时执行多个进程,每个进程独立运行。

3. 线程
一个进程有多个线程。

  • 一个进程内部的一个执行单元,它是程序中的一个单一的顺序控制流程
  • 一个进程可以有多个并行的线程
  • 多个线程共享相同的内存单元/内存地址空间,可以访问相同的 变量和对象

线程和进程的区别

  1. 每个进程有独立的代码和数据空间。
  2. 同一进程中的线程 共享代码和数据空间
  3. 进程是资源分配的单元,线程是调度和执行的单位
  4. 多线程: 在同一应用程序中有多个顺序流同时执行。

通过继承Thread类实现多线程

创建了的 进程 要用 start() 方法来启动

public class TestThread extends Thread {//自定义类继承Thread类
    //run()方法里是线程体
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(this.getName() + ":" + i);//getName()方法是返回线程名称
        }
    }
 
    public static void main(String[] args) {
        TestThread thread1 = new TestThread();//创建线程对象
        thread1.start();//启动线程
        TestThread thread2 = new TestThread();
        thread2.start();
    }
}

 

通过Runnable 接口实现多线程

public class TestThread2 implements Runnable {//自定义类实现Runnable接口;
    //run()方法里是线程体;
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
    public static void main(String[] args) {
        //创建线程对象,把实现了Runnable接口的对象作为参数传入;
        Thread thread1 = new Thread(new TestThread2());
        thread1.start();//启动线程;
        Thread thread2 = new Thread(new TestThread2());
        thread2.start();
    }
}

效果与上图相识

线程状态介绍

 

  1. **新生状态:**new 建立一个线程对象之后,新生状态的线程有自己的内存空间。start() 函数进入就绪状态
  2. **就绪状态:**系统选定一个等待的 Thread 对象后,进入 执行状态
    1. 新建线程:调用start()方法,进入就绪状态;
    2. 阻塞线程:阻塞解除,进入就绪状态;
    3. 运行线程:调用yield()方法,直接进入就绪状态;
    4. 运行线程:JVM将CPU资源从本线程切换到其他线程。
  3. **运行状态:**执行自己的代码,知道调用其他方法或等待某资源而阻塞或完成任务而死亡。
  4. **阻塞状态:**停止一个线程的执行 以等待某个条件发生
    1. sleep 方法
    2. wait() 方法
    3. 某个操作进入阻塞状态,比如执行IO流操作(read()/write()方法本身就是阻塞的方法)。
    4. join() 线程联合
  5. **死亡状态:**线程结束 stop() destroy()

终止线程的典型方式

public class TestThreadCiycle implements Runnable {
    String name;
    boolean live = true;// 标记变量,表示线程是否可中止;
    public TestThreadCiycle(String name) {
        super();
        this.name = name;
    }
    public void run() {
        int i = 0;
        //当live的值是true时,继续线程体;false则结束循环,继而终止线程体;
        while (live) {
            System.out.println(name + (i++));
        }
    }
    public void terminate() {
        live = false;
    }
 
    public static void main(String[] args) {
        TestThreadCiycle ttc = new TestThreadCiycle("线程A:");
        Thread t1 = new Thread(ttc);// 新生状态
        t1.start();// 就绪状态
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程" + i);
        }
        ttc.terminate();
        System.out.println("ttc stop!");
    }
}

 

线程的联合 jion()

线程A在运行期间,可以调用线程B的join()方法,让线程B和线程A联合。这样,线程A就必须等待线程B执行完毕后,才能继续执行。如下面示例中,“爸爸线程”要抽烟,于是联合了“儿子线程”去买烟,必须等待“儿子线程”买烟完毕,“爸爸线程”才能继续抽烟。

public class TestThreadState {
    public static void main(String[] args) {
        System.out.println("爸爸和儿子买烟故事");
        Thread father = new Thread(new FatherThread());
        father.start();
    }
}
 
class FatherThread implements Runnable {
    public void run() {
        System.out.println("爸爸想抽烟,发现烟抽完了");
        System.out.println("爸爸让儿子去买包红塔山");
        Thread son = new Thread(new SonThread());
        son.start();
        System.out.println("爸爸等儿子买烟回来");
        try {
            son.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("爸爸出门去找儿子跑哪去了");
            // 结束JVM。如果是0则表示正常结束;如果是非0则表示非正常结束
            System.exit(1);
        }
        System.out.println("爸爸高兴的接过烟开始抽,并把零钱给了儿子");
    }
}
 
class SonThread implements Runnable {
    public void run() {
        System.out.println("儿子出门去买烟");
        System.out.println("儿子买烟需要10分钟");
        try {
            for (int i = 1; i <= 10; i++) {
                System.out.println("第" + i + "分钟");
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("儿子买烟回来了");
    }
}

 

获取线程基本信息的方法

 

线程的常用方法一

public class TestThread {
    public static void main(String[] argc) throws Exception {
        Runnable r = new MyThread();
        Thread t = new Thread(r, "Name test");//定义线程对象,并传入参数;
        t.start();//启动线程;
        System.out.println("name is: " + t.getName());//输出线程名称;
        Thread.currentThread().sleep(5000);//线程暂停5分钟;
        System.out.println(t.isAlive());//判断线程还在运行吗?
        System.out.println("over!");
    }
}
class MyThread implements Runnable {
    //线程体;
    public void run() {
        for (int i = 0; i < 10; i++)
            System.out.println(i);
    }
}

线程的优先级

  1. 处于就绪状态的线程,会进入“就绪队列”等待JVM来挑选。
  2. 线程的优先级用数字表示,范围从1到10,一个线程的缺省优先级是5。
  3. 使用下列方法获得或设置线程对象的优先级。
    1. int getPriority();
    2. void setPriority(int newPriority);

线程的常用方法二

public class TestThread {
    public static void main(String[] args) {
        Thread t1 = new Thread(new MyThread(), "t1");
        Thread t2 = new Thread(new MyThread(), "t2");
        t1.setPriority(1);
        t2.setPriority(10);
        t1.start();
        t2.start();
    }
}
class MyThread extends Thread {
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
        }
    }
}

线程同步

多个线程同时 访问 (处理) 同一个对象。对对象进行操作

多线程操作同一个对象*(未使用线程同步)

public class TestSync {
    public static void main(String[] args) {
        Account a1 = new Account(100, "高");
        Drawing draw1 = new Drawing(80, a1);// 定义取钱线程对象;
        Drawing draw2 = new Drawing(80, a1);// 定义取钱线程对象;
        draw1.start(); // 你取钱
        draw2.start(); // 你老婆取钱
    }
}
/*
 * 简单表示银行账户
 */class Account {
    int money;
    String aname;
 
    public Account(int money, String aname) {
        super();
        this.money = money;
        this.aname = aname;
    }
}
/**
 * 模拟提款操作
 */class Drawing extends Thread {
    int drawingNum; // 取多少钱
    Account account; // 要取钱的账户
    int expenseTotal; // 总共取的钱数
 
    public Drawing(int drawingNum, Account account) {
        super();
        this.drawingNum = drawingNum;
        this.account = account;
    }
 
    @Override
    public void run() {
        if (account.money - drawingNum < 0) {
            return;
        }
        try {
            Thread.sleep(1000); // 判断完后阻塞。其他线程开始运行。
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        account.money -= drawingNum;
        expenseTotal += drawingNum;
        System.out.println(this.getName() + "--账户余额:" + account.money);
        System.out.println(this.getName() + "--总共取了:" + expenseTotal);
    }
}

执行结果:

关于两种机制

因为 通常用 private 关键字保存数据对象,只能用 方法访问,
我们提出了 stnchronized 关键字,包括两种用法: synchronized方法和 synchronized

  • synchronized 方法
    声明方法
public  synchronized  void accessVal(int newVal);

这个方法 控制对 "对象的类成员变量" 的访问:需要先获得对象的锁才能进行操作,操作完返回时 解锁,让被阻塞的线程获得锁,重新进入可执行状态

synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率。

  • synchronized块
    语法
synchronized(syncObject)
   { 
   //允许访问控制的代码 
   }

多线程操作同一个对象(使用线程同步)

public class TestSync {
    public static void main(String[] args) {
        Account a1 = new Account(100, "高");
        Drawing draw1 = new Drawing(80, a1);
        Drawing draw2 = new Drawing(80, a1);
        draw1.start(); // 你取钱
        draw2.start(); // 你老婆取钱
    }
}
/*
 * 简单表示银行账户
 */class Account {
    int money;
    String aname;
    public Account(int money, String aname) {
        super();
        this.money = money;
        this.aname = aname;
    }
}
/**
 * 模拟提款操作
 * 
 * @author Administrator
 *
 */class Drawing extends Thread {
    int drawingNum; // 取多少钱
    Account account; // 要取钱的账户
    int expenseTotal; // 总共取的钱数
 
    public Drawing(int drawingNum, Account account) {
        super();
        this.drawingNum = drawingNum;
        this.account = account;
    }
 
    @Override
    public void run() {
        draw();
    }
 
    void draw() {
        synchronized (account) {
            if (account.money - drawingNum < 0) {
                System.out.println(this.getName() + "取款,余额不足!");
                return;
            }
            try {
                Thread.sleep(1000); // 判断完后阻塞。其他线程开始运行。
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            account.money -= drawingNum;
            expenseTotal += drawingNum;
        }
        System.out.println(this.getName() + "--账户余额:" + account.money);
        System.out.println(this.getName() + "--总共取了:" + expenseTotal);
    }
}

运行结果

 

死锁及解决办法

死锁是什么:

多线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或多个线程在等待对方释放资源,都停止执行的情况

点赞

发表评论

电子邮件地址不会被公开。必填项已用 * 标注